KiCad PCB EDA Suite
Loading...
Searching...
No Matches
construction_manager.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 (C) 2024 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 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
26
28 m_constructionGeomPreview( aHelper )
29{
30}
31
33{
35 {
38
39 m_updateCallback( showAnything );
40 }
41}
42
44 bool aIsPersistent )
45{
46 if( aIsPersistent )
47 {
48 // We only keep one previous persistent batch for the moment
49 m_persistentConstructionBatch = std::move( aBatch );
50 }
51 else
52 {
53 bool anyNewItems = false;
54 for( CONSTRUCTION_ITEM& item : aBatch )
55 {
56 if( m_involvedItems.count( item.Item ) == 0 )
57 {
58 anyNewItems = true;
59 break;
60 }
61 }
62
63 // If there are no new items involved, don't bother adding the batch
64 if( !anyNewItems )
65 {
66 return;
67 }
68
69 // We only keep up to one previous temporary batch and the current one
70 // we could make this a setting if we want to keep more, but it gets cluttered
71 const int maxTempItems = 2;
72
73 while( m_temporaryConstructionBatches.size() >= maxTempItems )
74 {
76 }
77
78 m_temporaryConstructionBatches.emplace_back( std::move( aBatch ) );
79 }
80
81 // Refresh what items are drawn
82
84 m_involvedItems.clear();
85
86 const auto addBatchItems = [&]( const CONSTRUCTION_ITEM_BATCH& aBatchToAdd, bool aPersistent )
87 {
88 for( const CONSTRUCTION_ITEM& item : aBatchToAdd )
89 {
90 // Only show the item if it's not already involved
91 // (avoid double-drawing the same item)
92 if( m_involvedItems.count( item.Item ) == 0 )
93 {
94 m_involvedItems.insert( item.Item );
95
96 for( const KIGFX::CONSTRUCTION_GEOM::DRAWABLE& construction : item.Constructions )
97 {
98 m_constructionGeomPreview.AddDrawable( construction, aPersistent );
99 }
100 }
101 }
102 };
103
105 {
106 addBatchItems( *m_persistentConstructionBatch, true );
107 }
108
110 {
111 addBatchItems( batch, false );
112 }
113
114 updateView();
115}
116
117bool CONSTRUCTION_MANAGER::InvolvesAllGivenRealItems( const std::vector<EDA_ITEM*>& aItems ) const
118{
119 for( EDA_ITEM* item : aItems )
120 {
121 // Null items (i.e. construction items) are always considered involved
122 if( item && m_involvedItems.count( item ) == 0 )
123 {
124 return false;
125 }
126 }
127
128 return true;
129}
130
132{
133 // Setting the origin clears the snap line as the end point is no longer valid
135 m_snapLineOrigin = aOrigin;
136}
137
139{
140 if( m_snapLineOrigin && aSnapEnd != m_snapLineEnd )
141 {
142 m_snapLineEnd = aSnapEnd;
143
144 if( m_snapLineEnd )
146 else
148
149 updateView();
150 }
151}
152
154{
155 m_snapLineOrigin.reset();
156 m_snapLineEnd.reset();
158 updateView();
159}
160
162{
163 if( m_snapLineOrigin )
164 {
165 if( aAnchorPos.x == m_snapLineOrigin->x || aAnchorPos.y == m_snapLineOrigin->y )
166 {
167 SetSnapLineEnd( aAnchorPos );
168 }
169 else
170 {
171 // Snapped to something that is not the snap line origin, so
172 // this anchor is now the new snap line origin
173 SetSnapLineOrigin( aAnchorPos );
174 }
175 }
176 else
177 {
178 // If there's no snap line, start one
179 m_snapLineOrigin = aAnchorPos;
180 }
181}
182
183std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH>
185{
186 std::vector<CONSTRUCTION_ITEM_BATCH> batches;
187
189 {
190 batches.push_back( *m_persistentConstructionBatch );
191 }
192
194 {
195 batches.push_back( batch );
196 }
197
198 if( m_snapLineOrigin )
199 {
201
202 CONSTRUCTION_ITEM& snapPointItem = batch.emplace_back( CONSTRUCTION_ITEM{
204 nullptr,
205 {},
206 } );
207
208 snapPointItem.Constructions.push_back(
209 LINE{ *m_snapLineOrigin, *m_snapLineOrigin + VECTOR2I( 100000, 0 ) } );
210 snapPointItem.Constructions.push_back(
211 LINE{ *m_snapLineOrigin, *m_snapLineOrigin + VECTOR2I( 0, 100000 ) } );
212
213 batches.push_back( std::move( batch ) );
214 }
215
216 return batches;
217}
218
219
227static bool pointHasEscapedSnapLineX( const VECTOR2I& aCursor, const VECTOR2I& aSnapLineOrigin,
228 int aEscapeRange, EDA_ANGLE aLongRangeEscapeAngle )
229{
230 if( std::abs( aCursor.x - aSnapLineOrigin.x ) < aEscapeRange )
231 {
232 return false;
233 }
234 EDA_ANGLE angle = EDA_ANGLE( aCursor - aSnapLineOrigin ) + EDA_ANGLE( 90, DEGREES_T );
235 return std::abs( angle.Normalize90() ) > aLongRangeEscapeAngle;
236}
237
241static bool pointHasEscapedSnapLineY( const VECTOR2I& aCursor, const VECTOR2I& aSnapLineOrigin,
242 int aEscapeRange, EDA_ANGLE aLongRangeEscapeAngle )
243{
244 if( std::abs( aCursor.y - aSnapLineOrigin.y ) < aEscapeRange )
245 {
246 return false;
247 }
248 EDA_ANGLE angle = EDA_ANGLE( aCursor - aSnapLineOrigin );
249 return std::abs( angle.Normalize90() ) > aLongRangeEscapeAngle;
250}
251
252
254 const VECTOR2I& aNearestGrid,
255 std::optional<int> aDistToNearest,
256 int aSnapRange ) const
257{
258 // return std::nullopt;
259 if( m_snapLineOrigin )
260 {
261 bool snapLine = false;
262 VECTOR2I bestSnapPoint = aNearestGrid;
263
264 // If there's no snap anchor, or it's too far away, prefer the grid
265 const bool gridBetterThanNearest = !aDistToNearest || *aDistToNearest > aSnapRange;
266
267 // The escape range is how far you go before the snap line is de-activated.
268 // Make this a bit more forgiving than the snap range, as you can easily cancel
269 // deliberately with a mouse move.
270 // These are both a bit arbitrary, and can be adjusted as preferred
271 const int escapeRange = 2 * aSnapRange;
272 const EDA_ANGLE longRangeEscapeAngle( 3, DEGREES_T );
273
274 const bool escapedX = pointHasEscapedSnapLineX( aCursor, *m_snapLineOrigin, escapeRange,
275 longRangeEscapeAngle );
276 const bool escapedY = pointHasEscapedSnapLineY( aCursor, *m_snapLineOrigin, escapeRange,
277 longRangeEscapeAngle );
278
281 if( !escapedX && gridBetterThanNearest )
282 {
283 bestSnapPoint.x = m_snapLineOrigin->x;
284 snapLine = true;
285 }
286
287 if( !escapedY && gridBetterThanNearest )
288 {
289 bestSnapPoint.y = m_snapLineOrigin->y;
290 snapLine = true;
291 }
292
293 if( snapLine )
294 {
295 return bestSnapPoint;
296 }
297 }
298
299 return std::nullopt;
300}
KIGFX::CONSTRUCTION_GEOM & m_constructionGeomPreview
void AddConstructionItems(CONSTRUCTION_ITEM_BATCH aBatch, bool aIsPersistent)
Add a batch of construction items to the helper.
void ClearSnapLine()
Clear the snap line origin and end points.
std::deque< CONSTRUCTION_ITEM_BATCH > m_temporaryConstructionBatches
GFX_UPDATE_CALLBACK m_updateCallback
void SetSnappedAnchor(const VECTOR2I &aAnchorPos)
Inform the construction manager that an anchor snap is wanted.
std::vector< CONSTRUCTION_ITEM_BATCH > GetConstructionItems() const
OPT_VECTOR2I GetNearestSnapLinePoint(const VECTOR2I &aCursor, const VECTOR2I &aNearestGrid, std::optional< int > aDistToNearest, int snapRange) const
If the snap line is active, return the best snap point that is closest to the cursor.
std::vector< CONSTRUCTION_ITEM > CONSTRUCTION_ITEM_BATCH
std::optional< CONSTRUCTION_ITEM_BATCH > m_persistentConstructionBatch
void SetSnapLineOrigin(const VECTOR2I &aOrigin)
The snap point is a special point that is located at the last point the cursor snapped to.
void SetSnapLineEnd(const OPT_VECTOR2I &aSnapPoint)
Set the end point of the snap line.
bool InvolvesAllGivenRealItems(const std::vector< EDA_ITEM * > &aItems) const
Check if all 'real' (non-null = constructed) the items in the batch are in the list of items currentl...
CONSTRUCTION_MANAGER(KIGFX::CONSTRUCTION_GEOM &aHelper)
std::set< EDA_ITEM * > m_involvedItems
EDA_ANGLE Normalize90()
Definition: eda_angle.h:249
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
Shows construction geometry for things like line extensions, arc centers, etc.
void AddDrawable(const DRAWABLE &aItem, bool aIsPersistent)
std::variant< SEG, LINE, HALF_LINE, CIRCLE, SHAPE_ARC, VECTOR2I > DRAWABLE
void SetSnapLine(const SEG &aLine)
Definition: line.h:36
Definition: seg.h:42
static bool pointHasEscapedSnapLineX(const VECTOR2I &aCursor, const VECTOR2I &aSnapLineOrigin, int aEscapeRange, EDA_ANGLE aLongRangeEscapeAngle)
Check if the cursor has moved far enough away from the snap line origin to escape snapping in the X d...
static bool pointHasEscapedSnapLineY(const VECTOR2I &aCursor, const VECTOR2I &aSnapLineOrigin, int aEscapeRange, EDA_ANGLE aLongRangeEscapeAngle)
As above, but for the Y direction.
@ DEGREES_T
Definition: eda_angle.h:31
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
std::vector< KIGFX::CONSTRUCTION_GEOM::DRAWABLE > Constructions
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691