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, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifndef POLY_YSTRIPES_INDEX_H
21#define POLY_YSTRIPES_INDEX_H
22
23#include <algorithm>
24#include <cmath>
25#include <climits>
26#include <cstdint>
27#include <vector>
28
29#include <geometry/seg.h>
31#include <math/util.h>
32#include <math/vector2d.h>
33
49{
50public:
52
61 void Build( const SHAPE_POLY_SET& aPolySet )
62 {
63 m_outlineCount = aPolySet.OutlineCount();
64 m_edges.clear();
65 m_stripes.clear();
66
67 int totalEdges = 0;
68
69 for( int outlineIdx = 0; outlineIdx < m_outlineCount; outlineIdx++ )
70 {
71 totalEdges += aPolySet.COutline( outlineIdx ).PointCount();
72
73 for( int holeIdx = 0; holeIdx < aPolySet.HoleCount( outlineIdx ); holeIdx++ )
74 totalEdges += aPolySet.CHole( outlineIdx, holeIdx ).PointCount();
75 }
76
77 if( totalEdges == 0 )
78 return;
79
80 m_edges.reserve( totalEdges );
81
82 int64_t yMin = INT_MAX;
83 int64_t yMax = INT_MIN;
84
85 for( int outlineIdx = 0; outlineIdx < m_outlineCount; outlineIdx++ )
86 {
87 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( outlineIdx );
88 int ptCount = outline.PointCount();
89
90 if( ptCount >= 3 )
91 {
92 for( int j = 0; j < ptCount; j++ )
93 {
94 const VECTOR2I& p1 = outline.CPoint( j );
95 const VECTOR2I& p2 = outline.CPoint( ( j + 1 ) % ptCount );
96
97 if( p1.y == p2.y )
98 continue;
99
100 m_edges.push_back( { p1, p2, outlineIdx, false } );
101
102 yMin = std::min( yMin, static_cast<int64_t>( std::min( p1.y, p2.y ) ) );
103 yMax = std::max( yMax, static_cast<int64_t>( std::max( p1.y, p2.y ) ) );
104 }
105 }
106
107 for( int holeIdx = 0; holeIdx < aPolySet.HoleCount( outlineIdx ); holeIdx++ )
108 {
109 const SHAPE_LINE_CHAIN& hole = aPolySet.CHole( outlineIdx, holeIdx );
110 int holePtCount = hole.PointCount();
111
112 if( holePtCount < 3 )
113 continue;
114
115 for( int j = 0; j < holePtCount; j++ )
116 {
117 const VECTOR2I& p1 = hole.CPoint( j );
118 const VECTOR2I& p2 = hole.CPoint( ( j + 1 ) % holePtCount );
119
120 if( p1.y == p2.y )
121 continue;
122
123 m_edges.push_back( { p1, p2, outlineIdx, true } );
124
125 yMin = std::min( yMin, static_cast<int64_t>( std::min( p1.y, p2.y ) ) );
126 yMax = std::max( yMax, static_cast<int64_t>( std::max( p1.y, p2.y ) ) );
127 }
128 }
129 }
130
131 if( m_edges.empty() )
132 return;
133
134 int stripeCount = static_cast<int>( std::sqrt( static_cast<double>( m_edges.size() ) ) );
135 stripeCount = std::max( 1, std::min( stripeCount, 65536 ) );
136
137 m_yMin = yMin;
138 m_yMax = yMax;
139 int64_t yRange = yMax - yMin;
140
141 // Guard against degenerate case where all edges share the same Y coordinate
142 if( yRange == 0 )
143 {
144 stripeCount = 1;
145 m_stripeHeight = 1;
146 }
147 else
148 {
149 m_stripeHeight = ( yRange + stripeCount - 1 ) / stripeCount;
150 }
151
152 m_stripeCount = stripeCount;
153 m_stripes.resize( stripeCount );
154
155 for( size_t i = 0; i < m_edges.size(); i++ )
156 {
157 const EDGE& e = m_edges[i];
158 int64_t eYMin = std::min( static_cast<int64_t>( e.p1.y ),
159 static_cast<int64_t>( e.p2.y ) );
160 int64_t eYMax = std::max( static_cast<int64_t>( e.p1.y ),
161 static_cast<int64_t>( e.p2.y ) );
162
163 int sMin = yToStripe( eYMin );
164 int sMax = yToStripe( eYMax );
165
166 for( int s = sMin; s <= sMax; s++ )
167 m_stripes[s].push_back( static_cast<int>( i ) );
168 }
169 }
170
184 bool Contains( const VECTOR2I& aPt, int aAccuracy = 0 ) const
185 {
186 if( m_edges.empty() )
187 return false;
188
189 int stripe = yToStripe( static_cast<int64_t>( aPt.y ) );
190
191 if( stripe >= 0 && stripe < m_stripeCount )
192 {
193 int crossingsStack[8] = {};
194 int* crossings = crossingsStack;
195 std::vector<int> crossingsHeap;
196
197 if( m_outlineCount > 8 )
198 {
199 crossingsHeap.resize( m_outlineCount, 0 );
200 crossings = crossingsHeap.data();
201 }
202
203 for( int idx : m_stripes[stripe] )
204 {
205 const EDGE& seg = m_edges[idx];
206 const VECTOR2I& p1 = seg.p1;
207 const VECTOR2I& p2 = seg.p2;
208
209 if( ( p1.y >= aPt.y ) == ( p2.y >= aPt.y ) )
210 continue;
211
212 const VECTOR2I diff = p2 - p1;
213 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
214
215 if( aPt.x - p1.x < d )
216 crossings[seg.outlineIdx]++;
217 }
218
219 for( int i = 0; i < m_outlineCount; i++ )
220 {
221 if( crossings[i] & 1 )
222 return true;
223 }
224 }
225
226 if( aAccuracy > 1 )
227 {
228 int sMin = yToStripe( static_cast<int64_t>( aPt.y ) - aAccuracy );
229 int sMax = yToStripe( static_cast<int64_t>( aPt.y ) + aAccuracy );
230 SEG::ecoord accuracySq = SEG::Square( aAccuracy );
231
232 for( int s = sMin; s <= sMax; s++ )
233 {
234 if( s < 0 || s >= m_stripeCount )
235 continue;
236
237 for( int idx : m_stripes[s] )
238 {
239 const EDGE& seg = m_edges[idx];
240
241 if( seg.isHole )
242 continue;
243
244 SEG edge( seg.p1, seg.p2 );
245
246 if( edge.SquaredDistance( aPt ) <= accuracySq )
247 return true;
248 }
249 }
250 }
251
252 return false;
253 }
254
255private:
256 int yToStripe( int64_t aY ) const
257 {
258 int64_t offset = aY - m_yMin;
259 int s = static_cast<int>( offset / m_stripeHeight );
260 return std::max( 0, std::min( s, m_stripeCount - 1 ) );
261 }
262
263 struct EDGE
264 {
268 bool isHole;
269 };
270
271 std::vector<EDGE> m_edges;
272 std::vector<std::vector<int>> m_stripes;
273 int64_t m_yMin = 0;
274 int64_t m_yMax = 0;
275 int64_t m_stripeHeight = 1;
278};
279
280#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:38
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:76
VECTOR2I::extended_type ecoord
Definition seg.h:40
static SEG::ecoord Square(int a)
Definition seg.h:119
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:135
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683