KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_constraint_overlay.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 2
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
28
30
31#include <set>
32
33#include <board.h>
34#include <math/box2.h>
35#include <pcb_shape.h>
36#include <view/view.h>
37
41
43
44BOOST_AUTO_TEST_SUITE( ConstraintOverlay )
45
46
47BOOST_AUTO_TEST_CASE( OverlayRegistersOncePerLayerAndUnregistersFully )
48{
49 BOARD board;
50 KIGFX::VIEW view;
51
52 BOX2I maxBox;
53 maxBox.SetMaximum();
54
55 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> baseline;
56 view.Query( maxBox, baseline );
57
58 {
59 CONSTRAINT_OVERLAY overlay( &board, &view );
60
61 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> withOverlay;
62 view.Query( maxBox, withOverlay );
63
64 // Each item may appear once per layer it occupies. A duplicate (item, layer) pair means
65 // the same VIEW_ITEM was added to the view twice and can only be removed once.
66 std::set<KIGFX::VIEW::LAYER_ITEM_PAIR> unique( withOverlay.begin(), withOverlay.end() );
67 BOOST_CHECK_EQUAL( unique.size(), withOverlay.size() );
68
69 // The overlay registers two items: the world-space tint overlay and the screen-constant
70 // badge item.
71 BOOST_CHECK_EQUAL( withOverlay.size(), baseline.size() + 2 );
72 }
73
74 // After destruction the view must hold no leftover (dangling) reference to the overlay. The
75 // pair query only collects pointers, so this is safe even when the overlay has been freed.
76 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> afterDestruction;
77 view.Query( maxBox, afterDestruction );
78
79 BOOST_CHECK_EQUAL( afterDestruction.size(), baseline.size() );
80}
81
82
84
85
86BOOST_AUTO_TEST_SUITE( ConstraintBadges )
87
88
89BOOST_AUTO_TEST_CASE( BadgesFanOutAndSelectionToggles )
90{
91 constexpr int MM = 1000000;
92
93 BOARD board;
94 KIGFX::VIEW view;
95
96 PCB_SHAPE* seg = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
97 seg->SetStart( VECTOR2I( 0, 0 ) );
98 seg->SetEnd( VECTOR2I( 10 * MM, 0 ) );
99 board.Add( seg );
100
101 // Two constraints whose first member resolves to the same point: their badges must not stack.
102 auto* c1 = new PCB_CONSTRAINT( &board, PCB_CONSTRAINT_TYPE::FIXED_POSITION );
103 c1->AddMember( seg->m_Uuid, CONSTRAINT_ANCHOR::START );
104 board.Add( c1 );
105
106 auto* c2 = new PCB_CONSTRAINT( &board, PCB_CONSTRAINT_TYPE::FIXED_POSITION );
107 c2->AddMember( seg->m_Uuid, CONSTRAINT_ANCHOR::START );
108 board.Add( c2 );
109
110 CONSTRAINT_OVERLAY overlay( &board, &view );
111 overlay.Update( DiagnoseBoardConstraints( &board ) );
112
113 BOOST_REQUIRE_EQUAL( overlay.Badges().size(), 2 );
114
115 // The badges store their (shared) anchor; the fan-out lives in the screen-space layout.
116 BOOST_CHECK_EQUAL( overlay.Badges()[0].pos, overlay.Badges()[1].pos );
117
118 // LayoutBadges separates same-anchor badges by at least the fan step at any zoom, and the
119 // separation scales with world-per-pixel -- the regression against the old fixed 1.5 mm fan.
120 for( double worldPerPx : { 200.0, 8000.0 } )
121 {
122 std::vector<VECTOR2D> layout = CONSTRAINT_OVERLAY::LayoutBadges( overlay.Badges(), worldPerPx );
123
124 BOOST_REQUIRE_EQUAL( layout.size(), 2 );
125 double dist = ( layout[0] - layout[1] ).EuclideanNorm();
126 BOOST_TEST( dist >= 18.0 * worldPerPx - 1.0 );
127 }
128
129 // Selection state changes only on a real change.
130 BOOST_TEST( overlay.SetSelected( c1->m_Uuid ) );
131 BOOST_TEST( !overlay.SetSelected( c1->m_Uuid ) );
132 BOOST_CHECK( overlay.GetSelected() == c1->m_Uuid );
133 BOOST_TEST( overlay.SetSelected( niluuid ) );
134}
135
136
137// Badges on DIFFERENT anchors that project within a glyph of each other at far zoom-out are still
138// pushed apart by the layout (the cross-anchor de-overlap the old world-space fan also did).
139BOOST_AUTO_TEST_CASE( LayoutDeOverlapsNearbyAnchors )
140{
141 constexpr int MM = 1000000;
142
143 BOARD board;
144 KIGFX::VIEW view;
145
146 PCB_SHAPE* seg = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
147 seg->SetStart( VECTOR2I( 0, 0 ) );
148 seg->SetEnd( VECTOR2I( MM / 10, 0 ) ); // endpoints only 0.1 mm apart
149 board.Add( seg );
150
151 auto* c1 = new PCB_CONSTRAINT( &board, PCB_CONSTRAINT_TYPE::FIXED_POSITION );
152 c1->AddMember( seg->m_Uuid, CONSTRAINT_ANCHOR::START );
153 board.Add( c1 );
154
155 auto* c2 = new PCB_CONSTRAINT( &board, PCB_CONSTRAINT_TYPE::FIXED_POSITION );
156 c2->AddMember( seg->m_Uuid, CONSTRAINT_ANCHOR::END );
157 board.Add( c2 );
158
159 CONSTRAINT_OVERLAY overlay( &board, &view );
160 overlay.Update( DiagnoseBoardConstraints( &board ) );
161 BOOST_REQUIRE_EQUAL( overlay.Badges().size(), 2 );
162
163 const double worldPerPx = 8000.0;
164 std::vector<VECTOR2D> layout = CONSTRAINT_OVERLAY::LayoutBadges( overlay.Badges(), worldPerPx );
165
166 BOOST_TEST( ( layout[0] - layout[1] ).EuclideanNorm() >= 18.0 * worldPerPx - 1.0 );
167}
168
169
170// The visibility mode and hover filter are orthogonal: ALWAYS shows every badge, HOVER with nothing
171// hovered shows none, HOVER with a shape shows only its constraints, and a panel isolation wins.
172BOOST_AUTO_TEST_CASE( VisibilityModeAndHoverFilter )
173{
174 constexpr int MM = 1000000;
175
176 BOARD board;
177 KIGFX::VIEW view;
178
179 PCB_SHAPE* seg1 = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
180 seg1->SetStart( VECTOR2I( 0, 0 ) );
181 seg1->SetEnd( VECTOR2I( 10 * MM, 0 ) );
182 board.Add( seg1 );
183
184 PCB_SHAPE* seg2 = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
185 seg2->SetStart( VECTOR2I( 0, 10 * MM ) );
186 seg2->SetEnd( VECTOR2I( 10 * MM, 10 * MM ) );
187 board.Add( seg2 );
188
189 auto* c1 = new PCB_CONSTRAINT( &board, PCB_CONSTRAINT_TYPE::FIXED_POSITION );
190 c1->AddMember( seg1->m_Uuid, CONSTRAINT_ANCHOR::START );
191 board.Add( c1 );
192
193 auto* c2 = new PCB_CONSTRAINT( &board, PCB_CONSTRAINT_TYPE::FIXED_POSITION );
194 c2->AddMember( seg2->m_Uuid, CONSTRAINT_ANCHOR::START );
195 board.Add( c2 );
196
198 CONSTRAINT_OVERLAY overlay( &board, &view );
199
200 overlay.SetVisibilityMode( OVERLAY_MODE::ALWAYS );
201 overlay.Update( diag );
202 BOOST_CHECK_EQUAL( overlay.Badges().size(), 2 );
203
204 overlay.SetVisibilityMode( OVERLAY_MODE::HOVER );
205 overlay.Update( diag );
206 BOOST_CHECK_EQUAL( overlay.Badges().size(), 0 ); // nothing hovered -> hidden
207
208 overlay.SetHoverShape( seg1->m_Uuid );
209 overlay.Update( diag );
210 BOOST_REQUIRE_EQUAL( overlay.Badges().size(), 1 );
211 BOOST_CHECK( overlay.Badges()[0].constraint == c1->m_Uuid );
212
213 overlay.SetIsolated( c2->m_Uuid ); // panel focus wins over hover
214 overlay.Update( diag );
215 BOOST_REQUIRE_EQUAL( overlay.Badges().size(), 1 );
216 BOOST_CHECK( overlay.Badges()[0].constraint == c2->m_Uuid );
217}
218
219
220BOOST_AUTO_TEST_CASE( NearestConstrainedShapeUsesCandidatesOnly )
221{
222 constexpr int MM = 1000000;
223
224 BOARD board;
225 PCB_SHAPE* seg = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
226 seg->SetStart( VECTOR2I( 0, 0 ) );
227 seg->SetEnd( VECTOR2I( 10 * MM, 0 ) );
228 board.Add( seg );
229
230 std::vector<PCB_SHAPE*> candidates = { seg };
231
232 std::optional<KIID> hit = NearestConstrainedShape( candidates, VECTOR2I( 5 * MM, 1000 ), 5000 );
233 BOOST_REQUIRE( hit.has_value() );
234 BOOST_CHECK( *hit == seg->m_Uuid );
235
236 BOOST_CHECK( !NearestConstrainedShape( candidates, VECTOR2I( 5 * MM, 5 * MM ), 5000 ).has_value() );
237 BOOST_CHECK( !NearestConstrainedShape( {}, VECTOR2I( 0, 0 ), 5000 ).has_value() );
238}
239
240
BOARD_CONSTRAINT_DIAGNOSTICS DiagnoseBoardConstraints(BOARD *aBoard)
Diagnose every constraint cluster on the board (validate only – geometry is not changed) and return t...
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1355
constexpr void SetMaximum()
Definition box2.h:76
static std::vector< VECTOR2D > LayoutBadges(const std::vector< CONSTRAINT_BADGE > &aBadges, double aWorldPerPx)
The on-screen draw position (world units) of each badge at the given scale: the anchor offset by Badg...
const KIID m_Uuid
Definition eda_item.h:531
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
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
A geometric constraint between board items (issue #2329).
void SetEnd(const VECTOR2I &aEnd) override
void SetStart(const VECTOR2I &aStart) override
std::optional< KIID > NearestConstrainedShape(const std::vector< PCB_SHAPE * > &aCandidates, const VECTOR2I &aPos, int aMaxDist)
The candidate shape whose outline aPos hits within aMaxDist, or std::nullopt.
@ SEGMENT
Definition eda_shape.h:46
KIID niluuid(0)
@ START
First endpoint of a segment or arc.
@ END
Second endpoint of a segment or arc.
@ FIXED_POSITION
A point is locked at its current location.
std::shared_ptr< PNS_LOG_VIEWER_OVERLAY > overlay
Board-wide diagnostics for the constraint overlay and info bar.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(OverlayRegistersOncePerLayerAndUnregistersFully)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST(netlist.find("R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)") !=std::string::npos)
static const long long MM
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683