KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_constraints.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) 2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
23#include "tool/edit_points.h"
24
25#include <geometry/seg.h>
26#include <trigo.h>
27
29#include <math/vector2d.h>
30#include <math/util.h>
31
32
33void EC_VERTICAL::Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid )
34{
35 VECTOR2I point = aHandle.GetPosition();
36
37 if( aHandle.GetGridConstraint() == SNAP_TO_GRID )
38 point = aGrid.AlignGrid( point );
39
40 point.x = m_constrainer.GetPosition().x;
41 aHandle.SetPosition( point );
42}
43
44
45void EC_HORIZONTAL::Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid )
46{
47 VECTOR2I point = aHandle.GetPosition();
48
49 if( aHandle.GetGridConstraint() == SNAP_TO_GRID )
50 point = aGrid.AlignGrid( point );
51
52 point.y = m_constrainer.GetPosition().y;
53 aHandle.SetPosition( point );
54}
55
56
57void EC_45DEGREE::Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid )
58{
59 VECTOR2I lineVector( aHandle.GetPosition() - m_constrainer.GetPosition() );
60 VECTOR2I newLineVector = GetVectorSnapped45( lineVector );
61
62 if( aHandle.GetGridConstraint() == SNAP_TO_GRID
63 && ( newLineVector.x == 0 || newLineVector.y == 0 ) )
64 {
65 VECTOR2I snap = aGrid.AlignGrid( m_constrainer.GetPosition() + newLineVector );
66
67 if( newLineVector.x == 0 )
68 aHandle.SetPosition( VECTOR2I( m_constrainer.GetPosition().x, snap.y ) );
69 else
70 aHandle.SetPosition( VECTOR2I( snap.x, m_constrainer.GetPosition().y ) );
71 }
72 else
73 {
74 aHandle.SetPosition( m_constrainer.GetPosition() + newLineVector );
75 }
76}
77
78
79void EC_90DEGREE::Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid )
80{
81 VECTOR2I lineVector( aHandle.GetPosition() - m_constrainer.GetPosition() );
82 VECTOR2I newLineVector = GetVectorSnapped90( lineVector );
83
84 if( aHandle.GetGridConstraint() == SNAP_TO_GRID )
85 {
86 VECTOR2I snap = aGrid.AlignGrid( m_constrainer.GetPosition() + newLineVector );
87
88 if( newLineVector.x == 0 )
89 aHandle.SetPosition( VECTOR2I( m_constrainer.GetPosition().x, snap.y ) );
90 else
91 aHandle.SetPosition( VECTOR2I( snap.x, m_constrainer.GetPosition().y ) );
92 }
93 else
94 {
95 aHandle.SetPosition( m_constrainer.GetPosition() + newLineVector );
96 }
97}
98
99EC_LINE::EC_LINE( EDIT_POINT& aConstrained, const EDIT_POINT& aConstrainer ) :
100 EDIT_CONSTRAINT<EDIT_POINT>( aConstrained ),
101 m_constrainer( aConstrainer )
102{
103 m_line = m_constrained.GetPosition() - m_constrainer.GetPosition();
104}
105
106
107void EC_LINE::Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid )
108{
109 SEG main( m_constrainer.GetPosition(), m_constrainer.GetPosition() + m_line );
110
111 if( aHandle.GetGridConstraint() == SNAP_TO_GRID
112 && ( m_line.x == 0 || m_line.y == 0 ) )
113 {
114 VECTOR2I snappedHandle = aGrid.AlignGrid( aHandle.GetPosition() );
115
116 if( m_line.x == 0 )
117 aHandle.SetPosition( VECTOR2I( aHandle.GetPosition().x, snappedHandle.y ) );
118 else
119 aHandle.SetPosition( VECTOR2I( snappedHandle.x, aHandle.GetPosition().y ) );
120 }
121
122 SEG projection( aHandle.GetPosition(), aHandle.GetPosition() + m_line.Perpendicular() );
123
124 if( OPT_VECTOR2I intersect = projection.IntersectLines( main ) )
125 aHandle.SetPosition( *intersect );
126}
127
128
129void EC_CIRCLE::Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid )
130{
131 VECTOR2I centerToEnd = m_end.GetPosition() - m_center.GetPosition();
132 VECTOR2I centerToPoint = aHandle.GetPosition() - m_center.GetPosition();
133
134 int radius = centerToEnd.EuclideanNorm();
135 EDA_ANGLE angle( centerToPoint );
136
137 VECTOR2I newLine( radius, 0 );
138 RotatePoint( newLine, -angle );
139
140 aHandle.SetPosition( m_center.GetPosition() + newLine );
141}
142
143
145 POLYGON_LINE_MODE aMode ) :
146 EDIT_CONSTRAINT<EDIT_LINE>( aLine ),
147 m_mode( aMode ),
148 m_colinearConstraint( nullptr ),
149 m_editPoints( aPoints ),
150 m_prevOrigin( aPoints.Previous( aLine.GetOrigin(), false ) ),
151 m_nextEnd( aPoints.Next( aLine.GetEnd(), false ) )
152{
153 EDIT_POINT& origin = aLine.GetOrigin();
154 EDIT_POINT& end = aLine.GetEnd();
155
156 // Constraints for segments adjacent to the dragged one
157 m_originSideConstraint = std::make_unique<EC_LINE>( origin, *m_prevOrigin );
158 m_endSideConstraint = std::make_unique<EC_LINE>( end, *m_nextEnd );
159
160 // Store the current vector and center of the line
161 m_draggedVector = end.GetPosition() - origin.GetPosition();
163
164 // Perpendicular direction for constraining movement
165 m_perpVector = m_draggedVector.Perpendicular();
166
167 // Half-length for fixed-length mode
168 m_halfLength = m_draggedVector.EuclideanNorm() / 2.0;
169
170 // Check for colinearity
171 SEG originSide( origin.GetPosition(), m_prevOrigin->GetPosition() );
172 SEG endSide( end.GetPosition(), m_nextEnd->GetPosition() );
173 SEG dragged( origin.GetPosition(), end.GetPosition() );
174
175 const int alignAngle = 10;
176
177 m_originCollinear = dragged.Angle( originSide ).AsDegrees() < alignAngle;
178 m_endCollinear = dragged.Angle( endSide ).AsDegrees() < alignAngle;
179
182 else if( m_endCollinear )
184
185 if( OPT_VECTOR2I intersect = originSide.IntersectLines( endSide ) )
187 else
189
191}
192
193
195{
196 // m_colinearConstraint should not be freed, it is a pointer to one of the above
197}
198
199
200void EC_CONVERGING::Apply( EDIT_LINE& aHandle, const GRID_HELPER& aGrid )
201{
202 VECTOR2I handlePos = aHandle.GetPosition();
203
204 // Project the handle position onto the perpendicular line through the original center.
205 // This ensures the line always moves perpendicular to itself.
207 SEG toHandle( handlePos, handlePos + m_draggedVector );
208 VECTOR2I newCenter;
209
210 if( OPT_VECTOR2I intersect = perpLine.IntersectLines( toHandle ) )
211 newCenter = *intersect;
212 else
213 newCenter = handlePos;
214
215 // In converging mode, don't allow movement past the convergence point
217 {
219 VECTOR2I centerToNew = newCenter - m_originalCenter;
220
221 // Check if we've crossed past the convergence point
222 if( centerToConv.Dot( m_perpVector ) != 0 )
223 {
224 double t = double( centerToNew.Dot( m_perpVector ) )
225 / double( centerToConv.Dot( m_perpVector ) );
226
227 if( t > 1.0 )
228 newCenter = m_convergencePoint;
229 }
230 }
231
232 aHandle.SetPosition( newCenter );
233
235 applyFixedLength( aHandle );
236 else
237 applyConverging( aHandle );
238}
239
240
242{
243 EDIT_POINT& origin = aHandle.GetOrigin();
244 EDIT_POINT& end = aHandle.GetEnd();
245
247 {
249 {
250 GRID_HELPER dummyGrid;
251 m_colinearConstraint->Apply( origin, dummyGrid );
252 m_colinearConstraint->Apply( end, dummyGrid );
253 }
254
255 return;
256 }
257
258 // The dragged segment at new position (parallel to original)
259 VECTOR2I newCenter = aHandle.GetPosition();
260 SEG dragged( newCenter - m_draggedVector / 2, newCenter + m_draggedVector / 2 );
261
262 // Get the fixed directions of adjacent segments from the stored EC_LINE constraints.
263 // These directions were captured at drag start and don't change.
264 EC_LINE* originLine = static_cast<EC_LINE*>( m_originSideConstraint.get() );
265 EC_LINE* endLine = static_cast<EC_LINE*>( m_endSideConstraint.get() );
266
267 VECTOR2I originDir = originLine->GetLineVector();
268 VECTOR2I endDir = endLine->GetLineVector();
269
270 // Adjacent segments use fixed directions from the anchor points
271 SEG originSide( m_prevOrigin->GetPosition(),
272 m_prevOrigin->GetPosition() + originDir );
273 SEG endSide( m_nextEnd->GetPosition(),
274 m_nextEnd->GetPosition() + endDir );
275
276 // Find intersection of dragged line with origin side
277 if( OPT_VECTOR2I originIntersect = dragged.IntersectLines( originSide ) )
278 origin.SetPosition( *originIntersect );
279
280 // Find intersection of dragged line with end side
281 if( OPT_VECTOR2I endIntersect = dragged.IntersectLines( endSide ) )
282 end.SetPosition( *endIntersect );
283
284 // Check if adjacent segments would intersect (self-intersecting polygon)
285 originSide = SEG( origin.GetPosition(), m_prevOrigin->GetPosition() );
286 endSide = SEG( end.GetPosition(), m_nextEnd->GetPosition() );
287
288 if( OPT_VECTOR2I originEndIntersect = endSide.Intersect( originSide ) )
289 {
290 if( m_editPoints.LinesSize() > 3 )
291 {
292 origin.SetPosition( *originEndIntersect );
293 end.SetPosition( *originEndIntersect );
294 }
295 }
296}
297
298
300{
301 EDIT_POINT& origin = aHandle.GetOrigin();
302 EDIT_POINT& end = aHandle.GetEnd();
303
304 VECTOR2I newCenter = aHandle.GetPosition();
305
306 // Keep the line at its original length, centered on the new position
307 VECTOR2D unitDir = VECTOR2D( m_draggedVector );
308
309 if( unitDir.EuclideanNorm() > 0 )
310 unitDir = unitDir / unitDir.EuclideanNorm();
311
312 VECTOR2I newOrigin = newCenter - KiROUND( unitDir * m_halfLength );
313 VECTOR2I newEnd = newCenter + KiROUND( unitDir * m_halfLength );
314
315 origin.SetPosition( newOrigin );
316 end.SetPosition( newEnd );
317}
318
319
321 EDIT_CONSTRAINT<EDIT_LINE>( aLine )
322{
323 m_mid = aLine.GetPosition();
324 m_line = ( aLine.GetEnd().GetPosition() - aLine.GetOrigin().GetPosition() ).Perpendicular();
325}
326
327
328void EC_PERPLINE::Apply( EDIT_LINE& aHandle, const GRID_HELPER& aGrid )
329{
330 SEG main( m_mid, m_mid + m_line );
331 SEG projection( aHandle.GetPosition(), aHandle.GetPosition() + m_line.Perpendicular() );
332
333 if( OPT_VECTOR2I intersect = projection.IntersectLines( main ) )
334 aHandle.SetPosition( *intersect );
335
336 VECTOR2D delta = aHandle.GetEnd().GetPosition() - aHandle.GetOrigin().GetPosition();
337
338 aHandle.GetOrigin().SetPosition( aHandle.GetOrigin().GetPosition() );
339 aHandle.GetEnd().SetPosition( aHandle.GetOrigin().GetPosition() + delta );
340}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
const EDIT_POINT & m_constrainer
Point that imposes the constraint.
virtual void Apply(EDIT_POINT &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
const EDIT_POINT & m_constrainer
Point that imposes the constraint.
virtual void Apply(EDIT_POINT &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
const EDIT_POINT & m_end
Point that imposes the constraint (decides on the radius of the circle).
const EDIT_POINT & m_center
Point that imposes the constraint (center of the circle).
virtual void Apply(EDIT_POINT &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
std::unique_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_endSideConstraint
Constraint for end side segment.
double m_halfLength
Original half-length of the line (for fixed-length mode)
void applyConverging(EDIT_LINE &aHandle)
Apply converging mode: find intersections with adjacent lines.
std::unique_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_originSideConstraint
Constraint for origin side segment.
POLYGON_LINE_MODE m_mode
Constraint mode.
EC_CONVERGING(EDIT_LINE &aLine, EDIT_POINTS &aPoints, POLYGON_LINE_MODE aMode=POLYGON_LINE_MODE::CONVERGING)
EDIT_CONSTRAINT< EDIT_POINT > * m_colinearConstraint
Additional constraint, applied when at least two points are collinear.
VECTOR2I m_midVector
Vector from the convergence point to the mid-line point.
VECTOR2I m_draggedVector
Vector that represents the initial direction of the dragged segment.
EDIT_POINT * m_nextEnd
EDIT_POINT * m_prevOrigin
Previous and next points to keep drag endpoints fixed.
VECTOR2I m_originalCenter
Original center position of the line.
bool m_originCollinear
Flags to indicate when dragged and neighbouring lines are (almost) collinear.
VECTOR2I m_perpVector
Perpendicular direction to the dragged segment (for constraining movement)
EDIT_POINTS & m_editPoints
EDIT_POINTS instance that stores currently modified lines.
void applyFixedLength(EDIT_LINE &aHandle)
Apply fixed-length mode: maintain line length, adjust adjacent line angles.
virtual void Apply(EDIT_LINE &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
VECTOR2I m_convergencePoint
Original convergence point of adjacent segments.
const EDIT_POINT & m_constrainer
Point that imposes the constraint.
virtual void Apply(EDIT_POINT &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a line (determined by 2 points).
VECTOR2I GetLineVector() const
const EDIT_POINT & m_constrainer
Point that imposes the constraint.
VECTOR2I m_line
Vector representing the constraining line.
virtual void Apply(EDIT_POINT &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
EC_LINE(EDIT_POINT &aConstrained, const EDIT_POINT &aConstrainer)
EC_PERPLINE(EDIT_LINE &aLine)
virtual void Apply(EDIT_LINE &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
virtual void Apply(EDIT_POINT &aHandle, const GRID_HELPER &aGrid) override
Correct coordinates of the constrained edit handle.
const EDIT_POINT & m_constrainer
Point that imposes the constraint.
double AsDegrees() const
Definition eda_angle.h:116
EDIT_CONSTRAINT(EDIT_POINT &aConstrained)
Represent a line connecting two EDIT_POINTs.
virtual void SetPosition(const VECTOR2I &aPosition) override
Return coordinates of an EDIT_POINT.
EDIT_POINT & GetEnd()
Return the end EDIT_POINT.
EDIT_POINT & GetOrigin()
Return the origin EDIT_POINT.
virtual VECTOR2I GetPosition() const override
Return coordinates of an EDIT_POINT.
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
Represent a single point that can be used for modifying items.
Definition edit_points.h:44
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition edit_points.h:68
GRID_CONSTRAINT_TYPE GetGridConstraint() const
virtual VECTOR2I AlignGrid(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
Definition grid_helper.h:87
Definition seg.h:38
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition seg.cpp:442
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:216
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:107
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:542
POLYGON_LINE_MODE
Mode for polygon line edge constraints.
@ CONVERGING
Adjacent lines converge/diverge, dragged line length changes.
@ FIXED_LENGTH
Dragged line maintains its length, adjacent lines adjust angles.
@ SNAP_TO_GRID
a few functions useful in geometry calculations.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
VECTOR2< T > GetVectorSnapped90(const VECTOR2< T > &aVec)
Snap a vector onto the nearest horizontal or vertical line.
static bool intersect(const SEGMENT_WITH_NORMALS &aSeg, const SFVEC2F &aStart, const SFVEC2F &aEnd)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
int radius
VECTOR2I end
int delta
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:225
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682