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 DIFF_COLOR_THEME theme;
43
44 if( aDarkTheme )
45 {
46 for( KIGFX::COLOR4D* color : { &theme.added, &theme.removed, &theme.modified, &theme.conflict } )
47 *color = color->Brightened( 0.4 );
48 }
49
50 return theme;
51}
52
53
55{
56 switch( aCategory )
57 {
58 case CATEGORY::ADDED: return aTheme.added;
59 case CATEGORY::REMOVED: return aTheme.removed;
60 case CATEGORY::MODIFIED: return aTheme.modified;
61 case CATEGORY::CONFLICT: return aTheme.conflict;
62 }
63
64 wxFAIL_MSG( wxS( "Unknown CATEGORY" ) );
65 return KIGFX::COLOR4D();
66}
67
68
69namespace
70{
71 struct NET_SHAPE_GROUP
72 {
73 KIID_PATH firstChangeId;
74 wxString netName;
75 CHANGE_KIND kind;
76 BOX2I bbox;
77 bool hasBBox = false;
78 };
79
80
81 bool bboxValid( const BOX2I& aBox )
82 {
83 return aBox.GetWidth() > 0 && aBox.GetHeight() > 0;
84 }
85
86
87 std::vector<SCENE_SHAPE>& mutableShapesFor( DIFF_SCENE& aScene, CATEGORY aCategory )
88 {
89 // The const-overload caller never returns a reference to a true const
90 // SCENE_SHAPE vector — only switches on category — so we can re-use
91 // its switch.
92 return const_cast<std::vector<SCENE_SHAPE>&>( ShapesFor( aScene, aCategory ) );
93 }
94
95
96 void mergeSceneBBox( const BOX2I& aBBox, BOX2I& aUnionBBox, bool& aFirst )
97 {
98 if( aFirst )
99 {
100 aUnionBBox = aBBox;
101 aFirst = false;
102 }
103 else
104 {
105 aUnionBBox.Merge( aBBox );
106 }
107 }
108
109
110 SHAPE_POLY_SET polygonSetFromDiffValue( const DIFF_VALUE& aValue )
111 {
112 SHAPE_POLY_SET out;
113
114 if( aValue.GetType() != DIFF_VALUE::T::POLYGON_SET )
115 return out;
116
117 for( const auto& poly : aValue.AsPolygonSet() )
118 {
119 if( poly.empty() )
120 continue;
121
122 int outlineIdx = out.NewOutline();
123
124 for( const VECTOR2I& pt : poly.front() )
125 out.Append( pt.x, pt.y, outlineIdx, /*aHole=*/-1 );
126
127 for( std::size_t h = 1; h < poly.size(); ++h )
128 {
129 int holeIdx = out.NewHole( outlineIdx );
130
131 for( const VECTOR2I& pt : poly[h] )
132 out.Append( pt.x, pt.y, outlineIdx, holeIdx );
133 }
134 }
135
136 return out;
137 }
138
139
140 SCENE_SHAPE::PolygonList polygonsFromPolySet( const SHAPE_POLY_SET& aPoly )
141 {
143
144 for( int o = 0; o < aPoly.OutlineCount(); ++o )
145 {
146 const auto& polyRefs = aPoly.CPolygon( o );
147 std::vector<std::vector<VECTOR2I>> contours;
148
149 for( const auto& contour : polyRefs )
150 {
151 std::vector<VECTOR2I> pts;
152 pts.reserve( contour.PointCount() );
153
154 for( int p = 0; p < contour.PointCount(); ++p )
155 pts.push_back( contour.CPoint( p ) );
156
157 contours.push_back( std::move( pts ) );
158 }
159
160 out.push_back( std::move( contours ) );
161 }
162
163 return out;
164 }
165
166
167 void pushDeltaShape( DIFF_SCENE& aScene, const DIFF_COLOR_THEME& aTheme, CATEGORY aCategory,
168 const ITEM_CHANGE& aChange, const SHAPE_POLY_SET& aRegion, BOX2I& aUnionBBox, bool& aFirst )
169 {
170 if( aRegion.OutlineCount() == 0 )
171 return;
172
173 SCENE_SHAPE shape;
174 shape.bbox = aRegion.BBox();
175 shape.changeId = aChange.id;
176 shape.color = ThemeColorFor( aTheme, aCategory );
177 shape.label = ChangeDisplayLabel( aChange );
178 shape.polygons = polygonsFromPolySet( aRegion );
179
180 mutableShapesFor( aScene, aCategory ).push_back( std::move( shape ) );
181 mergeSceneBBox( aRegion.BBox(), aUnionBBox, aFirst );
182 }
183
184
185 bool emitOutlineDelta( const ITEM_CHANGE& aChange, const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene,
186 BOX2I& aUnionBBox, bool& aFirst )
187 {
188 bool emitted = false;
189
190 for( const PROPERTY_DELTA& d : aChange.properties )
191 {
192 if( d.before.GetType() != DIFF_VALUE::T::POLYGON_SET || d.after.GetType() != DIFF_VALUE::T::POLYGON_SET )
193 {
194 continue;
195 }
196
197 if( d.name != wxS( "Outline" ) && !d.name.StartsWith( wxS( "Filled Area" ) ) )
198 continue;
199
200 SHAPE_POLY_SET before = polygonSetFromDiffValue( d.before );
201 SHAPE_POLY_SET after = polygonSetFromDiffValue( d.after );
202
203 SHAPE_POLY_SET added = after;
204 added.BooleanSubtract( before );
205
206 SHAPE_POLY_SET removed = before;
207 removed.BooleanSubtract( after );
208
209 pushDeltaShape( aScene, aTheme, CATEGORY::ADDED, aChange, added, aUnionBBox, aFirst );
210 pushDeltaShape( aScene, aTheme, CATEGORY::REMOVED, aChange, removed, aUnionBBox, aFirst );
211
212 if( added.OutlineCount() > 0 || removed.OutlineCount() > 0 )
213 emitted = true;
214 }
215
216 return emitted;
217 }
218
219
220 void addChangeShape( const ITEM_CHANGE& aChange, const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene,
221 BOX2I& aUnionBBox, bool& aFirst )
222 {
223 if( emitOutlineDelta( aChange, aTheme, aScene, aUnionBBox, aFirst ) )
224 return;
225
226 if( !bboxValid( aChange.bbox ) )
227 return;
228
229 const CATEGORY cat = CategoryFor( aChange.kind );
230
231 SCENE_SHAPE shape;
232 shape.bbox = aChange.bbox;
233 shape.changeId = aChange.id;
234 shape.color = ThemeColorFor( aTheme, cat );
235
236 shape.label = ChangeDisplayLabel( aChange );
237
238 mutableShapesFor( aScene, cat ).push_back( shape );
239 mergeSceneBBox( aChange.bbox, aUnionBBox, aFirst );
240 }
241
242
243 void collectShapes( const ITEM_CHANGE& aChange, const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene,
244 BOX2I& aUnionBBox, bool& aFirst,
245 std::map<std::pair<CHANGE_KIND, wxString>, NET_SHAPE_GROUP>& aNetGroups )
246 {
247 if( IsRoutingNetChange( aChange ) )
248 {
249 auto& group = aNetGroups[std::make_pair( aChange.kind, *aChange.refdes )];
250
251 if( !group.hasBBox )
252 {
253 group.firstChangeId = aChange.id;
254 group.netName = *aChange.refdes;
255 group.kind = aChange.kind;
256 group.bbox = aChange.bbox;
257 group.hasBBox = bboxValid( aChange.bbox );
258 }
259 else if( bboxValid( aChange.bbox ) )
260 {
261 group.bbox.Merge( aChange.bbox );
262 }
263 }
264 else
265 {
266 addChangeShape( aChange, aTheme, aScene, aUnionBBox, aFirst );
267 }
268
269 for( const ITEM_CHANGE& child : aChange.children )
270 collectShapes( child, aTheme, aScene, aUnionBBox, aFirst, aNetGroups );
271 }
272
273
274 void addNetShapes( const DIFF_COLOR_THEME& aTheme, DIFF_SCENE& aScene, BOX2I& aUnionBBox, bool& aFirst,
275 const std::map<std::pair<CHANGE_KIND, wxString>, NET_SHAPE_GROUP>& aNetGroups )
276 {
277 for( const auto& [key, group] : aNetGroups )
278 {
279 if( !group.hasBBox )
280 continue;
281
282 const CATEGORY cat = CategoryFor( group.kind );
283
284 SCENE_SHAPE shape;
285 shape.bbox = group.bbox;
286 shape.changeId = group.firstChangeId;
287 shape.color = ThemeColorFor( aTheme, cat );
288 shape.label = wxS( "NET [" ) + group.netName + wxS( "]" );
289
290 mutableShapesFor( aScene, cat ).push_back( shape );
291 mergeSceneBBox( group.bbox, aUnionBBox, aFirst );
292 }
293 }
294
295} // namespace
296
297
298std::optional<BOX2I> BBoxFromGeometry( const DOCUMENT_GEOMETRY& aGeometry )
299{
300 std::optional<BOX2I> bbox;
301
302 auto addPoint = [&]( const VECTOR2I& aPt )
303 {
304 if( !bbox.has_value() )
305 bbox = BOX2I( aPt, VECTOR2I( 0, 0 ) );
306 else
307 bbox->Merge( aPt );
308 };
309
310 auto addPointInflated = [&]( const VECTOR2I& aPt, int aHalfStroke )
311 {
312 const VECTOR2I pad( aHalfStroke, aHalfStroke );
313 addPoint( aPt - pad );
314 addPoint( aPt + pad );
315 };
316
317 // Mirror the renderer's stroke-width substitution: a primitive declared
318 // with width <= 0 is drawn at PLOT_HAIRLINE_IU by RenderSceneTo{Png,Svg},
319 // so the bbox must inflate by the same effective half-stroke or a
320 // width-0 horizontal/vertical primitive collapses to a degenerate slice
321 // and the renderer's hairline pixels spill outside the reported bbox.
322
323 for( const DOCUMENT_SEGMENT& s : aGeometry.segments )
324 {
325 const int half = EffectivePlotWidth( s.width ) / 2;
326 addPointInflated( s.start, half );
327 addPointInflated( s.end, half );
328 }
329
330 for( const DOCUMENT_POLYGON& p : aGeometry.polygons )
331 {
332 // Match the most-permissive renderer's polygon skip. The headless
333 // plotter requires outline.size() >= 3 for closed shapes, but the
334 // GAL renderer (diff_renderer_gal.cpp) draws two-point outlines as
335 // open segments, so the bbox must include them too. Only a strictly
336 // single-point outline draws nothing anywhere — skip just that.
337 if( p.outline.size() < 2 )
338 continue;
339
340 const int half = EffectivePlotWidth( p.lineWidth ) / 2;
341
342 for( const VECTOR2I& pt : p.outline )
343 addPointInflated( pt, half );
344 }
345
346 for( const DOCUMENT_CIRCLE& c : aGeometry.circles )
347 {
348 const int reach = c.radius + EffectivePlotWidth( c.lineWidth ) / 2;
349 const VECTOR2I r( reach, reach );
350 addPoint( c.center - r );
351 addPoint( c.center + r );
352 }
353
354 return bbox;
355}
356
357
359{
360 LSET layers;
361
362 for( const DOCUMENT_SEGMENT& s : aGeometry.segments )
363 layers |= s.layers;
364
365 for( const DOCUMENT_POLYGON& p : aGeometry.polygons )
366 layers |= p.layers;
367
368 for( const DOCUMENT_CIRCLE& c : aGeometry.circles )
369 layers |= c.layers;
370
371 return layers;
372}
373
374
376{
378
379 auto visible = [&]( const LSET& aLayers )
380 {
381 return aLayers.none() || ( aLayers & aVisibleLayers ).any();
382 };
383
384 for( const DOCUMENT_SEGMENT& s : aGeometry.segments )
385 {
386 if( visible( s.layers ) )
387 out.segments.push_back( s );
388 }
389
390 for( const DOCUMENT_POLYGON& p : aGeometry.polygons )
391 {
392 if( visible( p.layers ) )
393 out.polygons.push_back( p );
394 }
395
396 for( const DOCUMENT_CIRCLE& c : aGeometry.circles )
397 {
398 if( visible( c.layers ) )
399 out.circles.push_back( c );
400 }
401
402 return out;
403}
404
405
407{
408 aDst.segments.insert( aDst.segments.end(), std::make_move_iterator( aSrc.segments.begin() ),
409 std::make_move_iterator( aSrc.segments.end() ) );
410 aDst.polygons.insert( aDst.polygons.end(), std::make_move_iterator( aSrc.polygons.begin() ),
411 std::make_move_iterator( aSrc.polygons.end() ) );
412 aDst.circles.insert( aDst.circles.end(), std::make_move_iterator( aSrc.circles.begin() ),
413 std::make_move_iterator( aSrc.circles.end() ) );
414}
415
416
417bool IsRoutingNetChange( const ITEM_CHANGE& aChange )
418{
419 return aChange.refdes.has_value() && !aChange.refdes->IsEmpty()
420 && ( aChange.typeName == wxS( "PCB_TRACK" ) || aChange.typeName == wxS( "PCB_ARC" )
421 || aChange.typeName == wxS( "PCB_VIA" ) );
422}
423
424
425wxString ChangeDisplayLabel( const ITEM_CHANGE& aChange )
426{
427 wxString label = aChange.typeName;
428
429 if( aChange.refdes.has_value() )
430 label += wxS( " [" ) + *aChange.refdes + wxS( "]" );
431
432 return label;
433}
434
435
437{
438 DIFF_SCENE scene;
439 BOX2I unionBBox;
440 bool first = true;
441 std::map<std::pair<CHANGE_KIND, wxString>, NET_SHAPE_GROUP> netGroups;
442
443 for( const ITEM_CHANGE& c : aDiff.changes )
444 collectShapes( c, aTheme, scene, unionBBox, first, netGroups );
445
446 addNetShapes( aTheme, scene, unionBBox, first, netGroups );
447
448 if( !first )
449 scene.documentBBox = unionBBox;
450
451 wxLogTrace( traceDiffMerge, wxT( "BuildScene: %zu changes -> added=%zu removed=%zu modified=%zu conflict=%zu" ),
452 aDiff.changes.size(), scene.addedShapes.size(), scene.removedShapes.size(), scene.modifiedShapes.size(),
453 scene.conflictShapes.size() );
454
455 return scene;
456}
457
458
460{
462
463 sides.reference = aScene;
464 sides.reference.addedShapes.clear();
465 sides.reference.removedShapes.clear();
466 sides.reference.modifiedShapes.clear();
467 sides.reference.conflictShapes.clear();
468 sides.reference.comparisonGeometry = {};
469
470 sides.comparison = aScene;
471 sides.comparison.referenceGeometry = {};
472
473 return sides;
474}
475
476
478{
479 std::optional<BOX2I> unionBBox;
480
481 if( aScene.documentBBox.GetWidth() > 0 || aScene.documentBBox.GetHeight() > 0 )
482 unionBBox = aScene.documentBBox;
483
484 auto merge = [&]( const std::optional<BOX2I>& aSrc )
485 {
486 if( !aSrc.has_value() )
487 return;
488
489 if( !unionBBox.has_value() )
490 unionBBox = *aSrc;
491 else
492 unionBBox->Merge( *aSrc );
493 };
494
495 merge( BBoxFromGeometry( aScene.referenceGeometry ) );
496 merge( BBoxFromGeometry( aScene.comparisonGeometry ) );
497
498 if( unionBBox.has_value() )
499 aScene.documentBBox = *unionBBox;
500}
501
502
503void CollectChangeBBoxes( const DOCUMENT_DIFF& aDiff, std::map<KIID_PATH, BOX2I>& aOut )
504{
505 // IndexChangesByKiid already does the recursive walk; reuse it so the
506 // tree-flatten logic lives in one place. emplace preserves any
507 // pre-existing entries the caller seeded.
508 for( const auto& [id, change] : IndexChangesByKiid( aDiff ) )
509 {
510 if( change->bbox.GetWidth() > 0 || change->bbox.GetHeight() > 0 )
511 aOut.emplace( id, change->bbox );
512 }
513}
514
515
516DOCUMENT_POLYGON MakeBBoxOutline( const BOX2I& aBBox, const KIGFX::COLOR4D& aColor, int aLineWidth )
517{
518 DOCUMENT_POLYGON poly;
519 poly.filled = false;
520 poly.lineWidth = aLineWidth;
521 poly.color = aColor;
522
523 if( !aBBox.GetWidth() || !aBBox.GetHeight() )
524 return poly;
525
526 poly.outline = { aBBox.GetOrigin(),
527 { aBBox.GetEnd().x, aBBox.GetOrigin().y },
528 aBBox.GetEnd(),
529 { aBBox.GetOrigin().x, aBBox.GetEnd().y } };
530 return poly;
531}
532
533
535{
536 SHAPE_POLY_SET out;
537
538 for( const auto& poly : aPolygons )
539 {
540 if( poly.empty() || poly.front().size() < 3 )
541 continue;
542
543 int outlineIdx = out.NewOutline();
544
545 for( const VECTOR2I& pt : poly.front() )
546 out.Append( pt.x, pt.y, outlineIdx, /*aHole=*/-1 );
547
548 for( std::size_t h = 1; h < poly.size(); ++h )
549 {
550 int holeIdx = out.NewHole( outlineIdx );
551
552 for( const VECTOR2I& pt : poly[h] )
553 out.Append( pt.x, pt.y, outlineIdx, holeIdx );
554 }
555 }
556
557 return out;
558}
559
560
562{
563 switch( aKind )
564 {
565 case CHANGE_KIND::ADDED: return CATEGORY::ADDED;
566 case CHANGE_KIND::REMOVED: return CATEGORY::REMOVED;
567 case CHANGE_KIND::MODIFIED: return CATEGORY::MODIFIED;
569 case CHANGE_KIND::DUPLICATE_UUID: return CATEGORY::CONFLICT;
570 }
571
572 wxFAIL_MSG( wxS( "Unknown CHANGE_KIND" ) );
573 return CATEGORY::MODIFIED;
574}
575
576
577const std::vector<SCENE_SHAPE>& ShapesFor( const DIFF_SCENE& aScene, CATEGORY aCategory )
578{
579 switch( aCategory )
580 {
581 case CATEGORY::ADDED: return aScene.addedShapes;
582 case CATEGORY::REMOVED: return aScene.removedShapes;
583 case CATEGORY::MODIFIED: return aScene.modifiedShapes;
584 case CATEGORY::CONFLICT: return aScene.conflictShapes;
585 }
586
587 wxFAIL_MSG( wxS( "Unknown CATEGORY" ) );
588 return aScene.modifiedShapes;
589}
590
591
592std::optional<BOX2I> HighlightedBBox( const DIFF_SCENE& aScene, const KIID_PATH& aChangeId,
593 const std::array<bool, CATEGORY_COUNT>& aCategoryVisible )
594{
595 std::optional<BOX2I> result;
596
597 for( CATEGORY cat : PAINT_ORDER )
598 {
599 if( !aCategoryVisible[static_cast<std::size_t>( cat )] )
600 continue;
601
602 for( const SCENE_SHAPE& s : ShapesFor( aScene, cat ) )
603 {
604 if( s.changeId != aChangeId )
605 continue;
606
607 if( !result )
608 result = s.bbox;
609 else
610 result->Merge( s.bbox );
611 }
612 }
613
614 return result;
615}
616
617} // namespace KICAD_DIFF
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr const Vec & GetOrigin() const
Definition box2.h:207
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.
SIDE_BY_SIDE_SCENES SplitSceneBySide(const DIFF_SCENE &aScene)
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.
Diff colors with defaults for light backgrounds.
Definition diff_scene.h:278
static DIFF_COLOR_THEME ForText(bool aDarkTheme)
Resolve change colors for UI text. Pass the desktop theme captured on the UI thread.
std::vector< SCENE_SHAPE > modifiedShapes
Definition diff_scene.h:235
DOCUMENT_GEOMETRY referenceGeometry
Background geometry from the two source documents.
Definition diff_scene.h:242
std::vector< SCENE_SHAPE > conflictShapes
Definition diff_scene.h:236
std::vector< SCENE_SHAPE > addedShapes
Definition diff_scene.h:233
DOCUMENT_GEOMETRY comparisonGeometry
Definition diff_scene.h:243
std::vector< SCENE_SHAPE > removedShapes
Definition diff_scene.h:234
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
The two halves of a side by side view.
Definition diff_scene.h:333
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708