GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
convhulln.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2000-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26/*
2729. July 2000 - Kai Habel: first release
282002-04-22 Paul Kienzle
29* Use warning(...) function rather than writing to cerr
302006-05-01 Tom Holroyd
31* add support for consistent winding in all dimensions; output is
32* guaranteed to be simplicial.
33*/
34
35#if defined (HAVE_CONFIG_H)
36# include "config.h"
37#endif
38
39#include <limits>
40#include <string>
41
42#include "Array.h"
43#include "dMatrix.h"
44#include "oct-locbuf.h"
45#include "unwind-prot.h"
46
47#include "defun-dld.h"
48#include "error.h"
49#include "errwarn.h"
50#include "ov.h"
51
52#if defined (HAVE_QHULL)
53
54# include "oct-qhull.h"
55
56# if defined (NEED_QHULL_R_VERSION)
57char qh_version[] = "convhulln.oct 2007-07-24";
58# endif
59
60static void
61free_qhull_memory (qhT *qh)
62{
63 qh_freeqhull (qh, ! qh_ALL);
64
65 int curlong, totlong;
66 qh_memfreeshort (qh, &curlong, &totlong);
67
68 if (curlong || totlong)
69 warning ("convhulln: did not free %d bytes of long memory (%d pieces)",
70 totlong, curlong);
71}
72
73static bool
74octave_qhull_dims_ok (octave_idx_type dim, octave_idx_type n, const char *who)
75{
76 if (sizeof (octave_idx_type) > sizeof (int))
77 {
78 int maxval = std::numeric_limits<int>::max ();
79
80 if (dim > maxval || n > maxval)
81 error ("%s: dimension too large for Qhull", who);
82 }
83
84 return true;
85}
86
87#endif
88
90
91DEFUN_DLD (convhulln, args, nargout,
92 doc: /* -*- texinfo -*-
93@deftypefn {} {@var{h} =} convhulln (@var{pts})
94@deftypefnx {} {@var{h} =} convhulln (@var{pts}, @var{options})
95@deftypefnx {} {[@var{h}, @var{v}] =} convhulln (@dots{})
96Compute the convex hull of the set of points @var{pts}.
97
98@var{pts} is a matrix of size [n, dim] containing n points in a space of
99dimension dim.
100
101The hull @var{h} is an index vector into the set of points and specifies
102which points form the enclosing hull.
103
104An optional second argument, which must be a string or cell array of
105strings, contains options passed to the underlying qhull command. See the
106documentation for the Qhull library for details
107@url{http://www.qhull.org/html/qh-quick.htm#options}.
108The default options depend on the dimension of the input:
109
110@itemize
111@item 2D, 3D, 4D: @var{options} = @code{@{"Qt"@}}
112
113@item 5D and higher: @var{options} = @code{@{"Qt", "Qx"@}}
114@end itemize
115
116If @var{options} is not present or @code{[]} then the default arguments are
117used. Otherwise, @var{options} replaces the default argument list.
118To append user options to the defaults it is necessary to repeat the
119default arguments in @var{options}. Use a null string to pass no arguments.
120
121If the second output @var{v} is requested the volume of the enclosing
122convex hull is calculated.
123@seealso{convhull, delaunayn, voronoin}
124@end deftypefn */)
125{
126#if defined (HAVE_QHULL)
127
128 int nargin = args.length ();
129
130 if (nargin < 1 || nargin > 2)
131 print_usage ();
132
133 octave_value_list retval;
134
135 Matrix points (args(0).matrix_value ());
136 const octave_idx_type dim = points.columns ();
137 const octave_idx_type num_points = points.rows ();
138
139 if (! octave_qhull_dims_ok (dim, num_points, "convhulln"))
140 return retval;
141
142 points = points.transpose ();
143
144 std::string options;
145
146 if (dim <= 4)
147 options = " Qt";
148 else
149 options = " Qt Qx";
150
151 if (nargin == 2)
152 {
153 if (args(1).is_string ())
154 options = ' ' + args(1).string_value ();
155 else if (args(1).isempty ())
156 ; // Use default options.
157 else if (args(1).iscellstr ())
158 {
159 options = "";
160
161 Array<std::string> tmp = args(1).cellstr_value ();
162
163 for (octave_idx_type i = 0; i < tmp.numel (); i++)
164 options += ' ' + tmp(i);
165 }
166 else
167 error ("convhulln: OPTIONS must be a string, cell array of strings, or empty");
168 }
169
170 boolT ismalloc = false;
171
172 // Set the outfile pointer to stdout for status information.
173 FILE *outfile = nullptr;
174 FILE *errfile = stderr;
175
176 qhT context = { };
177 qhT *qh = &context;
178
179 std::string cmd = "qhull" + options;
180
181 int exitcode = qh_new_qhull (qh, dim, num_points, points.rwdata (),
182 ismalloc, &cmd[0], outfile, errfile);
183
184 unwind_action free_memory ([qh] () { free_qhull_memory (qh); });
185
186 if (exitcode)
187 error ("convhulln: qhull failed");
188
189 bool nonsimp_seen = false;
190
191 octave_idx_type nf = qh->num_facets;
192
193 Matrix idx (nf, dim + 1);
194
195 facetT *facet;
196
197 octave_idx_type i = 0;
198
199 FORALLfacets
200 {
201 octave_idx_type j = 0;
202
203 if (! (nonsimp_seen || facet->simplicial || qh->hull_dim == 2))
204 {
205 nonsimp_seen = true;
206
207 if (cmd.find ("QJ") != std::string::npos)
208 // Should never happen with QJ.
209 error ("convhulln: qhull failed: option 'QJ' returned non-simplicial facet");
210 }
211
212 if (dim == 3)
213 {
214 setT *vertices = qh_facet3vertex (qh, facet);
215
216 vertexT *vertex, **vertexp;
217
218 FOREACHvertex_ (vertices)
219 idx(i, j++) = 1 + qh_pointid(qh, vertex->point);
220
221 qh_settempfree (qh, &vertices);
222 }
223 else
224 {
225 if (facet->toporient ^ qh_ORIENTclock)
226 {
227 vertexT *vertex, **vertexp;
228
229 FOREACHvertex_ (facet->vertices)
230 idx(i, j++) = 1 + qh_pointid(qh, vertex->point);
231 }
232 else
233 {
234 vertexT *vertex, **vertexp;
235
236 FOREACHvertexreverse12_ (facet->vertices)
237 idx(i, j++) = 1 + qh_pointid(qh, vertex->point);
238 }
239 }
240 if (j < dim)
241 warning ("convhulln: facet %" OCTAVE_IDX_TYPE_FORMAT
242 " only has %" OCTAVE_IDX_TYPE_FORMAT
243 " vertices", i, j);
244
245 i++;
246 }
247
248 // Remove extra dimension if all facets were simplicial.
249
250 if (! nonsimp_seen)
251 idx.resize (nf, dim, 0.0);
252
253 if (nargout == 2)
254 {
255 // Calculate volume of convex hull, taken from qhull src/geom2.c.
256
257 realT area;
258 realT dist;
259
260 FORALLfacets
261 {
262 if (! facet->normal)
263 continue;
264
265 if (facet->upperdelaunay && qh->ATinfinity)
266 continue;
267
268 facet->f.area = area = qh_facetarea (qh, facet);
269 facet->isarea = True;
270
271 if (qh->DELAUNAY)
272 {
273 if (facet->upperdelaunay == qh->UPPERdelaunay)
274 qh->totarea += area;
275 }
276 else
277 {
278 qh->totarea += area;
279 qh_distplane (qh, qh->interior_point, facet, &dist);
280 qh->totvol += -dist * area / qh->hull_dim;
281 }
282 }
283
284 retval(1) = octave_value (qh->totvol);
285 }
286
287 retval(0) = idx;
288
289 return retval;
290
291#else
292
293 octave_unused_parameter (args);
294 octave_unused_parameter (nargout);
295
296 err_disabled_feature ("convhulln", "Qhull");
297
298#endif
299}
300
301/*
302%!testif HAVE_QHULL
303%! cube = [0 0 0;1 0 0;1 1 0;0 1 0;0 0 1;1 0 1;1 1 1;0 1 1];
304%! [h, v] = convhulln (cube, "Qt");
305%! assert (size (h), [12 3]);
306%! h = sortrows (sort (h, 2), [1:3]);
307%! assert (h,
308%! [1 2 4; 1 2 6; 1 4 8; 1 5 6; 1 5 8; 2 3 4; 2 3 7; 2 6 7; 3 4 7; 4 7 8; 5 6 7; 5 7 8]);
309%! assert (v, 1, 10*eps);
310%! [h2, v2] = convhulln (cube); # Test default option = "Qt"
311%! assert (size (h2), size (h));
312%! h2 = sortrows (sort (h2, 2), [1:3]);
313%! assert (h2, h);
314%! assert (v2, v, 10*eps);
315
316%!testif HAVE_QHULL
317%! cube = [0 0 0;1 0 0;1 1 0;0 1 0;0 0 1;1 0 1;1 1 1;0 1 1];
318%! [h, v] = convhulln (cube, "QJ");
319%! assert (size (h), [12 3]);
320%! assert (sortrows (sort (h, 2), [1:3]),
321%! [1 2 4; 1 2 5; 1 4 5; 2 3 4; 2 3 6; 2 5 6; 3 4 8; 3 6 7; 3 7 8; 4 5 8; 5 6 8; 6 7 8]);
322%! assert (v, 1.0, 1e6*eps);
323
324%!testif HAVE_QHULL
325%! tetrahedron = [1 1 1;-1 -1 1;-1 1 -1;1 -1 -1];
326%! [h, v] = convhulln (tetrahedron);
327%! h = sortrows (sort (h, 2), [1 2 3]);
328%! assert (h, [1 2 3;1 2 4; 1 3 4; 2 3 4]);
329%! assert (v, 8/3, 10*eps);
330
331%!testif HAVE_QHULL
332%! triangle = [0 0; 1 1; 1 0; 1 2];
333%! h = convhulln (triangle);
334%! assert (size (h), [3 2]);
335*/
336
337OCTAVE_END_NAMESPACE(octave)
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
octave_idx_type rows() const
Definition Array.h:463
octave_idx_type columns() const
Definition Array.h:475
T * rwdata()
Size of the specified dimension.
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Matrix transpose() const
Definition dMatrix.h:138
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition dMatrix.h:156
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin function.
Definition defun-dld.h:61
void print_usage()
Definition defun-int.h:72
void warning(const char *fmt,...)
Definition error.cc:1078
void error(const char *fmt,...)
Definition error.cc:1003
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition errwarn.cc:53