KiCad PCB EDA Suite
Loading...
Searching...
No Matches
diff_scene.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
28#include <trace_helpers.h>
29
30#include <wx/debug.h>
31#include <wx/log.h>
32
33#include <iterator>
34#include <map>
35
36
37namespace KICAD_DIFF
38{
39
41{
42 switch( aCategory )
43 {
44 case CATEGORY::ADDED: return aTheme.added;
45 case CATEGORY::REMOVED: return aTheme.removed;
46 case CATEGORY::MODIFIED: return aTheme.modified;
47 case CATEGORY::CONFLICT: return aTheme.conflict;
48 }
49
50 wxFAIL_MSG( wxS( "Unknown CATEGORY" ) );
51 return KIGFX::COLOR4D();
52}
53
54
55namespace
56{
57 struct NET_SHAPE_GROUP
58 {
59 KIID_PATH firstChangeId;
60 wxString netName;
61 CHANGE_KIND kind;
62 BOX2I bbox;
63 bool hasBBox = false;
64 };
65
66
67 bool bboxValid( const BOX2I& aBox )
68 {
69 return aBox.GetWidth() > 0 && aBox.GetHeight() > 0;
70 }
71
72
73 std::vector<SCENE_SHAPE>& mutableShapesFor( DIFF_SCENE& aScene, CATEGORY aCategory )
74 {
75 // The const-overload caller never returns a reference to a true const
76 // SCENE_SHAPE vector — only switches on category — so we can re-use
77 // its switch.
78 return const_cast<std::vector<SCENE_SHAPE>&>( ShapesFor( aScene, aCategory ) );
79 }
80
81
82 void mergeSceneBBox( const BOX2I& aBBox, BOX2I& aUnionBBox, bool& aFirst )
83 {
84 if( aFirst )
85 {
86 aUnionBBox = aBBox;
87 aFirst = false;
88 }
89 else
90 {
91 aUnionBBox.Merge( aBBox );
92 }
93 }
94
95
96 SHAPE_POLY_SET polygonSetFromDiffValue( const DIFF_VALUE& aValue )
97 {
98 SHAPE_POLY_SET out;
99
100 if( aValue.GetType() != DIFF_VALUE::T::POLYGON_SET )
101 return out;
102
103 for( const auto& poly : aValue.AsPolygonSet() )
104 {
105 if( poly.empty() )
106 continue;
107
108 int outlineIdx = out.NewOutline();
109
110 for( const VECTOR2I& pt : poly.front() )
111 out.Append( pt.x, pt.y, outlineIdx, /*aHole=*/-1 );
112
113 for( std::size_t h = 1; h < poly.size(); ++h )
114 {
115 int holeIdx = out.NewHole( outlineIdx );
116
117 for( const VECTOR2I& pt : poly[h] )
118 out.Append( pt.x, pt.y, outlineIdx, holeIdx );
119 }
120 }
121
122 return out;
123 }
124
125
126 SCENE_SHAPE::PolygonList polygonsFromPolySet( const SHAPE_POLY_SET& aPoly )
127 {
129
130 for( int o = 0; o < aPoly.OutlineCount(); ++o )
131 {
132 const auto& polyRefs = aPoly.CPolygon( o );
133 std::vector<std::vector<VECTOR2I>> contours;
134
135 for( const auto& contour : polyRefs )
136 {
137 std::vector<VECTOR2I> pts;
138 pts.reserve( contour.PointCount() );
139
140 for( int p = 0; p < contour.PointCount(); ++p )
141 pts.push_back( contour.CPoint( p ) );
142
143 contours.push_back( std::move( pts ) );
144 }
145
146 out.push_back( std::move( contours ) );
147 }
148
149 return out;
150 }
151
152
153 void pushDeltaShape( DIFF_SCENE& aScene, const DIFF_COLOR_THEME& aTheme, CATEGORY aCategory,
154 const ITEM_CHANGE& aChange, const SHAPE_POLY_SET& aRegion, BOX2I& aUnionBBox, bool& aFirst )
155 {
156 if( aRegion.OutlineCount() == 0 )
157 return;
158
159 SCENE_SHAPE shape;
160 shape.bbox = aRegion.BBox();
161 shape.changeId = aChange.id;
162 shape.color = ThemeColorFor( aTheme, aCategory );
163 shape.label = ChangeDisplayLabel( aChange );
164 shape.polygons = polygonsFromPolySet( aRegion );
165
166 mutableShapesFor( aScene, aCategory ).push_back( std::move( shape ) );
167 mergeSceneBBox( aRegion.BBox(), aUnionBBox, aFirst );
168 }
169
170
171 bool emitOutlineDelta( const ITEM_CHANGE& aChange, const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene,
172 BOX2I& aUnionBBox, bool& aFirst )
173 {
174 bool emitted = false;
175
176 for( const PROPERTY_DELTA& d : aChange.properties )
177 {
178 if( d.before.GetType() != DIFF_VALUE::T::POLYGON_SET || d.after.GetType() != DIFF_VALUE::T::POLYGON_SET )
179 {
180 continue;
181 }
182
183 if( d.name != wxS( "Outline" ) && !d.name.StartsWith( wxS( "Filled Area" ) ) )
184 continue;
185
186 SHAPE_POLY_SET before = polygonSetFromDiffValue( d.before );
187 SHAPE_POLY_SET after = polygonSetFromDiffValue( d.after );
188
189 SHAPE_POLY_SET added = after;
190 added.BooleanSubtract( before );
191
192 SHAPE_POLY_SET removed = before;
193 removed.BooleanSubtract( after );
194
195 pushDeltaShape( aScene, aTheme, CATEGORY::ADDED, aChange, added, aUnionBBox, aFirst );
196 pushDeltaShape( aScene, aTheme, CATEGORY::REMOVED, aChange, removed, aUnionBBox, aFirst );
197
198 if( added.OutlineCount() > 0 || removed.OutlineCount() > 0 )
199 emitted = true;
200 }
201
202 return emitted;
203 }
204
205
206 void addChangeShape( const ITEM_CHANGE& aChange, const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene,
207 BOX2I& aUnionBBox, bool& aFirst )
208 {
209 if( emitOutlineDelta( aChange, aTheme, aScene, aUnionBBox, aFirst ) )
210 return;
211
212 if( !bboxValid( aChange.bbox ) )
213 return;
214
215 const CATEGORY cat = CategoryFor( aChange.kind );
216
217 SCENE_SHAPE shape;
218 shape.bbox = aChange.bbox;
219 shape.changeId = aChange.id;
220 shape.color = ThemeColorFor( aTheme, cat );
221
222 shape.label = ChangeDisplayLabel( aChange );
223
224 mutableShapesFor( aScene, cat ).push_back( shape );
225 mergeSceneBBox( aChange.bbox, aUnionBBox, aFirst );
226 }
227
228
229 void collectShapes( const ITEM_CHANGE& aChange, const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene,
230 BOX2I& aUnionBBox, bool& aFirst,
231 std::map<std::pair<CHANGE_KIND, wxString>, NET_SHAPE_GROUP>& aNetGroups )
232 {
233 if( IsRoutingNetChange( aChange ) )
234 {
235 auto& group = aNetGroups[std::make_pair( aChange.kind, *aChange.refdes )];
236
237 if( !group.hasBBox )
238 {
239 group.firstChangeId = aChange.id;
240 group.netName = *aChange.refdes;
241 group.kind = aChange.kind;
242 group.bbox = aChange.bbox;
243 group.hasBBox = bboxValid( aChange.bbox );
244 }
245 else if( bboxValid( aChange.bbox ) )
246 {
247 group.bbox.Merge( aChange.bbox );
248 }
249 }
250 else
251 {
252 addChangeShape( aChange, aTheme, aScene, aUnionBBox, aFirst );
253 }
254
255 for( const ITEM_CHANGE& child : aChange.children )
256 collectShapes( child, aTheme, aScene, aUnionBBox, aFirst, aNetGroups );
257 }
258
259
260 void addNetShapes( const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene, BOX2I& aUnionBBox, bool& aFirst,
261 const std::map<std::pair<CHANGE_KIND, wxString>, NET_SHAPE_GROUP>& aNetGroups )
262 {
263 for( const auto& [key, group] : aNetGroups )
264 {
265 if( !group.hasBBox )
266 continue;
267
268 const CATEGORY cat = CategoryFor( group.kind );
269
270 SCENE_SHAPE shape;
271 shape.bbox = group.bbox;
272 shape.changeId = group.firstChangeId;
273 shape.color = ThemeColorFor( aTheme, cat );
274 shape.label = wxS( "NET [" ) + group.netName + wxS( "]" );
275
276 mutableShapesFor( aScene, cat ).push_back( shape );
277 mergeSceneBBox( group.bbox, aUnionBBox, aFirst );
278 }
279 }
280
281} // namespace
282
283
284std::optional<BOX2I> BBoxFromGeometry( const DOCUMENT_GEOMETRY& aGeometry )
285{
286 std::optional<BOX2I> bbox;
287
288 auto addPoint = [&]( const VECTOR2I& aPt )
289 {
290 if( !bbox.has_value() )
291 bbox = BOX2I( aPt, VECTOR2I( 0, 0 ) );
292 else
293 bbox->Merge( aPt );
294 };
295
296 auto addPointInflated = [&]( const VECTOR2I& aPt, int aHalfStroke )
297 {
298 const VECTOR2I pad( aHalfStroke, aHalfStroke );
299 addPoint( aPt - pad );
300 addPoint( aPt + pad );
301 };
302
303 // Mirror the renderer's stroke-width substitution: a primitive declared
304 // with width <= 0 is drawn at PLOT_HAIRLINE_IU by RenderSceneTo{Png,Svg},
305 // so the bbox must inflate by the same effective half-stroke or a
306 // width-0 horizontal/vertical primitive collapses to a degenerate slice
307 // and the renderer's hairline pixels spill outside the reported bbox.
308
309 for( const DOCUMENT_SEGMENT& s : aGeometry.segments )
310 {
311 const int half = EffectivePlotWidth( s.width ) / 2;
312 addPointInflated( s.start, half );
313 addPointInflated( s.end, half );
314 }
315
316 for( const DOCUMENT_POLYGON& p : aGeometry.polygons )
317 {
318 // Match the most-permissive renderer's polygon skip. The headless
319 // plotter requires outline.size() >= 3 for closed shapes, but the
320 // GAL renderer (diff_renderer_gal.cpp) draws two-point outlines as
321 // open segments, so the bbox must include them too. Only a strictly
322 // single-point outline draws nothing anywhere — skip just that.
323 if( p.outline.size() < 2 )
324 continue;
325
326 const int half = EffectivePlotWidth( p.lineWidth ) / 2;
327
328 for( const VECTOR2I& pt : p.outline )
329 addPointInflated( pt, half );
330 }
331
332 for( const DOCUMENT_CIRCLE& c : aGeometry.circles )
333 {
334 const int reach = c.radius + EffectivePlotWidth( c.lineWidth ) / 2;
335 const VECTOR2I r( reach, reach );
336 addPoint( c.center - r );
337 addPoint( c.center + r );
338 }
339
340 return bbox;
341}
342
343
345{
346 LSET layers;
347
348 for( const DOCUMENT_SEGMENT& s : aGeometry.segments )
349 layers |= s.layers;
350
351 for( const DOCUMENT_POLYGON& p : aGeometry.polygons )
352 layers |= p.layers;
353
354 for( const DOCUMENT_CIRCLE& c : aGeometry.circles )
355 layers |= c.layers;
356
357 return layers;
358}
359
360
362{
364
365 auto visible = [&]( const LSET& aLayers )
366 {
367 return aLayers.none() || ( aLayers & aVisibleLayers ).any();
368 };
369
370 for( const DOCUMENT_SEGMENT& s : aGeometry.segments )
371 {
372 if( visible( s.layers ) )
373 out.segments.push_back( s );
374 }
375
376 for( const DOCUMENT_POLYGON& p : aGeometry.polygons )
377 {
378 if( visible( p.layers ) )
379 out.polygons.push_back( p );
380 }
381
382 for( const DOCUMENT_CIRCLE& c : aGeometry.circles )
383 {
384 if( visible( c.layers ) )
385 out.circles.push_back( c );
386 }
387
388 return out;
389}
390
391
393{
394 aDst.segments.insert( aDst.segments.end(), std::make_move_iterator( aSrc.segments.begin() ),
395 std::make_move_iterator( aSrc.segments.end() ) );
396 aDst.polygons.insert( aDst.polygons.end(), std::make_move_iterator( aSrc.polygons.begin() ),
397 std::make_move_iterator( aSrc.polygons.end() ) );
398 aDst.circles.insert( aDst.circles.end(), std::make_move_iterator( aSrc.circles.begin() ),
399 std::make_move_iterator( aSrc.circles.end() ) );
400}
401
402
403bool IsRoutingNetChange( const ITEM_CHANGE& aChange )
404{
405 return aChange.refdes.has_value() && !aChange.refdes->IsEmpty()
406 && ( aChange.typeName == wxS( "PCB_TRACK" ) || aChange.typeName == wxS( "PCB_ARC" )
407 || aChange.typeName == wxS( "PCB_VIA" ) );
408}
409
410
411wxString ChangeDisplayLabel( const ITEM_CHANGE& aChange )
412{
413 wxString label = aChange.typeName;
414
415 if( aChange.refdes.has_value() )
416 label += wxS( " [" ) + *aChange.refdes + wxS( "]" );
417
418 return label;
419}
420
421
423{
424 DIFF_SCENE scene;
425 BOX2I unionBBox;
426 bool first = true;
427 std::map<std::pair<CHANGE_KIND, wxString>, NET_SHAPE_GROUP> netGroups;
428
429 for( const ITEM_CHANGE& c : aDiff.changes )
430 collectShapes( c, aTheme, scene, unionBBox, first, netGroups );
431
432 addNetShapes( aTheme, scene, unionBBox, first, netGroups );
433
434 if( !first )
435 scene.documentBBox = unionBBox;
436
437 wxLogTrace( traceDiffMerge, wxT( "BuildScene: %zu changes -> added=%zu removed=%zu modified=%zu conflict=%zu" ),
438 aDiff.changes.size(), scene.addedShapes.size(), scene.removedShapes.size(), scene.modifiedShapes.size(),
439 scene.conflictShapes.size() );
440
441 return scene;
442}
443
444
446{
447 std::optional<BOX2I> unionBBox;
448
449 if( aScene.documentBBox.GetWidth() > 0 || aScene.documentBBox.GetHeight() > 0 )
450 unionBBox = aScene.documentBBox;
451
452 auto merge = [&]( const std::optional<BOX2I>& aSrc )
453 {
454 if( !aSrc.has_value() )
455 return;
456
457 if( !unionBBox.has_value() )
458 unionBBox = *aSrc;
459 else
460 unionBBox->Merge( *aSrc );
461 };
462
463 merge( BBoxFromGeometry( aScene.referenceGeometry ) );
464 merge( BBoxFromGeometry( aScene.comparisonGeometry ) );
465
466 if( unionBBox.has_value() )
467 aScene.documentBBox = *unionBBox;
468}
469
470
471void CollectChangeBBoxes( const DOCUMENT_DIFF& aDiff, std::map<KIID_PATH, BOX2I>& aOut )
472{
473 // IndexChangesByKiid already does the recursive walk; reuse it so the
474 // tree-flatten logic lives in one place. emplace preserves any
475 // pre-existing entries the caller seeded.
476 for( const auto& [id, change] : IndexChangesByKiid( aDiff ) )
477 {
478 if( change->bbox.GetWidth() > 0 || change->bbox.GetHeight() > 0 )
479 aOut.emplace( id, change->bbox );
480 }
481}
482
483
484DOCUMENT_POLYGON MakeBBoxOutline( const BOX2I& aBBox, const KIGFX::COLOR4D& aColor, int aLineWidth )
485{
486 DOCUMENT_POLYGON poly;
487 poly.filled = false;
488 poly.lineWidth = aLineWidth;
489 poly.color = aColor;
490
491 if( !aBBox.GetWidth() || !aBBox.GetHeight() )
492 return poly;
493
494 poly.outline = { aBBox.GetOrigin(),
495 { aBBox.GetEnd().x, aBBox.GetOrigin().y },
496 aBBox.GetEnd(),
497 { aBBox.GetOrigin().x, aBBox.GetEnd().y } };
498 return poly;
499}
500
501
503{
504 SHAPE_POLY_SET out;
505
506 for( const auto& poly : aPolygons )
507 {
508 if( poly.empty() || poly.front().size() < 3 )
509 continue;
510
511 int outlineIdx = out.NewOutline();
512
513 for( const VECTOR2I& pt : poly.front() )
514 out.Append( pt.x, pt.y, outlineIdx, /*aHole=*/-1 );
515
516 for( std::size_t h = 1; h < poly.size(); ++h )
517 {
518 int holeIdx = out.NewHole( outlineIdx );
519
520 for( const VECTOR2I& pt : poly[h] )
521 out.Append( pt.x, pt.y, outlineIdx, holeIdx );
522 }
523 }
524
525 return out;
526}
527
528
530{
531 switch( aKind )
532 {
533 case CHANGE_KIND::ADDED: return CATEGORY::ADDED;
534 case CHANGE_KIND::REMOVED: return CATEGORY::REMOVED;
535 case CHANGE_KIND::MODIFIED: return CATEGORY::MODIFIED;
537 case CHANGE_KIND::DUPLICATE_UUID: return CATEGORY::CONFLICT;
538 }
539
540 wxFAIL_MSG( wxS( "Unknown CHANGE_KIND" ) );
541 return CATEGORY::MODIFIED;
542}
543
544
545const std::vector<SCENE_SHAPE>& ShapesFor( const DIFF_SCENE& aScene, CATEGORY aCategory )
546{
547 switch( aCategory )
548 {
549 case CATEGORY::ADDED: return aScene.addedShapes;
550 case CATEGORY::REMOVED: return aScene.removedShapes;
551 case CATEGORY::MODIFIED: return aScene.modifiedShapes;
552 case CATEGORY::CONFLICT: return aScene.conflictShapes;
553 }
554
555 wxFAIL_MSG( wxS( "Unknown CATEGORY" ) );
556 return aScene.modifiedShapes;
557}
558
559
560std::optional<BOX2I> HighlightedBBox( const DIFF_SCENE& aScene, const KIID_PATH& aChangeId,
561 const std::array<bool, CATEGORY_COUNT>& aCategoryVisible )
562{
563 std::optional<BOX2I> result;
564
565 for( CATEGORY cat : PAINT_ORDER )
566 {
567 if( !aCategoryVisible[static_cast<std::size_t>( cat )] )
568 continue;
569
570 for( const SCENE_SHAPE& s : ShapesFor( aScene, cat ) )
571 {
572 if( s.changeId != aChangeId )
573 continue;
574
575 if( !result )
576 result = s.bbox;
577 else
578 result->Merge( s.bbox );
579 }
580 }
581
582 return result;
583}
584
585} // namespace KICAD_DIFF
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr const Vec GetEnd() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr const Vec & GetOrigin() const
Definition box2.h:206
A typed sum value used to carry the before/after of any single property.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
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 NewHole(int aOutline=-1)
Creates a new hole in a given outline.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const POLYGON & CPolygon(int aIndex) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
A type-safe container of any type.
Definition ki_any.h:92
const wxChar *const traceDiffMerge
Flag to enable diff/merge engine and renderer debugging output.
CHANGE_KIND
Coarse classification of a single item-level change between two documents.
wxString ChangeDisplayLabel(const ITEM_CHANGE &aChange)
User-facing item label used consistently by scene tooltips and change tree entries.
DIFF_SCENE BuildScene(const DOCUMENT_DIFF &aDiff, const DIFF_COLOR_THEME &aTheme)
Build a DIFF_SCENE from a DOCUMENT_DIFF, populating the shape lists and computing the union bbox.
DOCUMENT_POLYGON MakeBBoxOutline(const BOX2I &aBBox, const KIGFX::COLOR4D &aColor, int aLineWidth)
Build a DOCUMENT_POLYGON outlining a bounding box.
LSET GeometryLayerSet(const DOCUMENT_GEOMETRY &aGeometry)
Return the union of every non-empty layer set carried by the geometry.
constexpr int EffectivePlotWidth(int aWidth)
Return aWidth if positive, otherwise PLOT_HAIRLINE_IU.
void CollectChangeBBoxes(const DOCUMENT_DIFF &aDiff, std::map< KIID_PATH, BOX2I > &aOut)
Walk a DOCUMENT_DIFF and populate a (KIID_PATH → BOX2I) map with each changed item's bbox,...
KIGFX::COLOR4D ThemeColorFor(const DIFF_COLOR_THEME &aTheme, CATEGORY aCategory)
Map a CATEGORY to its color in a DIFF_COLOR_THEME.
std::optional< BOX2I > HighlightedBBox(const DIFF_SCENE &aScene, const KIID_PATH &aChangeId, const std::array< bool, CATEGORY_COUNT > &aCategoryVisible)
Union bbox of every visible SCENE_SHAPE whose changeId matches aChangeId.
void AppendGeometry(DOCUMENT_GEOMETRY &aDst, DOCUMENT_GEOMETRY &&aSrc)
Move all primitives from aSrc into aDst.
void ExpandBBoxToGeometry(DIFF_SCENE &aScene)
Grow the scene's documentBBox to also include the extent of any background geometry.
bool IsRoutingNetChange(const ITEM_CHANGE &aChange)
Presentation predicate for PCB routing changes that should be displayed as one net-level entry/shape.
CATEGORY
Visual category each ITEM_CHANGE belongs to in the scene.
Definition diff_scene.h:52
std::map< KIID_PATH, const ITEM_CHANGE * > IndexChangesByKiid(const DOCUMENT_DIFF &aDiff)
Flatten a DOCUMENT_DIFF's ITEM_CHANGE tree into a KIID_PATH -> ITEM_CHANGE* map, recursing into child...
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.
std::optional< BOX2I > BBoxFromGeometry(const DOCUMENT_GEOMETRY &aGeometry)
Compute the tight bounding box of a DOCUMENT_GEOMETRY, inflating each primitive by half its stroke so...
CATEGORY CategoryFor(CHANGE_KIND aKind)
Map a CHANGE_KIND to the visual category it belongs to.
DOCUMENT_GEOMETRY FilterGeometryByVisibleLayers(const DOCUMENT_GEOMETRY &aGeometry, const LSET &aVisibleLayers)
Copy geometry primitives whose layer set intersects aVisibleLayers.
std::vector< SCENE_SHAPE > modifiedShapes
Definition diff_scene.h:232
DOCUMENT_GEOMETRY referenceGeometry
Background geometry from the two source documents.
Definition diff_scene.h:239
std::vector< SCENE_SHAPE > conflictShapes
Definition diff_scene.h:233
std::vector< SCENE_SHAPE > addedShapes
Definition diff_scene.h:230
DOCUMENT_GEOMETRY comparisonGeometry
Definition diff_scene.h:240
std::vector< SCENE_SHAPE > removedShapes
Definition diff_scene.h:231
Filled or stroked circle.
Definition diff_scene.h:146
The full set of changes between two parsed documents of one type.
std::vector< ITEM_CHANGE > changes
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
std::vector< DOCUMENT_SEGMENT > segments
Definition diff_scene.h:164
std::vector< DOCUMENT_POLYGON > polygons
Definition diff_scene.h:165
std::vector< DOCUMENT_CIRCLE > circles
Definition diff_scene.h:166
Closed polygon outline from a source document.
Definition diff_scene.h:133
std::vector< VECTOR2I > outline
Definition diff_scene.h:134
Stroked line segment from one of the source documents.
Definition diff_scene.h:117
One change record on a single item.
std::optional< wxString > refdes
std::vector< ITEM_CHANGE > children
Single (name, before, after) triple for one mutated property on an item.
Shared rendering model consumed by both the GAL renderer (interactive widget) and the plotter rendere...
Definition diff_scene.h:90
std::vector< std::vector< std::vector< VECTOR2I > > > PolygonList
Definition diff_scene.h:101
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683