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
83std::optional<SEG> KIGEOM::ClipHalfLineToBox( const HALF_LINE& aRay, const BOX2I& aBox )
84{
85 // Do the naive implementation - if this really is done in a tight loop,
86 // the Cohen-Sutherland implementation in ClipLine could be faster, but
87 // needs to be adapted to work with half-lines.
88 const std::array<SEG, 4> boxSegs = KIGEOM::BoxToSegs( aBox );
89
90 std::optional<VECTOR2I> ptA, ptB;
91
92 for( const SEG& boxSeg : boxSegs )
93 {
94 OPT_VECTOR2I intersection = aRay.Intersect( boxSeg );
95
96 if( !intersection )
97 {
98 continue;
99 }
100
101 // Init the first point or eat it if it's the same
102 if( !ptA || *intersection == *ptA )
103 {
104 ptA = *intersection;
105 }
106 else
107 {
108 ptB = *intersection;
109 }
110 }
111
112 // If we have exactly two intersections, the ray crossed twice
113 // so take the segment between the two points
114 if( ptA && ptB )
115 {
116 return SEG( *ptA, *ptB );
117 }
118
119 // It only crosses once, so the start is in the box. Take the segment from
120 // the start point to the intersection
121 if( ptA && *ptA != aRay.GetStart() )
122 {
123 return SEG( aRay.GetStart(), *ptA );
124 }
125
126 // It didn't cross at all
127 return std::nullopt;
128}
129
130
131std::optional<SEG> KIGEOM::ClipLineToBox( const LINE& aLine, const BOX2I& aBox )
132{
133 // As above, maybe can be optimised?
134 const std::array<SEG, 4> boxSegs = KIGEOM::BoxToSegs( aBox );
135
136 std::optional<VECTOR2I> ptA, ptB;
137
138 for( const SEG& boxSeg : boxSegs )
139 {
140 OPT_VECTOR2I intersection = aLine.Intersect( boxSeg );
141
142 // Reject intersections that are not on the actual box boundary
143 if( intersection && boxSeg.Contains( *intersection ) )
144 {
145 // Init the first point or eat it if it's the same
146 if( !ptA || *intersection == *ptA )
147 {
148 ptA = *intersection;
149 }
150 else
151 {
152 ptB = *intersection;
153 }
154 }
155 }
156
157 // If we have exactly two intersections, we have a segment
158 // (zero is no intersection, and one is a just crossing a corner exactly)
159 if( ptA && ptB )
160 {
161 return SEG( *ptA, *ptB );
162 }
163
164 return std::nullopt;
165}
166
167
169{
170 switch( aDir )
171 {
172 case DIRECTION_45::NW:
173 return SHAPE_ARC{
174 aCenter,
175 aCenter + VECTOR2I( -aRadius, 0 ),
176 ANGLE_90,
177 };
178 case DIRECTION_45::NE:
179 return SHAPE_ARC{
180 aCenter,
181 aCenter + VECTOR2I( 0, -aRadius ),
182 ANGLE_90,
183 };
184 case DIRECTION_45::SW:
185 return SHAPE_ARC{
186 aCenter,
187 aCenter + VECTOR2I( 0, aRadius ),
188 ANGLE_90,
189 };
190 case DIRECTION_45::SE:
191 return SHAPE_ARC{
192 aCenter,
193 aCenter + VECTOR2I( aRadius, 0 ),
194 ANGLE_90,
195 };
196 default: wxFAIL_MSG( "Invalid direction" ); return SHAPE_ARC();
197 }
198}
199
200
201SHAPE_ARC KIGEOM::MakeArcCw180( const VECTOR2I& aCenter, int aRadius,
203{
204 switch( aDir )
205 {
206 case DIRECTION_45::N:
207 return SHAPE_ARC{
208 aCenter,
209 aCenter + VECTOR2I( -aRadius, 0 ),
210 ANGLE_180,
211 };
212 case DIRECTION_45::E:
213 return SHAPE_ARC{
214 aCenter,
215 aCenter + VECTOR2I( 0, -aRadius ),
216 ANGLE_180,
217 };
218 case DIRECTION_45::S:
219 return SHAPE_ARC{
220 aCenter,
221 aCenter + VECTOR2I( aRadius, 0 ),
222 ANGLE_180,
223 };
224 case DIRECTION_45::W:
225 return SHAPE_ARC{
226 aCenter,
227 aCenter + VECTOR2I( 0, aRadius ),
228 ANGLE_180,
229 };
230 default: wxFAIL_MSG( "Invalid direction" );
231 }
232
233 return SHAPE_ARC();
234}
235
236
238{
239 const VECTOR2I nw = aRect.GetPosition();
240 switch( aDir )
241 {
242 // clang-format off
243 case DIRECTION_45::N: return nw + VECTOR2I( aRect.GetWidth() / 2, 0 );
244 case DIRECTION_45::E: return nw + VECTOR2I( aRect.GetWidth(), aRect.GetHeight() / 2 );
245 case DIRECTION_45::S: return nw + VECTOR2I( aRect.GetWidth() / 2, aRect.GetHeight() );
246 case DIRECTION_45::W: return nw + VECTOR2I( 0, aRect.GetHeight() / 2 );
247 case DIRECTION_45::NW: return nw;
248 case DIRECTION_45::NE: return nw + VECTOR2I( aRect.GetWidth(), 0 );
249 case DIRECTION_45::SW: return nw + VECTOR2I( 0, aRect.GetHeight() );
250 case DIRECTION_45::SE: return nw + VECTOR2I( aRect.GetWidth(), aRect.GetHeight() );
251 default: wxFAIL_MSG( "Invalid direction" );
252 // clang-format on
253 }
254 return VECTOR2I();
255}
256
257
258std::vector<TYPED_POINT2I> KIGEOM::GetCircleKeyPoints( const CIRCLE& aCircle, bool aIncludeCenter )
259{
260 std::vector<TYPED_POINT2I> pts;
261
262 if( aIncludeCenter )
263 {
264 pts.emplace_back( VECTOR2I{ 0, 0 }, POINT_TYPE::PT_CENTER );
265 }
266
267 pts.emplace_back( VECTOR2I{ 0, aCircle.Radius }, POINT_TYPE::PT_QUADRANT );
268 pts.emplace_back( VECTOR2I{ aCircle.Radius, 0 }, POINT_TYPE::PT_QUADRANT );
269 pts.emplace_back( VECTOR2I{ 0, -aCircle.Radius }, POINT_TYPE::PT_QUADRANT );
270 pts.emplace_back( VECTOR2I{ -aCircle.Radius, 0 }, POINT_TYPE::PT_QUADRANT );
271
272 // Shift the points to the circle center
273 for( TYPED_POINT2I& pt : pts )
274 {
275 pt.m_point += aCircle.Center;
276 }
277
278 return pts;
279}
constexpr coord_type GetLeft() const
Definition: box2.h:223
constexpr coord_type GetRight() const
Definition: box2.h:212
constexpr coord_type GetTop() const
Definition: box2.h:224
constexpr coord_type GetBottom() const
Definition: box2.h:217
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
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)
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
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.
Definition: shape_utils.cpp:83
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_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...
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