-
Notifications
You must be signed in to change notification settings - Fork 0
/
inhull.m
177 lines (162 loc) · 5.12 KB
/
inhull.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
function in = inhull(testpts,xyz,tess,tol)
% inhull: tests if a set of points are inside a convex hull
% usage: in = inhull(testpts,xyz)
% usage: in = inhull(testpts,xyz,tess)
% usage: in = inhull(testpts,xyz,tess,tol)
%
% arguments: (input)
% testpts - nxp array to test, n data points, in p dimensions
% If you have many points to test, it is most efficient to
% call this function once with the entire set.
%
% xyz - mxp array of vertices of the convex hull, as used by
% convhulln.
%
% tess - tessellation (or triangulation) generated by convhulln
% If tess is left empty or not supplied, then it will be
% generated.
%
% tol - (OPTIONAL) tolerance on the tests for inclusion in the
% convex hull. You can think of tol as the distance a point
% may possibly lie outside the hull, and still be perceived
% as on the surface of the hull. Because of numerical slop
% nothing can ever be done exactly here. I might guess a
% semi-intelligent value of tol to be
%
% tol = 1.e-13*mean(abs(xyz(:)))
%
% In higher dimensions, the numerical issues of floating
% point arithmetic will probably suggest a larger value
% of tol.
%
% DEFAULT: tol = 0
%
% arguments: (output)
% in - nx1 logical vector
% in(i) == 1 --> the i'th point was inside the convex hull.
%
% Example usage: The first point should be inside, the second out
%
% xy = randn(20,2);
% tess = convhulln(xy);
% testpoints = [ 0 0; 10 10];
% in = inhull(testpoints,xy,tess)
%
% in =
% 1
% 0
%
% A non-zero count of the number of degenerate simplexes in the hull
% will generate a warning (in 4 or more dimensions.) This warning
% may be disabled off with the command:
%
% warning('off','inhull:degeneracy')
%
% See also: convhull, convhulln, delaunay, delaunayn, tsearch, tsearchn
%
% Author: John D'Errico
% e-mail: [email protected]
% Release: 3.0
% Release date: 10/26/06
% get array sizes
% m points, p dimensions
p = size(xyz,2);
[n,c] = size(testpts);
if p ~= c
error 'testpts and xyz must have the same number of columns'
end
if p < 2
error 'Points must lie in at least a 2-d space.'
end
% was the convex hull supplied?
if (nargin<3) || isempty(tess)
tess = convhulln(xyz);
end
[nt,c] = size(tess);
if c ~= p
error 'tess array is incompatible with a dimension p space'
end
% was tol supplied?
if (nargin<4) || isempty(tol)
tol = 0;
end
% build normal vectors
switch p
case 2
% really simple for 2-d
nrmls = (xyz(tess(:,1),:) - xyz(tess(:,2),:)) * [0 1;-1 0];
% Any degenerate edges?
del = sqrt(sum(nrmls.^2,2));
degenflag = (del<(max(del)*10*eps));
if sum(degenflag)>0
warning('inhull:degeneracy',[num2str(sum(degenflag)), ...
' degenerate edges identified in the convex hull'])
% we need to delete those degenerate normal vectors
nrmls(degenflag,:) = [];
nt = size(nrmls,1);
end
case 3
% use vectorized cross product for 3-d
ab = xyz(tess(:,1),:) - xyz(tess(:,2),:);
ac = xyz(tess(:,1),:) - xyz(tess(:,3),:);
nrmls = cross(ab,ac,2);
degenflag = false(nt,1);
otherwise
% slightly more work in higher dimensions,
nrmls = zeros(nt,p);
degenflag = false(nt,1);
for i = 1:nt
% just in case of a degeneracy
% Note that bsxfun COULD be used in this line, but I have chosen to
% not do so to maintain compatibility. This code is still used by
% users of older releases.
% nullsp = null(bsxfun(@minus,xyz(tess(i,2:end),:),xyz(tess(i,1),:)))';
nullsp = null(xyz(tess(i,2:end),:) - repmat(xyz(tess(i,1),:),p-1,1))';
if size(nullsp,1)>1
degenflag(i) = true;
nrmls(i,:) = NaN;
else
nrmls(i,:) = nullsp;
end
end
if sum(degenflag)>0
warning('inhull:degeneracy',[num2str(sum(degenflag)), ...
' degenerate simplexes identified in the convex hull'])
% we need to delete those degenerate normal vectors
nrmls(degenflag,:) = [];
nt = size(nrmls,1);
end
end
% scale normal vectors to unit length
nrmllen = sqrt(sum(nrmls.^2,2));
% again, bsxfun COULD be employed here...
% nrmls = bsxfun(@times,nrmls,1./nrmllen);
nrmls = nrmls.*repmat(1./nrmllen,1,p);
% center point in the hull
center = mean(xyz,1);
% any point in the plane of each simplex in the convex hull
a = xyz(tess(~degenflag,1),:);
% ensure the normals are pointing inwards
% this line too could employ bsxfun...
% dp = sum(bsxfun(@minus,center,a).*nrmls,2);
dp = sum((repmat(center,nt,1) - a).*nrmls,2);
k = dp<0;
nrmls(k,:) = -nrmls(k,:);
% We want to test if: dot((x - a),N) >= 0
% If so for all faces of the hull, then x is inside
% the hull. Change this to dot(x,N) >= dot(a,N)
aN = sum(nrmls.*a,2);
% test, be careful in case there are many points
in = false(n,1);
% if n is too large, we need to worry about the
% dot product grabbing huge chunks of memory.
memblock = 1e24;
blocks = max(1,floor(n/(memblock/nt)));
aNr = repmat(aN,1,length(1:blocks:n));
for i = 1:blocks
j = i:blocks:n;
if size(aNr,2) ~= length(j),
aNr = repmat(aN,1,length(j));
end
in(j) = all((nrmls*testpts(j,:)' - aNr) >= -tol,1)';
end