KiCad PCB EDA Suite
Loading...
Searching...
No Matches
diff_renderer_gal.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
27#include <view/view_overlay.h>
28
29
30namespace KICAD_DIFF
31{
32
33namespace
34{
35
36constexpr double FILL_ALPHA = 0.35;
37
38// Zone deltas can blanket a large area, so they fill fainter than the bbox
39// rectangles to keep the board readable underneath.
40constexpr double POLYGON_FILL_ALPHA = 0.20;
41
42const KIGFX::COLOR4D HIDDEN_FILL( 0.5, 0.5, 0.5, 0.10 );
43
44
52class OVERLAY_GEOMETRY_SINK : public GEOMETRY_SINK
53{
54public:
55 explicit OVERLAY_GEOMETRY_SINK( KIGFX::VIEW_OVERLAY& aOverlay ) : m_overlay( aOverlay ) {}
56
57 void FillPolygon( const DOCUMENT_POLYGON& aPoly ) override
58 {
59 if( !aPoly.filled || aPoly.outline.size() < 3 )
60 return;
61
62 m_overlay.SetIsFill( true );
63 m_overlay.SetIsStroke( false );
64 m_overlay.SetFillColor( aPoly.color );
65
66 std::deque<VECTOR2D> pts;
67
68 for( const VECTOR2I& pt : aPoly.outline )
69 pts.emplace_back( pt.x, pt.y );
70
71 m_overlay.Polygon( pts );
72 }
73
74 void StrokePolygon( const DOCUMENT_POLYGON& aPoly ) override
75 {
76 // Skip stroke only when the shape is filled AND has no line width;
77 // a filled poly with a positive width still wants its outline.
78 if( aPoly.outline.size() < 2 || ( aPoly.filled && aPoly.lineWidth == 0 ) )
79 return;
80
81 m_overlay.SetIsFill( false );
82 m_overlay.SetIsStroke( true );
83 m_overlay.SetStrokeColor( aPoly.color );
84 m_overlay.SetLineWidth( static_cast<double>( aPoly.lineWidth ) );
85
86 for( std::size_t i = 0; i + 1 < aPoly.outline.size(); ++i )
87 {
88 m_overlay.Line( VECTOR2D( aPoly.outline[i].x, aPoly.outline[i].y ),
89 VECTOR2D( aPoly.outline[i + 1].x, aPoly.outline[i + 1].y ) );
90 }
91
92 // Close the loop.
93 m_overlay.Line( VECTOR2D( aPoly.outline.back().x, aPoly.outline.back().y ),
94 VECTOR2D( aPoly.outline.front().x, aPoly.outline.front().y ) );
95 }
96
97 void DrawSegment( const DOCUMENT_SEGMENT& aSegment ) override
98 {
99 m_overlay.SetIsFill( false );
100 m_overlay.SetIsStroke( true );
101 m_overlay.SetStrokeColor( aSegment.color );
102
103 if( aSegment.width > 0 )
104 {
105 m_overlay.Segment( VECTOR2D( aSegment.start.x, aSegment.start.y ),
106 VECTOR2D( aSegment.end.x, aSegment.end.y ),
107 static_cast<double>( aSegment.width ) );
108 }
109 else
110 {
111 m_overlay.SetLineWidth( 0.0 );
112 m_overlay.Line( VECTOR2D( aSegment.start.x, aSegment.start.y ),
113 VECTOR2D( aSegment.end.x, aSegment.end.y ) );
114 }
115 }
116
117 void DrawCircle( const DOCUMENT_CIRCLE& aCircle ) override
118 {
119 if( aCircle.filled )
120 {
121 m_overlay.SetIsFill( true );
122 m_overlay.SetIsStroke( false );
123 m_overlay.SetFillColor( aCircle.color );
124 }
125 else
126 {
127 m_overlay.SetIsFill( false );
128 m_overlay.SetIsStroke( true );
129 m_overlay.SetStrokeColor( aCircle.color );
130 m_overlay.SetLineWidth( static_cast<double>( aCircle.lineWidth ) );
131 }
132
133 m_overlay.Circle( VECTOR2D( aCircle.center.x, aCircle.center.y ),
134 static_cast<double>( aCircle.radius ) );
135 }
136
137private:
138 KIGFX::VIEW_OVERLAY& m_overlay;
139};
140
141
142void renderDocumentGeometry( KIGFX::VIEW_OVERLAY& aOverlay, const DOCUMENT_GEOMETRY& aGeometry )
143{
144 OVERLAY_GEOMETRY_SINK sink( aOverlay );
145 IterateDocumentGeometry( aGeometry, sink );
146}
147
148} // namespace
149
150
152 const std::array<bool, CATEGORY_COUNT>& aVisible, const std::optional<KIID_PATH>& aHighlight,
153 const std::set<KIID_PATH>& aHidden )
154{
155 aOverlay.Clear();
156
157 // Background geometry — drawn first so the diff bbox rectangles sit on
158 // top. Reference side first so comparison-only items don't get washed out
159 // when both sides cover the same area.
160 renderDocumentGeometry( aOverlay, aScene.referenceGeometry );
161 renderDocumentGeometry( aOverlay, aScene.comparisonGeometry );
162
163 const bool nativeContext = aScene.referenceGeometry.Empty() && aScene.comparisonGeometry.Empty();
164
165 // Bounding boxes of the changed items (footprints, tracks, vias) in the
166 // visible categories. Zone heat fills are punched with these so an item
167 // added inside a changed zone shows through instead of being washed by the
168 // same-color fill.
169 SHAPE_POLY_SET itemBoxes;
170
171 for( CATEGORY cat : PAINT_ORDER )
172 {
173 if( !aVisible[static_cast<std::size_t>( cat )] )
174 continue;
175
176 for( const SCENE_SHAPE& s : ShapesFor( aScene, cat ) )
177 {
178 if( !s.polygons.empty() || s.bbox.GetWidth() <= 0 || s.bbox.GetHeight() <= 0 )
179 continue;
180
181 int o = itemBoxes.NewOutline();
182 itemBoxes.Append( s.bbox.GetOrigin().x, s.bbox.GetOrigin().y, o, -1 );
183 itemBoxes.Append( s.bbox.GetEnd().x, s.bbox.GetOrigin().y, o, -1 );
184 itemBoxes.Append( s.bbox.GetEnd().x, s.bbox.GetEnd().y, o, -1 );
185 itemBoxes.Append( s.bbox.GetOrigin().x, s.bbox.GetEnd().y, o, -1 );
186 }
187 }
188
189 for( CATEGORY cat : PAINT_ORDER )
190 {
191 if( !aVisible[static_cast<std::size_t>( cat )] )
192 continue;
193
194 for( const SCENE_SHAPE& s : ShapesFor( aScene, cat ) )
195 {
196 KIGFX::COLOR4D fill = s.color;
197 fill.a = FILL_ALPHA;
198
199 const bool hidden = !aHidden.empty() && aHidden.count( s.changeId ) > 0;
200
201 if( hidden )
202 fill = HIDDEN_FILL;
203
204 if( !s.polygons.empty() )
205 {
206 if( !hidden )
207 fill.a = POLYGON_FILL_ALPHA;
208
209 SHAPE_POLY_SET set = PolySetFromPolygonList( s.polygons );
210
211 if( set.OutlineCount() > 0 )
212 {
213 SHAPE_POLY_SET heat = set;
214
215 // Only punch item holes when the zone is muted. Shown zones
216 // keep a solid heat fill.
217 if( hidden && itemBoxes.OutlineCount() > 0 )
218 heat.BooleanSubtract( itemBoxes );
219
220 aOverlay.SetIsFill( true );
221 aOverlay.SetIsStroke( false );
222 aOverlay.SetFillColor( fill );
223 aOverlay.Polygon( heat );
224
225 // Stroke the full region boundary so it stays crisp where
226 // the punched holes break the fill.
227 KIGFX::COLOR4D border = s.color;
228 border.a = hidden ? HIDDEN_FILL.a : 1.0;
229
230 aOverlay.SetIsFill( false );
231 aOverlay.SetIsStroke( true );
232 aOverlay.SetStrokeColor( border );
233 aOverlay.SetLineWidth( 0.0 );
234 aOverlay.Polygon( set );
235 }
236
237 continue;
238 }
239
240 if( nativeContext )
241 continue;
242
243 aOverlay.SetIsFill( true );
244 aOverlay.SetIsStroke( false );
245 aOverlay.SetFillColor( fill );
246 aOverlay.Rectangle( s.bbox.GetOrigin(), s.bbox.GetEnd() );
247 }
248 }
249}
250
251} // namespace KICAD_DIFF
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double a
Alpha component.
Definition color4d.h:392
void SetLineWidth(double aLineWidth)
void Polygon(const std::deque< VECTOR2D > &aPointList)
void Rectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
void SetIsFill(bool aIsFillEnabled)
void SetFillColor(const COLOR4D &aColor)
void SetIsStroke(bool aIsStrokeEnabled)
void SetStrokeColor(const COLOR4D &aColor)
Represent a set of closed polygons.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
void IterateDocumentGeometry(const DOCUMENT_GEOMETRY &aGeometry, GEOMETRY_SINK &aSink)
Walk a DOCUMENT_GEOMETRY in the canonical render order shared by the GAL and plotter renderers: every...
Definition diff_scene.h:200
void RenderSceneToOverlay(KIGFX::VIEW_OVERLAY &aOverlay, const DIFF_SCENE &aScene, const std::array< bool, CATEGORY_COUNT > &aVisible, const std::optional< KIID_PATH > &aHighlight, const std::set< KIID_PATH > &aHidden)
Push a DIFF_SCENE's shapes onto a VIEW_OVERLAY as filled, semi-transparent rectangles,...
CATEGORY
Visual category each ITEM_CHANGE belongs to in the scene.
Definition diff_scene.h:52
const std::vector< SCENE_SHAPE > & ShapesFor(const DIFF_SCENE &aScene, CATEGORY aCategory)
Read-only access to a DIFF_SCENE's shape list for a given category.
constexpr std::array< CATEGORY, 4 > PAINT_ORDER
Paint order.
Definition diff_scene.h:67
SHAPE_POLY_SET PolySetFromPolygonList(const SCENE_SHAPE::PolygonList &aPolygons)
Build a SHAPE_POLY_SET from a SCENE_SHAPE::PolygonList.
void DrawSegment(const ROUND_SEGMENT_2D &aSegment, unsigned int aNrSidesPerCircle)
Draw a thick line segment with rounded ends.
DOCUMENT_GEOMETRY referenceGeometry
Background geometry from the two source documents.
Definition diff_scene.h:239
DOCUMENT_GEOMETRY comparisonGeometry
Definition diff_scene.h:240
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
Sink for the shared DOCUMENT_GEOMETRY walk.
Definition diff_scene.h:184
Shared rendering model consumed by both the GAL renderer (interactive widget) and the plotter rendere...
Definition diff_scene.h:90
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682