GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
convhulln.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2000-2013 Kai Habel
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 /*
24 29. July 2000 - Kai Habel: first release
25 2002-04-22 Paul Kienzle
26 * Use warning(...) function rather than writing to cerr
27 2006-05-01 Tom Holroyd
28 * add support for consistent winding in all dimensions; output is
29 * guaranteed to be simplicial.
30 */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include <sstream>
37 
38 #include "Cell.h"
39 #include "defun-dld.h"
40 #include "error.h"
41 #include "oct-obj.h"
42 #include "parse.h"
43 #include "unwind-prot.h"
44 
45 #if defined (HAVE_QHULL)
46 # include "oct-qhull.h"
47 # if defined (NEED_QHULL_VERSION)
48 char qh_version[] = "convhulln.oct 2007-07-24";
49 # endif
50 #endif
51 
52 static void
53 close_fcn (FILE *f)
54 {
55  gnulib::fclose (f);
56 }
57 
58 static bool
60 {
61  if (sizeof (octave_idx_type) > sizeof (int))
62  {
63  int maxval = std::numeric_limits<int>::max ();
64 
65  if (dim > maxval || n > maxval)
66  {
67  error ("%s: dimension too large for Qhull", who);
68  return false;
69  }
70  }
71 
72  return true;
73 }
74 
75 DEFUN_DLD (convhulln, args, nargout,
76  "-*- texinfo -*-\n\
77 @deftypefn {Loadable Function} {@var{h} =} convhulln (@var{pts})\n\
78 @deftypefnx {Loadable Function} {@var{h} =} convhulln (@var{pts}, @var{options})\n\
79 @deftypefnx {Loadable Function} {[@var{h}, @var{v}] =} convhulln (@dots{})\n\
80 Compute the convex hull of the set of points @var{pts} which is a matrix\n\
81 of size [n, dim] containing n points in a space of dimension dim.\n\
82 The hull @var{h} is an index vector into the set of points and specifies\n\
83 which points form the enclosing hull.\n\
84 \n\
85 An optional second argument, which must be a string or cell array of strings,\n\
86 contains options passed to the underlying qhull command.\n\
87 See the documentation for the Qhull library for details\n\
88 @url{http://www.qhull.org/html/qh-quick.htm#options}.\n\
89 The default options depend on the dimension of the input:\n\
90 \n\
91 @itemize\n\
92 @item 2D, 3D, 4D: @var{options} = @code{@{\"Qt\"@}}\n\
93 \n\
94 @item 5D and higher: @var{options} = @code{@{\"Qt\", \"Qx\"@}}\n\
95 @end itemize\n\
96 \n\
97 If @var{options} is not present or @code{[]} then the default arguments are\n\
98 used. Otherwise, @var{options} replaces the default argument list.\n\
99 To append user options to the defaults it is necessary to repeat the\n\
100 default arguments in @var{options}. Use a null string to pass no arguments.\n\
101 \n\
102 If the second output @var{v} is requested the volume of the enclosing\n\
103 convex hull is calculated.\n\n\
104 @seealso{convhull, delaunayn, voronoin}\n\
105 @end deftypefn")
106 {
107  octave_value_list retval;
108 
109 #if defined (HAVE_QHULL)
110 
111  int nargin = args.length ();
112  if (nargin < 1 || nargin > 2)
113  {
114  print_usage ();
115  return retval;
116  }
117 
118  Matrix points (args(0).matrix_value ());
119  const octave_idx_type dim = points.columns ();
120  const octave_idx_type num_points = points.rows ();
121 
122  if (! octave_qhull_dims_ok (dim, num_points, "convhulln"))
123  return retval;
124 
125  points = points.transpose ();
126 
127  std::string options;
128 
129  if (dim <= 4)
130  options = " Qt";
131  else
132  options = " Qt Qx";
133 
134  if (nargin == 2)
135  {
136  if (args(1).is_string ())
137  options = " " + args(1).string_value ();
138  else if (args(1).is_empty ())
139  ; // Use default options.
140  else if (args(1).is_cellstr ())
141  {
142  options = "";
143 
144  Array<std::string> tmp = args(1).cellstr_value ();
145 
146  for (octave_idx_type i = 0; i < tmp.numel (); i++)
147  options += " " + tmp(i);
148  }
149  else
150  {
151  error ("convhulln: OPTIONS must be a string, cell array of strings, or empty");
152  return retval;
153  }
154  }
155 
156  boolT ismalloc = false;
157 
158  unwind_protect frame;
159 
160  // Replace the outfile pointer with stdout for debugging information.
161 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM)
162  FILE *outfile = gnulib::fopen ("NUL", "w");
163 #else
164  FILE *outfile = gnulib::fopen ("/dev/null", "w");
165 #endif
166  FILE *errfile = stderr;
167 
168  if (outfile)
169  frame.add_fcn (close_fcn, outfile);
170  else
171  {
172  error ("convhulln: unable to create temporary file for output");
173  return retval;
174  }
175 
176  // qh_new_qhull command and points arguments are not const...
177 
178  std::string cmd = "qhull" + options;
179 
180  OCTAVE_LOCAL_BUFFER (char, cmd_str, cmd.length () + 1);
181 
182  strcpy (cmd_str, cmd.c_str ());
183 
184  int exitcode = qh_new_qhull (dim, num_points, points.fortran_vec (),
185  ismalloc, cmd_str, outfile, errfile);
186  if (! exitcode)
187  {
188  bool nonsimp_seen = false;
189 
190  octave_idx_type nf = qh num_facets;
191 
192  Matrix idx (nf, dim + 1);
193 
194  facetT *facet;
195 
196  octave_idx_type i = 0;
197 
198  FORALLfacets
199  {
200  octave_idx_type j = 0;
201 
202  if (! (nonsimp_seen || facet->simplicial || qh hull_dim == 2))
203  {
204  nonsimp_seen = true;
205 
206  if (cmd.find ("QJ") != std::string::npos)
207  {
208  // Should never happen with QJ.
209  error ("convhulln: qhull failed: option 'QJ' returned non-simplicial facet");
210  return retval;
211  }
212  }
213 
214  if (dim == 3)
215  {
216  setT *vertices = qh_facet3vertex (facet);
217 
218  vertexT *vertex, **vertexp;
219 
220  FOREACHvertex_ (vertices)
221  idx(i, j++) = 1 + qh_pointid(vertex->point);
222 
223  qh_settempfree (&vertices);
224  }
225  else
226  {
227  if (facet->toporient ^ qh_ORIENTclock)
228  {
229  vertexT *vertex, **vertexp;
230 
231  FOREACHvertex_ (facet->vertices)
232  idx(i, j++) = 1 + qh_pointid(vertex->point);
233  }
234  else
235  {
236  vertexT *vertex, **vertexp;
237 
238  FOREACHvertexreverse12_ (facet->vertices)
239  idx(i, j++) = 1 + qh_pointid(vertex->point);
240  }
241  }
242  if (j < dim)
243  warning ("convhulln: facet %d only has %d 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 (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 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  else
290  error ("convhulln: qhull failed");
291 
292  // Free memory from Qhull
293  qh_freeqhull (! qh_ALL);
294 
295  int curlong, totlong;
296  qh_memfreeshort (&curlong, &totlong);
297 
298  if (curlong || totlong)
299  warning ("convhulln: did not free %d bytes of long memory (%d pieces)",
300  totlong, curlong);
301 
302 #else
303  error ("convhulln: not available in this version of Octave");
304 #endif
305 
306  return retval;
307 }
308 
309 /*
310 %!testif HAVE_QHULL
311 %! 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];
312 %! [h, v] = convhulln (cube, "Qt");
313 %! assert (size (h), [12 3]);
314 %! h = sortrows (sort (h, 2), [1:3]);
315 %! assert (h, [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]);
316 %! assert (v, 1, 10*eps);
317 %! [h2, v2] = convhulln (cube); % Test defaut option = "Qt"
318 %! assert (size (h2), size (h));
319 %! h2 = sortrows (sort (h2, 2), [1:3]);
320 %! assert (h2, h);
321 %! assert (v2, v, 10*eps);
322 
323 %!testif HAVE_QHULL
324 %! 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];
325 %! [h, v] = convhulln (cube, "QJ");
326 %! assert (size (h), [12 3]);
327 %! assert (sortrows (sort (h, 2), [1:3]), [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]);
328 %! assert (v, 1.0, 1e6*eps);
329 
330 %!testif HAVE_QHULL
331 %! tetrahedron = [1 1 1;-1 -1 1;-1 1 -1;1 -1 -1];
332 %! [h, v] = convhulln (tetrahedron);
333 %! h = sortrows (sort (h, 2), [1 2 3]);
334 %! assert (h, [1 2 3;1 2 4; 1 3 4; 2 3 4]);
335 %! assert (v, 8/3, 10*eps);
336 
337 %!testif HAVE_QHULL
338 %! triangle=[0 0; 1 1; 1 0; 1 2];
339 %! h = convhulln (triangle);
340 %! assert (size (h), [3 2]);
341 */