KiCad PCB EDA Suite
Loading...
Searching...
No Matches
poly_ystripes_index.h
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 The 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 3
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-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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
24#ifndef POLY_YSTRIPES_INDEX_H
25#define POLY_YSTRIPES_INDEX_H
26
27#include <algorithm>
28#include <cmath>
29#include <climits>
30#include <cstdint>
31#include <vector>
32
33#include <geometry/seg.h>
35#include <math/util.h>
36#include <math/vector2d.h>
37
53{
54public:
56
65 void Build( const SHAPE_POLY_SET& aPolySet )
66 {
67 m_outlineCount = aPolySet.OutlineCount();
68 m_edges.clear();
69 m_stripes.clear();
70
71 int totalEdges = 0;
72
73 for( int outlineIdx = 0; outlineIdx < m_outlineCount; outlineIdx++ )
74 {
75 totalEdges += aPolySet.COutline( outlineIdx ).PointCount();
76
77 for( int holeIdx = 0; holeIdx < aPolySet.HoleCount( outlineIdx ); holeIdx++ )
78 totalEdges += aPolySet.CHole( outlineIdx, holeIdx ).PointCount();
79 }
80
81 if( totalEdges == 0 )
82 return;
83
84 m_edges.reserve( totalEdges );
85
86 int64_t yMin = INT_MAX;
87 int64_t yMax = INT_MIN;
88
89 for( int outlineIdx = 0; outlineIdx < m_outlineCount; outlineIdx++ )
90 {
91 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( outlineIdx );
92 int ptCount = outline.PointCount();
93
94 if( ptCount >= 3 )
95 {
96 for( int j = 0; j < ptCount; j++ )
97 {
98 const VECTOR2I& p1 = outline.CPoint( j );
99 const VECTOR2I& p2 = outline.CPoint( ( j + 1 ) % ptCount );
100
101 if( p1.y == p2.y )
102 continue;
103
104 m_edges.push_back( { p1, p2, outlineIdx, false } );
105
106 yMin = std::min( yMin, static_cast<int64_t>( std::min( p1.y, p2.y ) ) );
107 yMax = std::max( yMax, static_cast<int64_t>( std::max( p1.y, p2.y ) ) );
108 }
109 }
110
111 for( int holeIdx = 0; holeIdx < aPolySet.HoleCount( outlineIdx ); holeIdx++ )
112 {
113 const SHAPE_LINE_CHAIN& hole = aPolySet.CHole( outlineIdx, holeIdx );
114 int holePtCount = hole.PointCount();
115
116 if( holePtCount < 3 )
117 continue;
118
119 for( int j = 0; j < holePtCount; j++ )
120 {
121 const VECTOR2I& p1 = hole.CPoint( j );
122 const VECTOR2I& p2 = hole.CPoint( ( j + 1 ) % holePtCount );
123
124 if( p1.y == p2.y )
125 continue;
126
127 m_edges.push_back( { p1, p2, outlineIdx, true } );
128
129 yMin = std::min( yMin, static_cast<int64_t>( std::min( p1.y, p2.y ) ) );
130 yMax = std::max( yMax, static_cast<int64_t>( std::max( p1.y, p2.y ) ) );
131 }
132 }
133 }
134
135 if( m_edges.empty() )
136 return;
137
138 int stripeCount = static_cast<int>( std::sqrt( static_cast<double>( m_edges.size() ) ) );
139 stripeCount = std::max( 1, std::min( stripeCount, 65536 ) );
140
141 m_yMin = yMin;
142 m_yMax = yMax;
143 int64_t yRange = yMax - yMin;
144
145 // Guard against degenerate case where all edges share the same Y coordinate
146 if( yRange == 0 )
147 {
148 stripeCount = 1;
149 m_stripeHeight = 1;
150 }
151 else
152 {
153 m_stripeHeight = ( yRange + stripeCount - 1 ) / stripeCount;
154 }
155
156 m_stripeCount = stripeCount;
157 m_stripes.resize( stripeCount );
158
159 for( size_t i = 0; i < m_edges.size(); i++ )
160 {
161 const EDGE& e = m_edges[i];
162 int64_t eYMin = std::min( static_cast<int64_t>( e.p1.y ),
163 static_cast<int64_t>( e.p2.y ) );
164 int64_t eYMax = std::max( static_cast<int64_t>( e.p1.y ),
165 static_cast<int64_t>( e.p2.y ) );
166
167 int sMin = yToStripe( eYMin );
168 int sMax = yToStripe( eYMax );
169
170 for( int s = sMin; s <= sMax; s++ )
171 m_stripes[s].push_back( static_cast<int>( i ) );
172 }
173 }
174
188 bool Contains( const VECTOR2I& aPt, int aAccuracy = 0 ) const
189 {
190 if( m_edges.empty() )
191 return false;
192
193 int stripe = yToStripe( static_cast<int64_t>( aPt.y ) );
194
195 if( stripe >= 0 && stripe < m_stripeCount )
196 {
197 int crossingsStack[8] = {};
198 int* crossings = crossingsStack;
199 std::vector<int> crossingsHeap;
200
201 if( m_outlineCount > 8 )
202 {
203 crossingsHeap.resize( m_outlineCount, 0 );
204 crossings = crossingsHeap.data();
205 }
206
207 for( int idx : m_stripes[stripe] )
208 {
209 const EDGE& seg = m_edges[idx];
210 const VECTOR2I& p1 = seg.p1;
211 const VECTOR2I& p2 = seg.p2;
212
213 if( ( p1.y >= aPt.y ) == ( p2.y >= aPt.y ) )
214 continue;
215
216 const VECTOR2I diff = p2 - p1;
217 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
218
219 if( aPt.x - p1.x < d )
220 crossings[seg.outlineIdx]++;
221 }
222
223 for( int i = 0; i < m_outlineCount; i++ )
224 {
225 if( crossings[i] & 1 )
226 return true;
227 }
228 }
229
230 if( aAccuracy > 1 )
231 {
232 int sMin = yToStripe( static_cast<int64_t>( aPt.y ) - aAccuracy );
233 int sMax = yToStripe( static_cast<int64_t>( aPt.y ) + aAccuracy );
234 SEG::ecoord accuracySq = SEG::Square( aAccuracy );
235
236 for( int s = sMin; s <= sMax; s++ )
237 {
238 if( s < 0 || s >= m_stripeCount )
239 continue;
240
241 for( int idx : m_stripes[s] )
242 {
243 const EDGE& seg = m_edges[idx];
244
245 if( seg.isHole )
246 continue;
247
248 SEG edge( seg.p1, seg.p2 );
249
250 if( edge.SquaredDistance( aPt ) <= accuracySq )
251 return true;
252 }
253 }
254 }
255
256 return false;
257 }
258
259private:
260 int yToStripe( int64_t aY ) const
261 {
262 int64_t offset = aY - m_yMin;
263 int s = static_cast<int>( offset / m_stripeHeight );
264 return std::max( 0, std::min( s, m_stripeCount - 1 ) );
265 }
266
267 struct EDGE
268 {
272 bool isHole;
273 };
274
275 std::vector<EDGE> m_edges;
276 std::vector<std::vector<int>> m_stripes;
277 int64_t m_yMin = 0;
278 int64_t m_yMax = 0;
279 int64_t m_stripeHeight = 1;
282};
283
284#endif // POLY_YSTRIPES_INDEX_H
int yToStripe(int64_t aY) const
std::vector< EDGE > m_edges
bool Contains(const VECTOR2I &aPt, int aAccuracy=0) const
Test whether a point is inside the indexed polygon set.
POLY_YSTRIPES_INDEX()=default
std::vector< std::vector< int > > m_stripes
void Build(const SHAPE_POLY_SET &aPolySet)
Build the spatial index from a SHAPE_POLY_SET's outlines and holes.
Definition seg.h:42
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:80
VECTOR2I::extended_type ecoord
Definition seg.h:44
static SEG::ecoord Square(int a)
Definition seg.h:123
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.
Represent a set of closed polygons.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
table push_back({ "Source", "Layer", "Vertices", "Strategy", "Build(us)", "ns/query", "Mquery/s", "Inside" })
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition util.h:139
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687