-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathinpoly2.m
191 lines (151 loc) · 6.03 KB
/
inpoly2.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
function [STAT,BNDS] = inpoly2(varargin)
%INPOLY2 compute "points-in-polygon" queries.
% [STAT] = INPOLY2(VERT,NODE,EDGE) returns the "inside/ou-
% tside" status for a set of vertices VERT and a polygon
% {NODE,EDGE} embedded in a two-dimensional plane. General
% non-convex and multiply-connected polygonal regions can
% be handled. VERT is an N-by-2 array of XY coordinates to
% be tested. STAT is an associated N-by-1 logical array,
% with STAT(II) = TRUE if VERT(II,:) is an interior point.
% The polygonal region is defined as a piecewise-straight-
% line-graph, where NODE is an M-by-2 array of polygon ve-
% rtices and EDGE is a P-by-2 array of edge indexing. Each
% row in EDGE represents an edge of the polygon, such that
% NODE(EDGE(KK,1),:) and NODE(EDGE(KK,2),:) are the coord-
% inates of the endpoints of the KK-TH edge. If the argum-
% ent EDGE is omitted it assumed that the vertices in NODE
% are connected in ascending order.
%
% [STAT,BNDS] = INPOLY2(..., FTOL) also returns an N-by-1
% logical array BNDS, with BNDS(II) = TRUE if VERT(II,:)
% lies "on" a boundary segment, where FTOL is a floating-
% point tolerance for boundary comparisons. By default,
% FTOL = EPS ^ 0.85.
%
% See also INPOLYGON
% This algorithm is based on a "crossing-number" test, co-
% unting the number of times a line extending from each
% point past the right-most region of the polygon interse-
% cts with the polygonal boundary. Points with odd counts
% are "inside". A simple implementation requires that each
% edge intersection be checked for each point, leading to
% O(N*M) complexity...
%
% This implementation seeks to improve these bounds:
%
% * Sorting the query points by y-value and determining can-
% didate edge intersection sets via binary-search. Given a
% configuration with N test points, M edges and an average
% point-edge "overlap" of H, the overall complexity scales
% like O(M*H + M*LOG(N) + N*LOG(N)), where O(N*LOG(N))
% operations are required for sorting, O(M*LOG(N)) operat-
% ions required for the set of binary-searches, and O(M*H)
% operations required for the intersection tests, where H
% is typically small on average, such that H << N.
%
% * Carefully checking points against the bounding-box asso-
% ciated with each polygon edge. This minimises the number
% of calls to the (relatively) expensive edge intersection
% test.
% Darren Engwirda : 2017 --
% Email : d.engwirda@gmail.com
% Last updated : 19/12/2020
%---------------------------------------------- extract args
node = []; edge = []; vert = [];
fTOL = eps ^ .85;
if (nargin>=+1), vert = varargin{1}; end
if (nargin>=+2), node = varargin{2}; end
if (nargin>=+3), edge = varargin{3}; end
if (nargin>=+4), fTOL = varargin{4}; end
%---------------------------------------------- default args
nnod = size(node,1) ;
nvrt = size(vert,1) ;
if (isempty(edge))
edge = [(1:nnod-1)',(2:nnod)'; nnod,1];
end
%---------------------------------------------- basic checks
if ( ~isnumeric(node) || ...
~isnumeric(edge) || ...
~isnumeric(vert) || ...
~isnumeric(fTOL) )
error('inpoly2:incorrectInputClass' , ...
'Incorrect input class.') ;
end
%---------------------------------------------- basic checks
if (ndims(node) ~= +2 || ...
ndims(edge) ~= +2 || ...
ndims(vert) ~= +2 || ...
numel(fTOL) ~= +1 )
error('inpoly2:incorrectDimensions' , ...
'Incorrect input dimensions.');
end
if (size(node,2)~= +2 || ...
size(edge,2)~= +2 || ...
size(vert,2)~= +2 )
error('inpoly2:incorrectDimensions' , ...
'Incorrect input dimensions.');
end
%---------------------------------------------- basic checks
if (min([edge(:)]) < +1 || ...
max([edge(:)]) > nnod)
error('inpoly2:invalidInputs', ...
'Invalid EDGE input array.') ;
end
STAT = false(size(vert,1),1) ;
BNDS = false(size(vert,1),1) ;
%----------------------------------- prune points using bbox
nmin = min(node,[],1);
nmax = max(node,[],1);
ddxy = nmax - nmin ;
lbar = sum(ddxy) / 2.;
veps = fTOL * lbar ;
mask = ...
vert(:,1) >= min (node(:,1)) - veps & ...
vert(:,1) <= max (node(:,1)) + veps & ...
vert(:,2) >= min (node(:,2)) - veps & ...
vert(:,2) <= max (node(:,2)) + veps ;
vert = vert(mask, :) ;
if (isempty(vert)), return; end
%-------------- flip to ensure the y-axis is the "long" axis
vmin = min(vert,[],1);
vmax = max(vert,[],1);
ddxy = vmax - vmin ;
if (ddxy(1) > ddxy(2))
vert = vert(:,[2,1]) ;
node = node(:,[2,1]) ;
end
%----------------------------------- sort points via y-value
swap = ...
node(edge(:,2),2) ...
< node(edge(:,1),2) ;
edge(swap,[1,2]) = ...
edge(swap,[2,1]) ;
[~,ivec] = ...
sort(vert(:,+2)) ;
vert = vert (ivec,:) ;
if (exist( ...
'OCTAVE_VERSION','builtin') > +0)
if (exist('inpoly2_oct','file') == +3)
%-- delegate to the compiled version of the code if it's
%-- available
[stat,bnds] = ...
inpoly2_oct( ...
vert,node,edge,fTOL,lbar) ;
else
%-- otherwise, just call the native m-code version
[stat,bnds] = ...
inpoly2_mat( ...
vert,node,edge,fTOL,lbar) ;
end
else
%-- MATLAB's JIT is generally smart enough these days to
%-- run this efficiently
[stat,bnds] = ...
inpoly2_mat( ...
vert,node,edge,fTOL,lbar) ;
end
stat(ivec) = stat ;
bnds(ivec) = bnds ;
STAT(mask) = stat ;
BNDS(mask) = bnds ;
end