KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pcb_snap_pipeline.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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <algorithm>
21#include <chrono>
22#include <memory>
23#include <thread>
24
26
27#include <board.h>
28#include <footprint.h>
31#include <pad.h>
32#include <pcb_painter.h>
33#include <pcb_shape.h>
34#include <pcb_track.h>
35#include <pcb_view.h>
36#include <pcbnew_settings.h>
37#include <tool/tool_manager.h>
39#include <tools/pcb_tool_base.h>
40
41
42namespace
43{
44const int MM = pcbIUScale.mmToIU( 1.0 );
45
46
47class HEADLESS_GAL : public KIGFX::GAL
48{
49public:
50 explicit HEADLESS_GAL( KIGFX::GAL_DISPLAY_OPTIONS& aOptions ) :
51 GAL( aOptions )
52 {
53 }
54};
55
56
57class TEST_PCB_GRID_HELPER : public PCB_GRID_HELPER
58{
59public:
60 TEST_PCB_GRID_HELPER( TOOL_MANAGER* aToolMgr, MAGNETIC_SETTINGS* aMagneticSettings ) :
61 PCB_GRID_HELPER( aToolMgr, aMagneticSettings )
62 {
63 }
64
65 SNAP_MANAGER& SnapManager() { return getSnapManager(); }
66 int SnapRange() const { return computeSnapRanges( m_enableGrid ).range; }
67 int SnapIn() const { return computeSnapRanges( m_enableGrid ).in; }
68 int SnapOut() const { return computeSnapRanges( m_enableGrid ).out; }
69 int MarkerTypes() const { return m_viewSnapPoint.GetSnapTypes(); }
70 VECTOR2I MarkerPosition() const { return m_viewSnapPoint.GetPosition(); }
71 const std::optional<ANCHOR>& SnapItem() const { return m_snapItem; }
72};
73
74
75struct PCB_SNAP_FIXTURE
76{
77 KIGFX::GAL_DISPLAY_OPTIONS options;
78 HEADLESS_GAL gal;
79 KIGFX::PCB_VIEW view;
80 KIGFX::PCB_PAINTER painter;
81 BOARD board;
82 PCBNEW_SETTINGS settings;
83 TOOL_MANAGER manager;
84 MAGNETIC_SETTINGS magnetic;
85 PCB_TOOL_BASE* tool = nullptr;
86 std::unique_ptr<TEST_PCB_GRID_HELPER> helper;
87
88 PCB_SNAP_FIXTURE() :
89 gal( options ),
90 painter( &gal, FRAME_PCB_EDITOR )
91 {
92 gal.SetGridSize( { MM, MM } );
93 gal.SetGridOrigin( { 0, 0 } );
94 gal.SetWorldUnitLength( 1e-9 / 0.0254 );
95 gal.SetScreenSize( { 2000, 1200 } );
96 view.SetGAL( &gal );
97 view.SetPainter( &painter );
98 view.SetCenter( { 2 * MM, 2 * MM } );
99 view.SetScale( 10.0 );
100 view.SetLayerVisible( LAYER_TRACKS, true );
101 view.SetLayerVisible( LAYER_PADS, true );
102 view.SetLayerVisible( LAYER_FOOTPRINTS_FR, true );
103 view.SetLayerVisible( LAYER_FOOTPRINTS_BK, true );
104 view.Redraw();
105
106 manager.SetEnvironment( &board, &view, nullptr, &settings, nullptr );
107 tool = new PCB_TOOL_BASE( "qa.PcbSnapTool" );
108 manager.RegisterTool( tool );
109 manager.InvokeTool( "qa.PcbSnapTool" );
110
111 magnetic.pads = MAGNETIC_OPTIONS::CAPTURE_ALWAYS;
112 magnetic.tracks = MAGNETIC_OPTIONS::CAPTURE_ALWAYS;
113 magnetic.graphics = true;
114 magnetic.allLayers = true;
115 helper = std::make_unique<TEST_PCB_GRID_HELPER>( &manager, &magnetic );
116 }
117
118 PCB_SHAPE* AddSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, PCB_LAYER_ID aLayer = F_SilkS )
119 {
120 PCB_SHAPE* segment = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
121 segment->SetStart( aStart );
122 segment->SetEnd( aEnd );
123 segment->SetLayer( aLayer );
124 board.Add( segment );
125 view.Add( segment );
126 return segment;
127 }
128
129 PCB_TRACK* AddTrack( const VECTOR2I& aStart, const VECTOR2I& aEnd, PCB_LAYER_ID aLayer = F_Cu )
130 {
131 PCB_TRACK* track = new PCB_TRACK( &board );
132 track->SetStart( aStart );
133 track->SetEnd( aEnd );
134 track->SetLayer( aLayer );
135 track->SetWidth( 250000 );
136 board.Add( track );
137 view.Add( track );
138 return track;
139 }
140
141 PAD* AddPad( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer = F_Cu )
142 {
143 FOOTPRINT* footprint = new FOOTPRINT( &board );
144 footprint->SetPosition( aPosition + VECTOR2I( 2 * MM, 2 * MM ) );
145 footprint->SetLayer( aLayer == B_Cu ? B_Cu : F_Cu );
146
147 PAD* pad = new PAD( footprint );
148 pad->SetPosition( aPosition );
149 pad->SetShape( aLayer, PAD_SHAPE::CIRCLE );
150 pad->SetSize( aLayer, { MM, MM } );
151 pad->SetLayerSet( LSET( { aLayer } ) );
152 footprint->Add( pad );
153 board.Add( footprint );
154 view.Add( footprint );
155 return pad;
156 }
157
158 std::vector<PAD*> AddFootprintPads( const std::vector<VECTOR2I>& aPositions )
159 {
160 FOOTPRINT* footprint = new FOOTPRINT( &board );
161 footprint->SetPosition( aPositions.front() );
162 footprint->SetLayer( F_Cu );
163
164 std::vector<PAD*> pads;
165
166 for( const VECTOR2I& position : aPositions )
167 {
168 PAD* pad = new PAD( footprint );
169 pad->SetPosition( position );
170 pad->SetShape( F_Cu, PAD_SHAPE::CIRCLE );
171 pad->SetSize( F_Cu, { MM, MM } );
172 pad->SetLayerSet( LSET( { F_Cu } ) );
173 footprint->Add( pad );
174 pads.push_back( pad );
175 }
176
177 board.Add( footprint );
178 view.Add( footprint );
179 return pads;
180 }
181};
182} // namespace
183
184
185BOOST_AUTO_TEST_SUITE( PcbSnapPipeline )
186
187
188BOOST_FIXTURE_TEST_CASE( DisabledSnapUsesGridWhenEnabled, PCB_SNAP_FIXTURE )
189{
190 helper->SetSnap( false );
191 helper->SetUseGrid( true );
192
193 SNAP_RESULT result = helper->ResolveSnap( { 1400000, 1600000 }, LSET::AllLayersMask() );
194
195 BOOST_CHECK_EQUAL( result.position, VECTOR2I( MM, 2 * MM ) );
196 BOOST_CHECK_EQUAL( helper->GetSnapped(), nullptr );
197}
198
199
200BOOST_FIXTURE_TEST_CASE( DisabledSnapUsesCursorWhenGridDisabled, PCB_SNAP_FIXTURE )
201{
202 helper->SetSnap( false );
203 helper->SetUseGrid( false );
204 const VECTOR2I cursor( 1400000, 1600000 );
205
206 SNAP_RESULT result = helper->ResolveSnap( cursor, LSET::AllLayersMask() );
207
208 BOOST_CHECK_EQUAL( result.position, cursor );
209 BOOST_CHECK_EQUAL( helper->GetSnapped(), nullptr );
210}
211
212
213BOOST_FIXTURE_TEST_CASE( TrackEndpointWinsOverGrid, PCB_SNAP_FIXTURE )
214{
215 PCB_TRACK* track = AddTrack( { 1410000, 1590000 }, { 4 * MM, 1590000 } );
216 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> visible;
217 view.Query( BOX2I( { -10 * MM, -10 * MM }, { 20 * MM, 20 * MM } ), visible );
218
219 BOOST_REQUIRE( view.IsVisible( track ) );
220 BOOST_REQUIRE_GT( visible.size(), 0 );
221 BOOST_REQUIRE_GT( helper->SnapRange(), 10000 );
222 BOOST_REQUIRE_LT( track->ViewGetLOD( F_Cu, &view ), view.GetScale() );
223 SNAP_RESULT result = helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) );
224
225 BOOST_CHECK_EQUAL( result.position, track->GetStart() );
226 BOOST_CHECK_EQUAL( helper->GetSnapped(), track );
227}
228
229
230BOOST_FIXTURE_TEST_CASE( IntrinsicAnchorWinsOverConstructionLine, PCB_SNAP_FIXTURE )
231{
232 helper->SetSnapLineDirections( { { 1, 0 } } );
233 helper->SetSnapLineOrigin( { 0, 1600000 } );
234 const VECTOR2I cursor( 1400000, 1600000 );
235
236 SNAP_RESULT guideResult = helper->ResolveSnap( cursor, LSET( { F_Cu } ) );
237 BOOST_REQUIRE_EQUAL( guideResult.position, VECTOR2I( MM, 1600000 ) );
238
239 PCB_TRACK* track = AddTrack( { 1410000, 1590000 }, { 4 * MM, 1590000 } );
240 SNAP_RESULT result = helper->ResolveSnap( cursor, LSET( { F_Cu } ) );
241 BOOST_CHECK_EQUAL( result.position, track->GetStart() );
242 BOOST_CHECK_EQUAL( helper->GetSnapped(), track );
243}
244
245
246BOOST_FIXTURE_TEST_CASE( RetainedAnchorUsesSnapOutThreshold, PCB_SNAP_FIXTURE )
247{
248 const VECTOR2I anchor( 2 * MM, 2 * MM );
249 PCB_TRACK* track = AddTrack( anchor, anchor - VECTOR2I( 4 * MM, 0 ) );
250 helper->SetSnapLine( false );
251 helper->ResolveSnap( anchor, LSET( { F_Cu } ) );
252 BOOST_REQUIRE_EQUAL( helper->GetSnapped(), track );
253
254 SNAP_RESULT inside = helper->ResolveSnap( anchor + VECTOR2I( helper->SnapOut() - 1, 0 ), LSET( { F_Cu } ) );
256 BOOST_CHECK_EQUAL( helper->GetSnapped(), track );
257
258 SNAP_RESULT outside = helper->ResolveSnap( anchor + VECTOR2I( helper->SnapOut() + 1, 0 ), LSET( { F_Cu } ) );
259 BOOST_CHECK_NE( outside.position, anchor );
260 BOOST_CHECK_EQUAL( helper->GetSnapped(), nullptr );
261}
262
263
264BOOST_FIXTURE_TEST_CASE( ReferenceOnlyPointUpdatesGuideWithoutSnapping, PCB_SNAP_FIXTURE )
265{
266 const VECTOR2I reference( 1410000, 1590000 );
267 AddTrack( reference, { 4 * MM, 1590000 } );
268 helper->SnapManager().SetReferenceOnlyPoints( { reference } );
269
270 SNAP_RESULT result = helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) );
271
272 BOOST_CHECK_NE( result.position, reference );
273 BOOST_CHECK_EQUAL( helper->GetSnapped(), nullptr );
274 const OPT_VECTOR2I& origin = helper->SnapManager().GetSnapLineManager().GetSnapLineOrigin();
275 BOOST_REQUIRE( origin );
276 BOOST_CHECK_EQUAL( *origin, reference );
277}
278
279
280BOOST_FIXTURE_TEST_CASE( DefaultInferenceSnapsToElementWithGridEnabled, PCB_SNAP_FIXTURE )
281{
282 AddTrack( { 0, 1500000 }, { 4 * MM, 1500000 } );
283 const VECTOR2I cursor( 1400000, 1510000 );
284
285 helper->SetUseGrid( true );
286 SNAP_RESULT result = helper->ResolveSnap( cursor, LSET( { F_Cu } ) );
287
288 BOOST_CHECK_EQUAL( result.position, VECTOR2I( MM, 1500000 ) );
289}
290
291
292BOOST_FIXTURE_TEST_CASE( AlignmentFindsTargetFarAlongGuideDirection, PCB_SNAP_FIXTURE )
293{
294 PCB_SHAPE* moving = AddSegment( { 2 * MM, 2 * MM }, { 3 * MM, 2 * MM } );
295 AddSegment( { 2 * MM, 40 * MM }, { 3 * MM, 40 * MM } );
296 view.SetViewport( BOX2D( { 0, 0 }, { 50 * MM, 50 * MM } ) );
298
299 SNAP_RESULT result = helper->ResolveSnap( moving->GetStart(), LSET( { F_SilkS } ), GRID_CURRENT, { moving },
300 moving->GetStart() );
301
302 BOOST_CHECK( std::any_of( result.accepted.begin(), result.accepted.end(),
303 []( const SNAP_STABLE_ID& aId )
304 {
305 return aId.kind == SNAP_ID_KIND::BOUNDS_X;
306 } ) );
307 BOOST_CHECK( std::any_of( result.guides.begin(), result.guides.end(),
308 []( const SNAP_GUIDE& aGuide )
309 {
310 return std::abs( aGuide.end.y - aGuide.start.y ) > 30 * MM;
311 } ) );
312}
313
314
315BOOST_FIXTURE_TEST_CASE( EqualSpacingFindsTargetsFarAlongSpacingAxis, PCB_SNAP_FIXTURE )
316{
317 AddSegment( { 2 * MM, 2 * MM }, { 3 * MM, 3 * MM } );
318 AddSegment( { 12 * MM, 2 * MM }, { 13 * MM, 3 * MM } );
319 PCB_SHAPE* moving = AddSegment( { 22 * MM, 2 * MM }, { 23 * MM, 3 * MM } );
320 view.SetViewport( BOX2D( { 0, 0 }, { 50 * MM, 50 * MM } ) );
322
323 SNAP_RESULT result = helper->ResolveSnap( moving->GetStart(), LSET( { F_SilkS } ), GRID_CURRENT, { moving },
324 moving->GetStart() );
325
326 BOOST_CHECK( std::any_of( result.accepted.begin(), result.accepted.end(),
327 []( const SNAP_STABLE_ID& aId )
328 {
329 return aId.kind == SNAP_ID_KIND::COPY_GAP_X;
330 } ) );
331}
332
333
334BOOST_FIXTURE_TEST_CASE( FootprintEditorSpacesPadsEvenly, PCB_SNAP_FIXTURE )
335{
336 std::vector<PAD*> pads = AddFootprintPads( { { 2 * MM, 2 * MM }, { 12 * MM, 2 * MM }, { 22 * MM, 2 * MM } } );
337 view.SetViewport( BOX2D( { 0, 0 }, { 50 * MM, 50 * MM } ) );
340
341 PAD* moving = pads.back();
342 SNAP_RESULT result = helper->ResolveSnap( moving->GetPosition(), LSET( { F_Cu } ), GRID_CURRENT, { moving },
343 moving->GetPosition() );
344
345 BOOST_CHECK( std::any_of( result.accepted.begin(), result.accepted.end(),
346 []( const SNAP_STABLE_ID& aId )
347 {
348 return aId.kind == SNAP_ID_KIND::COPY_GAP_X
349 || aId.kind == SNAP_ID_KIND::EQUAL_GAP_X;
350 } ) );
351}
352
353
354BOOST_FIXTURE_TEST_CASE( BoardKeepsPadsInsideTheirFootprint, PCB_SNAP_FIXTURE )
355{
356 std::vector<PAD*> pads = AddFootprintPads( { { 2 * MM, 2 * MM }, { 12 * MM, 2 * MM }, { 22 * MM, 2 * MM } } );
357 view.SetViewport( BOX2D( { 0, 0 }, { 50 * MM, 50 * MM } ) );
359
360 PAD* moving = pads.back();
361 SNAP_RESULT result = helper->ResolveSnap( moving->GetPosition(), LSET( { F_Cu } ), GRID_CURRENT, { moving },
362 moving->GetPosition() );
363
364 // One footprint collapses to one object, so its pads cannot form a spacing series.
365 BOOST_CHECK( std::none_of( result.accepted.begin(), result.accepted.end(),
366 []( const SNAP_STABLE_ID& aId )
367 {
368 return aId.kind == SNAP_ID_KIND::COPY_GAP_X
369 || aId.kind == SNAP_ID_KIND::EQUAL_GAP_X;
370 } ) );
371}
372
373
374BOOST_FIXTURE_TEST_CASE( DraggedPadDoesNotAlignToItsOwnStartPosition, PCB_SNAP_FIXTURE )
375{
376 // A free pad's own footprint stays a layout object, so its pad list still reaches the pad
377 // being dragged even though the skip list excluded it.
378 std::vector<PAD*> pads = AddFootprintPads( { { 2 * MM, 2 * MM }, { 12 * MM, 9 * MM } } );
379 view.SetViewport( BOX2D( { 0, 0 }, { 50 * MM, 50 * MM } ) );
381
382 PAD* moving = pads.back();
383 const VECTOR2I origin = moving->GetPosition();
384 std::vector<BOARD_ITEM*> dragged = { moving };
385
386 // Grabbing a pad centre is what admits pad-centre alignment targets at all.
387 helper->BestDragOrigin( origin, dragged );
388 helper->SetUseGrid( false );
389
390 const VECTOR2I cursor( 30 * MM, origin.y + 100000 );
391 SNAP_RESULT result = helper->ResolveSnap( cursor, LSET( { F_Cu } ), GRID_CURRENT, dragged, origin );
392
393 BOOST_CHECK( result.position.y != origin.y );
394}
395
396
397BOOST_FIXTURE_TEST_CASE( LegacyFallbackSuppressesPointOnElementWhenGridEnabled, PCB_SNAP_FIXTURE )
398{
399 AddTrack( { 0, 1500000 }, { 4 * MM, 1500000 } );
400 const VECTOR2I cursor( 1400000, 1510000 );
401 settings.m_SnapInference.objectGeometry = false;
402
403 helper->SetUseGrid( true );
404 SNAP_RESULT gridResult = helper->ResolveSnap( cursor, LSET( { F_Cu } ) );
405 BOOST_CHECK_EQUAL( gridResult.position, VECTOR2I( MM, 2 * MM ) );
406
407 helper->SetUseGrid( false );
408 SNAP_RESULT elementResult = helper->ResolveSnap( cursor, LSET( { F_Cu } ) );
409 BOOST_CHECK_EQUAL( elementResult.position, VECTOR2I( cursor.x, 1500000 ) );
410 BOOST_CHECK_EQUAL( helper->MarkerTypes(), POINT_TYPE::PT_ON_ELEMENT );
411}
412
413
414BOOST_FIXTURE_TEST_CASE( TrackMagnetismTogglesIndependently, PCB_SNAP_FIXTURE )
415{
416 const VECTOR2I anchor( 1410000, 1590000 );
417 PCB_TRACK* track = AddTrack( anchor, { 4 * MM, 1590000 }, B_Cu );
419
420 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
421
423 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
425 magnetic.allLayers = false;
426 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
427
428 magnetic.allLayers = true;
429 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, anchor );
430 BOOST_CHECK_EQUAL( helper->GetSnapped(), track );
431}
432
433
434BOOST_FIXTURE_TEST_CASE( GraphicsMagnetismAndLayerMatchingToggleIndependently, PCB_SNAP_FIXTURE )
435{
436 const VECTOR2I anchor( 1410000, 1590000 );
437 PCB_SHAPE* segment = AddSegment( anchor, { 4 * MM, 1590000 }, B_SilkS );
438 magnetic.graphics = false;
439 magnetic.allLayers = true;
440
441 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
442
443 magnetic.graphics = true;
444 magnetic.allLayers = false;
445 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
446
447 magnetic.allLayers = true;
448 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, anchor );
449 BOOST_CHECK_EQUAL( helper->GetSnapped(), segment );
450}
451
452
453BOOST_FIXTURE_TEST_CASE( PadMagnetismTogglesIndependently, PCB_SNAP_FIXTURE )
454{
455 const VECTOR2I anchor( 1410000, 1590000 );
456 PAD* pad = AddPad( anchor, B_Cu );
458
459 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
460
462 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
464 magnetic.allLayers = false;
465 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, VECTOR2I( MM, 2 * MM ) );
466
467 magnetic.allLayers = true;
468 BOOST_CHECK_EQUAL( helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) ).position, anchor );
469 BOOST_CHECK_EQUAL( helper->GetSnapped(), pad );
470}
471
472
473BOOST_FIXTURE_TEST_CASE( FeasibilityFallsBackToNextObjectCandidate, PCB_SNAP_FIXTURE )
474{
475 const VECTOR2I firstSelfPoint( 1410000, 1590000 );
476 const VECTOR2I secondSelfPoint( 1420000, 1580000 );
477 PCB_SHAPE* moving = AddSegment( firstSelfPoint, secondSelfPoint );
478 helper->SetPointEditProfile( true );
479 helper->SetStationarySelfGeometry( { moving->GetStart(), moving->GetEnd() }, {} );
480 helper->SetFeasibilityCallback(
481 [firstSelfPoint]( const SNAP_SOURCE_CONTEXT& aContext, const std::vector<SNAP_CANDIDATE>& aTrial )
482 {
484 result.position = aContext.sourcePoint;
485
486 if( aTrial.empty() )
487 return result;
488
489 const SNAP_CANDIDATE& candidate = aTrial.back();
490
491 if( KiROUND( candidate.origin.x ) == firstSelfPoint.x )
492 {
494 return result;
495 }
496
497 result.position = KiROUND( candidate.origin );
498 result.remainingDof = 0;
499 return result;
500 } );
501
502 SNAP_RESULT result = helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_SilkS } ), GRID_CURRENT, { moving } );
503
504 BOOST_CHECK_EQUAL( result.position, secondSelfPoint );
505}
506
507
508BOOST_FIXTURE_TEST_CASE( AcceptedAnchorDrivesPresentationPayload, PCB_SNAP_FIXTURE )
509{
510 const VECTOR2I anchor( 1410000, 1590000 );
511 PCB_TRACK* track = AddTrack( anchor, { 4 * MM, 1590000 } );
512
513 SNAP_RESULT result = helper->ResolveSnap( { 1400000, 1600000 }, LSET( { F_Cu } ) );
514
515 BOOST_REQUIRE_EQUAL( result.accepted.size(), 1 );
516 BOOST_CHECK( result.accepted.front().kind == SNAP_ID_KIND::INTRINSIC_ANCHOR );
517 BOOST_CHECK_EQUAL( helper->GetSnapped(), track );
518 BOOST_CHECK_EQUAL( helper->MarkerPosition(), anchor );
519 BOOST_CHECK_EQUAL( helper->MarkerTypes(), POINT_TYPE::PT_END );
520
521 std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH> batches;
522 helper->SnapManager().GetConstructionManager().GetPendingConstructionItems( batches );
523 BOOST_REQUIRE_EQUAL( batches.size(), 1 );
524 BOOST_REQUIRE_EQUAL( batches.front().size(), 1 );
525 BOOST_CHECK_EQUAL( batches.front().front().Item, track );
526
527 helper->ResolveSnap( anchor + VECTOR2I( 1, 1 ), LSET( { F_Cu } ) );
528 batches.clear();
529 helper->SnapManager().GetConstructionManager().GetPendingConstructionItems( batches );
530 BOOST_REQUIRE_EQUAL( batches.size(), 1 );
531 BOOST_REQUIRE_EQUAL( batches.front().size(), 1 );
532 BOOST_CHECK_EQUAL( batches.front().front().Item, track );
533}
534
535
536BOOST_FIXTURE_TEST_CASE( CoincidentAcceptedIdSelectsOwningAnchorPayload, PCB_SNAP_FIXTURE )
537{
538 const VECTOR2I anchor( 1410000, 1590000 );
539 settings.m_SnapInference.objectGeometry = false;
541 PCB_TRACK* retainedTrack = AddTrack( anchor, { 4 * MM, 1590000 } );
542 helper->SetSnapLine( false );
543 SNAP_RESULT retainedResult = helper->ResolveSnap( anchor, LSET( { F_Cu } ) );
544 BOOST_REQUIRE_EQUAL( helper->GetSnapped(), retainedTrack );
545 BOOST_REQUIRE_EQUAL( retainedResult.accepted.size(), 1 );
546
547 retainedTrack->SetLayer( B_Cu );
548 PCB_TRACK* acceptedTrack = AddTrack( anchor, { 1410000, 4 * MM } );
549 magnetic.allLayers = false;
550 helper->SetUseGrid( false );
551 const SNAP_STABLE_ID rejectedId = retainedResult.accepted.front();
552 helper->SetFeasibilityCallback(
553 [rejectedId]( const SNAP_SOURCE_CONTEXT& aContext, const std::vector<SNAP_CANDIDATE>& aCandidates )
554 {
556 result.position = aContext.sourcePoint;
557
558 if( aCandidates.empty() )
559 return result;
560
561 if( aCandidates.back().id == rejectedId )
562 {
564 return result;
565 }
566
567 result.position = KiROUND( aCandidates.back().origin );
568 result.remainingDof = 0;
569 return result;
570 } );
571
572 SNAP_RESULT result = helper->ResolveSnap( anchor, LSET( { F_Cu } ) );
573
574 BOOST_REQUIRE_EQUAL( result.accepted.size(), 1 );
575 BOOST_CHECK( result.accepted.front() != rejectedId );
576 BOOST_CHECK_EQUAL( helper->GetSnapped(), acceptedTrack );
577 BOOST_CHECK_EQUAL( helper->MarkerPosition(), anchor );
578 BOOST_CHECK_EQUAL( helper->MarkerTypes(), POINT_TYPE::PT_END );
579}
580
581
582BOOST_FIXTURE_TEST_CASE( AlignmentGuideSurvivesPresentationReset, PCB_SNAP_FIXTURE )
583{
584 PCB_SHAPE* moving = AddSegment( { 2 * MM, 2 * MM }, { 3 * MM, 2 * MM } );
585 AddSegment( { 2 * MM, 10 * MM }, { 3 * MM, 10 * MM } );
587
588 SNAP_RESULT result = helper->ResolveSnap( moving->GetStart(), LSET( { F_SilkS } ), GRID_CURRENT, { moving },
589 moving->GetStart() );
590 const auto guideIt = std::find_if( result.guides.begin(), result.guides.end(),
591 []( const SNAP_GUIDE& aGuide )
592 {
593 return aGuide.style == SNAP_GUIDE_STYLE::SNAP_LINE;
594 } );
595 BOOST_REQUIRE( guideIt != result.guides.end() );
596 const SNAP_LINE_MANAGER& manager = helper->SnapManager().GetSnapLineManager();
597 BOOST_REQUIRE( manager.GetSnapLineOrigin() );
598 BOOST_CHECK( manager.HasCompleteSnapLine() );
599 BOOST_CHECK_EQUAL( *manager.GetSnapLineOrigin(), guideIt->start );
600}
601
602
603BOOST_FIXTURE_TEST_CASE( DenseCoincidentTieRetainsPartialBudgetResult, PCB_SNAP_FIXTURE )
604{
605 const VECTOR2I anchor( 1410000, 1590000 );
606 settings.m_SnapInference.objectGeometry = false;
608 helper->SetSnapLine( false );
609 helper->SetUseGrid( false );
610
611 for( int i = 0; i < 8; ++i )
612 AddTrack( anchor, { 4 * MM, ( 3 + i ) * MM } );
613
614 helper->SetFeasibilityCallback(
615 []( const SNAP_SOURCE_CONTEXT& aContext, const std::vector<SNAP_CANDIDATE>& aCandidates )
616 {
618 result.position = aContext.sourcePoint;
619 result.remainingDof = 1;
620
621 if( aCandidates.empty() )
622 {
623 // Exceed the resolver's 8 ms deadline before accepting the first candidate.
624 std::this_thread::sleep_for( std::chrono::milliseconds( 12 ) );
625 return result;
626 }
627
628 result.position = KiROUND( aCandidates.back().origin );
629 return result;
630 } );
631
632 SNAP_RESULT result = helper->ResolveSnap( anchor, LSET( { F_Cu } ) );
633
634 BOOST_REQUIRE_EQUAL( result.status, SNAP_RESULT_STATUS::BUDGET_EXHAUSTED );
635 BOOST_REQUIRE_EQUAL( result.accepted.size(), 1 );
636 BOOST_REQUIRE( helper->SnapItem() );
637 const auto& acceptedAnchor = *helper->SnapItem();
638 std::vector<SNAP_TARGET_ID> expectedTargets;
639
640 for( EDA_ITEM* item : acceptedAnchor.items )
641 {
642 if( item )
643 expectedTargets.push_back( item->m_Uuid.AsBytes() );
644 }
645
646 SNAP_STABLE_ID pointId =
647 MakePointSnapId( SNAP_ID_KIND::INTRINSIC_ANCHOR, acceptedAnchor.pos, acceptedAnchor.pointTypes );
648 expectedTargets.push_back( pointId.target );
649 BOOST_CHECK( result.accepted.front()
650 == MakeCompositeSnapId( SNAP_ID_KIND::INTRINSIC_ANCHOR, expectedTargets, acceptedAnchor.pointTypes ) );
651 BOOST_CHECK_EQUAL( result.position, anchor );
652}
653
654
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
@ FPHOLDER
Definition board.h:365
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetBoardUse(BOARD_USE aUse)
Set what the board is going to be used for.
Definition board.h:385
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
const KIID m_Uuid
Definition eda_item.h:531
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
void SetPosition(const VECTOR2I &aPos) override
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Abstract interface for drawing on a 2D-surface.
double GetScale() const
Definition view.h:281
void SetViewport(const BOX2D &aViewport)
Set the visible area of the VIEW.
Definition view.cpp:609
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition view.cpp:487
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition view.cpp:1805
std::array< uint8_t, 16 > AsBytes() const
Definition kiid.cpp:276
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:637
Definition pad.h:61
VECTOR2I GetPosition() const override
Definition pad.cpp:245
SNAP_INFERENCE_SETTINGS m_SnapInference
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:89
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
virtual void SetWidth(int aWidth)
Definition pcb_track.h:86
bool HasCompleteSnapLine() const
const OPT_VECTOR2I & GetSnapLineOrigin() const
@ SEGMENT
Definition eda_shape.h:46
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ GRID_CURRENT
Definition grid_helper.h:57
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:255
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:288
@ LAYER_TRACKS
Definition layer_ids.h:263
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:256
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ F_SilkS
Definition layer_ids.h:96
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
@ PT_END
The point is at the end of a segment, arc, etc.
Definition point_types.h:46
@ PT_ON_ELEMENT
The point is somewhere on another element, but not some specific point.
Definition point_types.h:68
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
SNAP_STABLE_ID MakePointSnapId(SNAP_ID_KIND aKind, const VECTOR2I &aPoint, int aFeatureIndex=0)
SNAP_STABLE_ID MakeCompositeSnapId(SNAP_ID_KIND aKind, const std::vector< SNAP_TARGET_ID > &aTargets, int aFeatureIndex=0)
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
VECTOR2I position
std::vector< SNAP_STABLE_ID > accepted
SNAP_TARGET_ID target
BOOST_FIXTURE_TEST_CASE(ServerStartsAndResponds, API_SERVER_E2E_FIXTURE)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static const long long MM
BOOST_FIXTURE_TEST_CASE(DisabledSnapUsesGridWhenEnabled, PCB_SNAP_FIXTURE)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683