KiCad PCB EDA Suite
Loading...
Searching...
No Matches
vertex_set.cpp
Go to the documentation of this file.
1/*
2 * This program is part of KiCad, a free EDA CAD application.
3 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include <geometry/vertex_set.h>
20
21void VERTEX_SET::SetBoundingBox( const BOX2I& aBBox ) { m_bbox = aBBox; }
22
23
27VERTEX* VERTEX_SET::createList( const SHAPE_LINE_CHAIN& points, VERTEX* aTail, void* aUserData )
28{
29 VERTEX* tail = aTail;
30 double sum = 0.0;
31
32 // Check for winding order
33 for( int i = 0; i < points.PointCount(); i++ )
34 {
35 VECTOR2D p1 = points.CPoint( i );
36 VECTOR2D p2 = points.CPoint( i + 1 );
37
38 sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
39 }
40
41 VECTOR2L last_pt;
42 bool first = true;
43
44 auto addVertex = [&]( int i )
45 {
46 const VECTOR2I& pt = points.CPoint( i );
47
48 if( first || pt.SquaredDistance( last_pt ) > m_simplificationLevel )
49 {
50 tail = insertVertex( i, pt, tail, aUserData );
51 last_pt = pt;
52 first = false;
53 }
54 };
55
56 if( sum > 0.0 )
57 {
58 for( int i = points.PointCount() - 1; i >= 0; i-- )
59 addVertex( i );
60 }
61 else
62 {
63 for( int i = 0; i < points.PointCount(); i++ )
64 addVertex( i );
65 }
66
67 if( tail && ( *tail == *tail->next ) )
68 {
69 tail->next->remove();
70 }
71
72 return tail;
73}
74
75
81uint32_t VERTEX_SET::zOrder( const double aX, const double aY ) const
82{
83 double limit_x = std::clamp( ( aX - m_bbox.GetX() ) / m_bbox.GetWidth(), 0.0, 1.0 );
84 double limit_y = std::clamp( ( aY - m_bbox.GetY() ) / m_bbox.GetHeight(), 0.0, 1.0 );
85
86 uint32_t x = static_cast<uint32_t>( limit_x * 32767.0 );
87 uint32_t y = static_cast<uint32_t>( limit_y * 32767.0 );
88
89 x = ( x | ( x << 8 ) ) & 0x00FF00FF;
90 x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;
91 x = ( x | ( x << 2 ) ) & 0x33333333;
92 x = ( x | ( x << 1 ) ) & 0x55555555;
93
94 y = ( y | ( y << 8 ) ) & 0x00FF00FF;
95 y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;
96 y = ( y | ( y << 2 ) ) & 0x33333333;
97 y = ( y | ( y << 1 ) ) & 0x55555555;
98
99 return x | ( y << 1 );
100}
101
102
106double VERTEX_SET::area( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const
107{
108 return ( q->y - p->y ) * ( r->x - q->x ) - ( q->x - p->x ) * ( r->y - q->y );
109}
110
111
113{
114 m_reflexIndex.clear();
115
116 VERTEX* p = aStart;
117
118 do
119 {
120 // Collinear vertices count as reflex, matching the blocker test in isEar()
121 p->m_inReflexIndex = area( p->prev, p, p->next ) >= 0;
122
123 if( p->m_inReflexIndex )
124 {
125 p->updateOrder();
126 m_reflexIndex.push_back( { p->z, p->x, p->y, p } );
127 }
128
129 p = p->next;
130 } while( p != aStart );
131
132 std::sort( m_reflexIndex.begin(), m_reflexIndex.end(),
133 []( const REFLEX_ENTRY& aLhs, const REFLEX_ENTRY& aRhs )
134 {
135 return aLhs.z < aRhs.z;
136 } );
137
138 m_reflexIndexValid = true;
139}
140
141
142bool VERTEX_SET::same_point( const VERTEX* aA, const VERTEX* aB ) const
143{
144 return aA && aB && aA->x == aB->x && aA->y == aB->y;
145}
146
148{
149 VERTEX* nz = aPt->nextZ;
150 VERTEX* pz = aPt->prevZ;
151
152 // If we hit a fracture point, we want to continue around the
153 // edge we are working on and not switch to the pair edge
154 // However, this will depend on which direction the initial
155 // fracture hit is. If we find that we skip directly to
156 // a new fracture point, then we know that we are proceeding
157 // in the wrong direction from the fracture and should
158 // fall through to the next point
159 if( same_point( aPt, nz ) && same_point( aPt->next, nz->prev )
160 && aPt->y == aPt->next->y )
161 {
162 return nz->next;
163 }
164
165 if( same_point( aPt, pz ) && same_point( aPt->next, pz->prev )
166 && aPt->y == aPt->next->y )
167 {
168 return pz->next;
169 }
170
171 return aPt->next;
172}
173
175{
176 VERTEX* nz = aPt->nextZ;
177 VERTEX* pz = aPt->prevZ;
178
179 // If we hit a fracture point, we want to continue around the
180 // edge we are working on and not switch to the pair edge
181 // However, this will depend on which direction the initial
182 // fracture hit is. If we find that we skip directly to
183 // a new fracture point, then we know that we are proceeding
184 // in the wrong direction from the fracture and should
185 // fall through to the next point
186 if( same_point( aPt, nz )
187 && aPt->y == aPt->prev->y)
188 {
189 return nz->prev;
190 }
191
192 if( same_point( aPt, pz )
193 && aPt->y == aPt->prev->y )
194 {
195 return pz->prev;
196 }
197
198 return aPt->prev;
199
200}
201
202
203bool VERTEX_SET::locallyInside( const VERTEX* a, const VERTEX* b ) const
204{
205 const VERTEX* an = getNextOutlineVertex( a );
206 const VERTEX* ap = getPrevOutlineVertex( a );
207
208 if( area( ap, a, an ) < 0 )
209 return area( a, b, an ) >= 0 && area( a, ap, b ) >= 0;
210 else
211 return area( a, b, ap ) < 0 || area( a, an, b ) < 0;
212}
213
214
215bool VERTEX_SET::middleInside( const VERTEX* a, const VERTEX* b ) const
216{
217 const VERTEX* p = a;
218 bool inside = false;
219 double px = ( a->x + b->x ) / 2;
220 double py = ( a->y + b->y ) / 2;
221
222 do
223 {
224 if( ( ( p->y > py ) != ( p->next->y > py ) )
225 && ( px < ( p->next->x - p->x ) * ( py - p->y ) / ( p->next->y - p->y ) + p->x ) )
226 inside = !inside;
227
228 p = p->next;
229 } while( p != a );
230
231 return inside;
232}
233
240VERTEX* VERTEX_SET::insertVertex( int aIndex, const VECTOR2I& pt, VERTEX* last, void* aUserData )
241{
242 m_vertices.emplace_back( aIndex, pt.x, pt.y, this, aUserData );
243
244 VERTEX* p = &m_vertices.back();
245
246 if( !last )
247 {
248 p->prev = p;
249 p->next = p;
250 }
251 else
252 {
253 p->next = last->next;
254 p->prev = last;
255 last->next->prev = p;
256 last->next = p;
257 }
258 return p;
259}
260
261
263{
264 VERTEX* a2 = parent->insertVertex( i, VECTOR2I( x, y ), nullptr, m_userData );
265 parent->m_vertices.emplace_back( b->i, b->x, b->y, parent, m_userData );
266 VERTEX* b2 = &parent->m_vertices.back();
267 VERTEX* an = next;
268 VERTEX* bp = b->prev;
269
270 next = b;
271 b->prev = this;
272
273 a2->next = an;
274 an->prev = a2;
275
276 b2->next = a2;
277 a2->prev = b2;
278
279 bp->next = b2;
280 b2->prev = bp;
281
282 return b2;
283}
284
285
287{
288 if( !z )
289 z = parent->zOrder( x, y );
290}
291
292
293bool VERTEX::isEar( bool aMatchUserData ) const
294{
295 const VERTEX* a = prev;
296 const VERTEX* b = this;
297 const VERTEX* c = next;
298
299 if( aMatchUserData )
300 {
301 while( a->GetUserData() != m_userData )
302 a = a->prev;
303
304 while( c->GetUserData() != m_userData )
305 c = c->next;
306 }
307
308 // If the area >=0, then the three points for a concave sequence
309 // with b as the reflex point
310 if( parent->area( a, b, c ) >= 0 )
311 return false;
312
313 auto blocksEar =
314 [&]( VERTEX* p )
315 {
316 return ( !aMatchUserData || p->GetUserData() == m_userData )
317 && p != a && p != c
318 && p->inTriangle( *a, *b, *c )
319 && parent->area( p->prev, p, p->next ) >= 0;
320 };
321
322 // Earcut revisits a rejected vertex on every lap of the ring, usually for the same blocker
323 if( m_earBlocker && m_earBlocker->next && blocksEar( m_earBlocker ) )
324 return false;
325
326 // triangle bbox
327 const double minTX = std::min( a->x, std::min( b->x, c->x ) );
328 const double minTY = std::min( a->y, std::min( b->y, c->y ) );
329 const double maxTX = std::max( a->x, std::max( b->x, c->x ) );
330 const double maxTY = std::max( a->y, std::max( b->y, c->y ) );
331
332 // z-order range for the current triangle bounding box
333 const uint32_t minZ = parent->zOrder( minTX, minTY );
334 const uint32_t maxZ = parent->zOrder( maxTX, maxTY );
335
336 if( !aMatchUserData && parent->m_reflexIndexValid )
337 {
338 const std::vector<VERTEX_SET::REFLEX_ENTRY>& index = parent->m_reflexIndex;
339
340 auto it = std::lower_bound( index.begin(), index.end(), minZ,
341 []( const VERTEX_SET::REFLEX_ENTRY& aEntry, uint32_t aZ )
342 {
343 return aEntry.z < aZ;
344 } );
345
346 for( ; it != index.end() && it->z <= maxZ; ++it )
347 {
348 // Same test as inTriangle(), on the copy held in the index to skip the dereference
349 if( ( c->x - it->x ) * ( a->y - it->y ) - ( a->x - it->x ) * ( c->y - it->y ) < 0
350 || ( a->x - it->x ) * ( b->y - it->y ) - ( b->x - it->x ) * ( a->y - it->y ) < 0
351 || ( b->x - it->x ) * ( c->y - it->y ) - ( c->x - it->x ) * ( b->y - it->y ) < 0 )
352 {
353 continue;
354 }
355
356 // Entries of clipped ears stay behind with a null link
357 if( it->v->next && blocksEar( it->v ) )
358 {
359 m_earBlocker = it->v;
360 return false;
361 }
362 }
363
364 return true;
365 }
366
367 // first look for points inside the triangle in increasing z-order
368 VERTEX* p = nextZ;
369
370 while( p && p->z <= maxZ )
371 {
372 if( blocksEar( p ) )
373 {
374 m_earBlocker = p;
375 return false;
376 }
377
378 p = p->nextZ;
379 }
380
381 // then look for points in decreasing z-order
382 p = prevZ;
383
384 while( p && p->z >= minZ )
385 {
386 if( blocksEar( p ) )
387 {
388 m_earBlocker = p;
389 return false;
390 }
391
392 p = p->prevZ;
393 }
394
395 return true;
396}
int index
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:582
std::deque< VERTEX > m_vertices
Definition vertex_set.h:366
friend class VERTEX
Definition vertex_set.h:261
bool middleInside(const VERTEX *a, const VERTEX *b) const
Check if the middle of the segment from a to b is inside the polygon.
VERTEX * createList(const SHAPE_LINE_CHAIN &points, VERTEX *aTail=nullptr, void *aUserData=nullptr)
Create a list of vertices from a line chain.
bool locallyInside(const VERTEX *a, const VERTEX *b) const
Check whether the segment from vertex a -> vertex b is inside the polygon around the immediate area o...
BOX2I m_bbox
Definition vertex_set.h:365
VERTEX * getPrevOutlineVertex(const VERTEX *aPt) const
Get the previous vertex in the outline, avoiding steiner points and points that overlap with splits.
void SetBoundingBox(const BOX2I &aBBox)
void buildReflexIndex(VERTEX *aStart)
Index the reflex vertices of the ring containing aStart in z-order.
std::vector< REFLEX_ENTRY > m_reflexIndex
Definition vertex_set.h:368
double area(const VERTEX *p, const VERTEX *q, const VERTEX *r) const
Return the twice the signed area of the triangle formed by vertices p, q, and r.
VERTEX * insertVertex(int aIndex, const VECTOR2I &pt, VERTEX *last, void *aUserData=nullptr)
Insert a vertex into the vertex set.
VECTOR2I::extended_type m_simplificationLevel
Definition vertex_set.h:367
VERTEX * getNextOutlineVertex(const VERTEX *aPt) const
Get the next vertex in the outline, avoiding steiner points and points that overlap with splits.
uint32_t zOrder(const double aX, const double aY) const
Note that while the inputs are doubles, these are scaled by the size of the bounding box to fit into ...
bool m_reflexIndexValid
Definition vertex_set.h:369
bool same_point(const VERTEX *aA, const VERTEX *aB) const
Check if two vertices are at the same point.
VERTEX * split(VERTEX *b)
Split the referenced polygon between the reference point and vertex b, assuming they are in the same ...
const double x
Definition vertex_set.h:235
VERTEX * next
Definition vertex_set.h:241
VERTEX * prevZ
Definition vertex_set.h:247
bool inTriangle(const VERTEX &a, const VERTEX &b, const VERTEX &c)
Check to see if triangle surrounds our current vertex.
Definition vertex_set.h:191
VERTEX * nextZ
Definition vertex_set.h:248
VERTEX * prev
Definition vertex_set.h:240
const int i
Definition vertex_set.h:234
VERTEX * m_earBlocker
Definition vertex_set.h:253
void * GetUserData() const
Definition vertex_set.h:76
void * m_userData
Definition vertex_set.h:250
void remove()
Remove the node from the linked list and z-ordered linked list.
Definition vertex_set.h:81
bool m_inReflexIndex
Definition vertex_set.h:256
VERTEX(int aIndex, double aX, double aY, VERTEX_SET *aParent, void *aUserData=nullptr)
Definition vertex_set.h:58
uint32_t z
Definition vertex_set.h:244
bool isEar(bool aMatchUserData=false) const
Check whether the given vertex is in the middle of an ear.
const double y
Definition vertex_set.h:236
void updateOrder()
VERTEX_SET * parent
Definition vertex_set.h:237
Definition vertex_set.h:358
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:709