KiCad PCB EDA Suite
Loading...
Searching...
No Matches
shape_utils.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#include <geometry/circle.h>
27#include <geometry/seg.h>
28#include <geometry/half_line.h>
29#include <geometry/line.h>
31#include <geometry/shape_rect.h>
32
33
35{
36 if( LexicographicalCompare( aSeg.A, aSeg.B ) <= 0 )
37 {
38 return aSeg;
39 }
40 return aSeg.Reversed();
41}
42
43
44const VECTOR2I& KIGEOM::GetOtherEnd( const SEG& aSeg, const VECTOR2I& aPoint )
45{
46 return ( aSeg.A == aPoint ) ? aSeg.B : aSeg.A;
47}
48
49
50OPT_VECTOR2I KIGEOM::GetSharedEndpoint( const SEG& aSegA, const SEG& aSegB )
51{
52 if( aSegA.A == aSegB.A || aSegA.A == aSegB.B )
53 {
54 return aSegA.A;
55 }
56 else if( aSegA.B == aSegB.A || aSegA.B == aSegB.B )
57 {
58 return aSegA.B;
59 }
60
61 return std::nullopt;
62}
63
64
65std::array<SEG, 4> KIGEOM::BoxToSegs( const BOX2I& aBox )
66{
67 const std::array<VECTOR2I, 4> corners = {
68 VECTOR2I{ aBox.GetLeft(), aBox.GetTop() },
69 VECTOR2I{ aBox.GetRight(), aBox.GetTop() },
70 VECTOR2I{ aBox.GetRight(), aBox.GetBottom() },
71 VECTOR2I{ aBox.GetLeft(), aBox.GetBottom() },
72 };
73
74 return {
75 SEG{ corners[0], corners[1] },
76 SEG{ corners[1], corners[2] },
77 SEG{ corners[2], corners[3] },
78 SEG{ corners[3], corners[0] },
79 };
80}
81
82
83void KIGEOM::CollectBoxCorners( const BOX2I& aBox, std::vector<VECTOR2I>& aCorners )
84{
85 aCorners.push_back( { aBox.GetLeft(), aBox.GetTop() } );
86 aCorners.push_back( { aBox.GetRight(), aBox.GetTop() } );
87 aCorners.push_back( { aBox.GetRight(), aBox.GetBottom() } );
88 aCorners.push_back( { aBox.GetLeft(), aBox.GetBottom() } );
89}
90
91
92std::vector<SEG> KIGEOM::GetSegsInDirection( const BOX2I& aBox, DIRECTION_45::Directions aDir )
93{
94 // clang-format off
95 switch( aDir )
96 {
98 return { SEG{ { aBox.GetLeft(), aBox.GetTop() },
99 { aBox.GetRight(), aBox.GetTop() } } };
101 return { SEG{ { aBox.GetRight(), aBox.GetTop() },
102 { aBox.GetRight(), aBox.GetBottom() } } };
104 return { SEG{ { aBox.GetLeft(), aBox.GetBottom() },
105 { aBox.GetRight(), aBox.GetBottom() } } };
107 return { SEG{ { aBox.GetLeft(), aBox.GetTop() },
108 { aBox.GetLeft(), aBox.GetBottom() } } };
110 return { SEG{ { aBox.GetLeft(), aBox.GetTop() },
111 { aBox.GetRight(), aBox.GetTop() } },
112 SEG{ { aBox.GetRight(), aBox.GetTop() },
113 { aBox.GetRight(), aBox.GetBottom() } } };
115 return { SEG{ { aBox.GetLeft(), aBox.GetBottom() },
116 { aBox.GetRight(), aBox.GetBottom() } },
117 SEG{ { aBox.GetRight(), aBox.GetTop() },
118 { aBox.GetRight(), aBox.GetBottom() } } };
120 return { SEG{ { aBox.GetLeft(), aBox.GetBottom() },
121 { aBox.GetRight(), aBox.GetBottom() } },
122 SEG{ { aBox.GetLeft(), aBox.GetTop() },
123 { aBox.GetLeft(), aBox.GetBottom() } } };
125 return { SEG{ { aBox.GetLeft(), aBox.GetTop() },
126 { aBox.GetRight(), aBox.GetTop() } },
127 SEG{ { aBox.GetLeft(), aBox.GetTop() },
128 { aBox.GetLeft(), aBox.GetBottom() } } };
131 }
132 // clang-format on
133
134 wxASSERT( false );
135 return {};
136};
137
138
139std::optional<SEG> KIGEOM::ClipHalfLineToBox( const HALF_LINE& aRay, const BOX2I& aBox )
140{
141 // Do the naive implementation - if this really is done in a tight loop,
142 // the Cohen-Sutherland implementation in ClipLine could be faster, but
143 // needs to be adapted to work with half-lines.
144 const std::array<SEG, 4> boxSegs = KIGEOM::BoxToSegs( aBox );
145
146 std::optional<VECTOR2I> ptA, ptB;
147
148 for( const SEG& boxSeg : boxSegs )
149 {
150 OPT_VECTOR2I intersection = aRay.Intersect( boxSeg );
151
152 if( !intersection )
153 {
154 continue;
155 }
156
157 // Init the first point or eat it if it's the same
158 if( !ptA || *intersection == *ptA )
159 {
160 ptA = *intersection;
161 }
162 else
163 {
164 ptB = *intersection;
165 }
166 }
167
168 // If we have exactly two intersections, the ray crossed twice
169 // so take the segment between the two points
170 if( ptA && ptB )
171 {
172 return SEG( *ptA, *ptB );
173 }
174
175 // It only crosses once, so the start is in the box. Take the segment from
176 // the start point to the intersection
177 if( ptA && *ptA != aRay.GetStart() )
178 {
179 return SEG( aRay.GetStart(), *ptA );
180 }
181
182 // It didn't cross at all
183 return std::nullopt;
184}
185
186
187std::optional<SEG> KIGEOM::ClipLineToBox( const LINE& aLine, const BOX2I& aBox )
188{
189 // As above, maybe can be optimised?
190 const std::array<SEG, 4> boxSegs = KIGEOM::BoxToSegs( aBox );
191
192 std::optional<VECTOR2I> ptA, ptB;
193
194 for( const SEG& boxSeg : boxSegs )
195 {
196 OPT_VECTOR2I intersection = aLine.Intersect( boxSeg );
197
198 // Reject intersections that are not on the actual box boundary
199 if( intersection && boxSeg.Contains( *intersection ) )
200 {
201 // Init the first point or eat it if it's the same
202 if( !ptA || *intersection == *ptA )
203 {
204 ptA = *intersection;
205 }
206 else
207 {
208 ptB = *intersection;
209 }
210 }
211 }
212
213 // If we have exactly two intersections, we have a segment
214 // (zero is no intersection, and one is a just crossing a corner exactly)
215 if( ptA && ptB )
216 {
217 return SEG( *ptA, *ptB );
218 }
219
220 return std::nullopt;
221}
222
223
225{
226 switch( aDir )
227 {
228 case DIRECTION_45::NW:
229 return SHAPE_ARC{
230 aCenter,
231 aCenter + VECTOR2I( -aRadius, 0 ),
232 ANGLE_90,
233 };
234 case DIRECTION_45::NE:
235 return SHAPE_ARC{
236 aCenter,
237 aCenter + VECTOR2I( 0, -aRadius ),
238 ANGLE_90,
239 };
240 case DIRECTION_45::SW:
241 return SHAPE_ARC{
242 aCenter,
243 aCenter + VECTOR2I( 0, aRadius ),
244 ANGLE_90,
245 };
246 case DIRECTION_45::SE:
247 return SHAPE_ARC{
248 aCenter,
249 aCenter + VECTOR2I( aRadius, 0 ),
250 ANGLE_90,
251 };
252 default: wxFAIL_MSG( "Invalid direction" ); return SHAPE_ARC();
253 }
254}
255
256
257SHAPE_ARC KIGEOM::MakeArcCw180( const VECTOR2I& aCenter, int aRadius,
259{
260 switch( aDir )
261 {
262 case DIRECTION_45::N:
263 return SHAPE_ARC{
264 aCenter,
265 aCenter + VECTOR2I( -aRadius, 0 ),
266 ANGLE_180,
267 };
268 case DIRECTION_45::E:
269 return SHAPE_ARC{
270 aCenter,
271 aCenter + VECTOR2I( 0, -aRadius ),
272 ANGLE_180,
273 };
274 case DIRECTION_45::S:
275 return SHAPE_ARC{
276 aCenter,
277 aCenter + VECTOR2I( aRadius, 0 ),
278 ANGLE_180,
279 };
280 case DIRECTION_45::W:
281 return SHAPE_ARC{
282 aCenter,
283 aCenter + VECTOR2I( 0, aRadius ),
284 ANGLE_180,
285 };
286 default: wxFAIL_MSG( "Invalid direction" );
287 }
288
289 return SHAPE_ARC();
290}
291
292
294{
295 const VECTOR2I nw = aRect.GetPosition();
296 switch( aDir )
297 {
298 // clang-format off
299 case DIRECTION_45::N:
300 return nw + VECTOR2I( aRect.GetWidth() / 2, -aOutset );
301 case DIRECTION_45::E:
302 return nw + VECTOR2I( aRect.GetWidth() + aOutset, aRect.GetHeight() / 2 );
303 case DIRECTION_45::S:
304 return nw + VECTOR2I( aRect.GetWidth() / 2, aRect.GetHeight() + aOutset );
305 case DIRECTION_45::W:
306 return nw + VECTOR2I( -aOutset, aRect.GetHeight() / 2 );
307 case DIRECTION_45::NW:
308 return nw + VECTOR2I( -aOutset, -aOutset );
309 case DIRECTION_45::NE:
310 return nw + VECTOR2I( aRect.GetWidth() + aOutset, -aOutset );
311 case DIRECTION_45::SW:
312 return nw + VECTOR2I( -aOutset, aRect.GetHeight() + aOutset );
313 case DIRECTION_45::SE:
314 return nw + VECTOR2I( aRect.GetWidth() + aOutset, aRect.GetHeight() + aOutset );
315 default:
316 wxFAIL_MSG( "Invalid direction" );
317 // clang-format on
318 }
319 return VECTOR2I();
320}
321
322
323std::vector<TYPED_POINT2I> KIGEOM::GetCircleKeyPoints( const CIRCLE& aCircle, bool aIncludeCenter )
324{
325 std::vector<TYPED_POINT2I> pts;
326
327 if( aIncludeCenter )
328 {
329 pts.emplace_back( VECTOR2I{ 0, 0 }, POINT_TYPE::PT_CENTER );
330 }
331
332 pts.emplace_back( VECTOR2I{ 0, aCircle.Radius }, POINT_TYPE::PT_QUADRANT );
333 pts.emplace_back( VECTOR2I{ aCircle.Radius, 0 }, POINT_TYPE::PT_QUADRANT );
334 pts.emplace_back( VECTOR2I{ 0, -aCircle.Radius }, POINT_TYPE::PT_QUADRANT );
335 pts.emplace_back( VECTOR2I{ -aCircle.Radius, 0 }, POINT_TYPE::PT_QUADRANT );
336
337 // Shift the points to the circle center
338 for( TYPED_POINT2I& pt : pts )
339 {
340 pt.m_point += aCircle.Center;
341 }
342
343 return pts;
344}
345
346
348{
349 SHAPE_LINE_CHAIN raOutline;
350
351 const auto handleSegment = [&]( const SEG& aSeg )
352 {
353 const VECTOR2I p0( aSeg.A.x, aSeg.B.y );
354 const VECTOR2I p1( aSeg.B.x, aSeg.A.y );
355
356 raOutline.Append( aSeg.A );
357 if( !aPoly.PointInside( p0 ) )
358 raOutline.Append( p0 );
359 else
360 raOutline.Append( p1 );
361 };
362
363 for( int i = 0; i < aPoly.SegmentCount(); i++ )
364 {
365 handleSegment( aPoly.CSegment( i ) );
366 }
367
368 // Manually handle the last segment if not closed
369 if( !aPoly.IsClosed() )
370 {
371 handleSegment( SEG( aPoly.CPoint( -1 ), aPoly.CPoint( 0 ) ) );
372 }
373
374 raOutline.SetClosed( true );
375 raOutline.Simplify();
376
377 return raOutline;
378}
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:130
int Radius
Public to make access simpler.
Definition: circle.h:129
Directions
Available directions, there are 8 of them, as on a rectilinear map (north = up) + an extra undefined ...
Definition: direction45.h:49
OPT_VECTOR2I Intersect(const SEG &aSeg) const
Definition: half_line.cpp:57
const VECTOR2I & GetStart() const
Get the start point of the ray.
Definition: half_line.h:57
Definition: line.h:36
OPT_VECTOR2I Intersect(const SEG &aOther) const
Definition: line.cpp:22
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
SEG Reversed() const
Returns the center point of the line.
Definition: seg.h:363
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Simplify(int aMaxError=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
const VECTOR2I & GetPosition() const
Definition: shape_rect.h:160
int GetWidth() const
Definition: shape_rect.h:176
int GetHeight() const
Definition: shape_rect.h:184
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
VECTOR2I GetPoint(const SHAPE_RECT &aRect, DIRECTION_45::Directions aDir, int aOutset=0)
Get the point on a rectangle that corresponds to a given direction.
std::vector< TYPED_POINT2I > GetCircleKeyPoints(const CIRCLE &aCircle, bool aIncludeCenter)
Get key points of an CIRCLE.
OPT_VECTOR2I GetSharedEndpoint(const SEG &aSegA, const SEG &aSegB)
Get the shared endpoint of two segments, if it exists, or std::nullopt if the segments are not connec...
Definition: shape_utils.cpp:50
std::vector< SEG > GetSegsInDirection(const BOX2I &aBox, DIRECTION_45::Directions aDir)
Get the segments of a box that are in the given direction.
Definition: shape_utils.cpp:92
SEG NormalisedSeg(const SEG &aSeg)
Returns a SEG such that the start point is smaller or equal in x and y compared to the end point.
Definition: shape_utils.cpp:34
std::array< SEG, 4 > BoxToSegs(const BOX2I &aBox)
Decompose a BOX2 into four segments.
Definition: shape_utils.cpp:65
std::optional< SEG > ClipHalfLineToBox(const HALF_LINE &aRay, const BOX2I &aBox)
Get the segment of a half-line that is inside a box, if any.
std::optional< SEG > ClipLineToBox(const LINE &aLine, const BOX2I &aBox)
Get the segment of a line that is inside a box, if any.
SHAPE_ARC MakeArcCw180(const VECTOR2I &aCenter, int aRadius, DIRECTION_45::Directions aDir)
Get a SHAPE_ARC representing a 180-degree arc in the clockwise direction with the midpoint in the giv...
SHAPE_LINE_CHAIN RectifyPolygon(const SHAPE_LINE_CHAIN &aPoly)
SHAPE_ARC MakeArcCw90(const VECTOR2I &aCenter, int aRadius, DIRECTION_45::Directions aDir)
Get a SHAPE_ARC representing a 90-degree arc in the clockwise direction with the midpoint in the give...
void CollectBoxCorners(const BOX2I &aBox, std::vector< VECTOR2I > &aCorners)
Add the 4 corners of a BOX2I to a vector.
Definition: shape_utils.cpp:83
const VECTOR2I & GetOtherEnd(const SEG &aSeg, const VECTOR2I &aPoint)
Get the end point of the segment that is not the given point.
Definition: shape_utils.cpp:44
@ PT_CENTER
The point is the center of something.
Definition: point_types.h:46
@ PT_QUADRANT
The point is on a quadrant of a circle (N, E, S, W points).
Definition: point_types.h:58
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Utility functions for working with shapes.
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691
constexpr int LexicographicalCompare(const VECTOR2< T > &aA, const VECTOR2< T > &aB)
Definition: vector2d.h:640