GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
__voronoi__.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/*
2720. Augiust 2000 - Kai Habel: first release
28*/
29
30/*
312003-12-14 Rafael Laboissiere <rafael@laboissiere.net>
32Added optional second argument to pass options to the underlying
33qhull command
34*/
35
36#if defined (HAVE_CONFIG_H)
37# include "config.h"
38#endif
39
40#include <cstdio>
41
42#include <limits>
43#include <string>
44
45#include "Array.h"
46#include "boolMatrix.h"
47#include "dMatrix.h"
48#include "dRowVector.h"
49#include "oct-locbuf.h"
50#include "unwind-prot.h"
51
52#include "Cell.h"
53#include "defun-dld.h"
54#include "error.h"
55#include "errwarn.h"
56#include "ov.h"
57#include "ovl.h"
58
59#if defined (HAVE_QHULL)
60# include "oct-qhull.h"
61# if defined (NEED_QHULL_R_VERSION)
62char qh_version[] = "__voronoi__.oct 2007-07-24";
63# endif
64#endif
65
67
68#if defined (HAVE_QHULL)
69
70static void
71free_qhull_memory (qhT *qh)
72{
73 qh_freeqhull (qh, ! qh_ALL);
74
75 int curlong, totlong;
76 qh_memfreeshort (qh, &curlong, &totlong);
77
78 if (curlong || totlong)
79 warning ("__voronoi__: did not free %d bytes of long memory (%d pieces)",
80 totlong, curlong);
81}
82
83static bool
84octave_qhull_dims_ok (octave_idx_type dim, octave_idx_type n, const char *who)
85{
86 if (sizeof (octave_idx_type) > sizeof (int))
87 {
88 int maxval = std::numeric_limits<int>::max ();
89
90 if (dim > maxval || n > maxval)
91 error ("%s: dimension too large for Qhull", who);
92 }
93
94 return true;
95}
96
97#endif
98
99DEFUN_DLD (__voronoi__, args, ,
100 doc: /* -*- texinfo -*-
101@deftypefn {} {@var{C}, @var{F} =} __voronoi__ (@var{caller}, @var{pts})
102@deftypefnx {} {@var{C}, @var{F} =} __voronoi__ (@var{caller}, @var{pts}, @var{options})
103@deftypefnx {} {@var{C}, @var{F}, @var{Inf_Pts} =} __voronoi__ (@dots{})
104Undocumented internal function.
105@end deftypefn */)
106{
107#if defined (HAVE_QHULL)
108
109 int nargin = args.length ();
110
111 if (nargin < 2 || nargin > 3)
112 print_usage ();
113
114 std::string caller = args(0).xstring_value ("__voronoi__: CALLER must be a string");
115
116 octave_value_list retval;
117
118 Matrix points = args(1).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, "__voronoi__"))
123 return ovl (0.0);
124
125 points = points.transpose ();
126
127 std::string options;
128
129 if (dim <= 3)
130 options = " Qbb";
131 else
132 options = " Qbb Qx";
133
134 if (nargin == 3)
135 {
136 octave_value opt_arg = args(2);
137
138 if (opt_arg.is_string ())
139 options = ' ' + opt_arg.string_value ();
140 else if (opt_arg.isempty ())
141 ; // Use default options.
142 else if (opt_arg.iscellstr ())
143 {
144 options = "";
145
146 Array<std::string> tmp = opt_arg.cellstr_value ();
147
148 for (octave_idx_type i = 0; i < tmp.numel (); i++)
149 options += ' ' + tmp(i);
150 }
151 else
152 error ("%s: OPTIONS must be a string, cell array of strings, or empty",
153 caller.c_str ());
154 }
155
156 boolT ismalloc = false;
157
158 // Set the outfile pointer to stdout for status information.
159 FILE *outfile = nullptr;
160 FILE *errfile = stderr;
161
162 qhT context = { };
163 qhT *qh = &context;
164
165 std::string cmd = "qhull v" + options;
166
167 int exitcode = qh_new_qhull (qh, dim, num_points, points.rwdata (),
168 ismalloc, &cmd[0], outfile, errfile);
169
170 unwind_action free_memory ([qh] () { free_qhull_memory (qh); });
171
172 if (exitcode)
173 error ("%s: qhull failed", caller.c_str ());
174
175 // Calling findgood_all provides the number of Voronoi vertices
176 // (sets qh->num_good).
177
178 qh_findgood_all (qh, qh->facet_list);
179
180 octave_idx_type num_voronoi_regions
181 = qh->num_vertices - qh_setsize (qh, qh->del_vertices);
182
183 octave_idx_type num_voronoi_vertices = qh->num_good;
184
185 // Find the voronoi centers for all facets.
186
187 qh_setvoronoi_all (qh);
188
189 facetT *facet;
190 vertexT *vertex;
192
193 // Find the number of Voronoi vertices for each Voronoi cell and
194 // store them in NI so we can use them later to set the dimensions
195 // of the RowVector objects used to collect them.
196
197 FORALLfacets
198 {
199 facet->seen = false;
200 }
201
202 OCTAVE_LOCAL_BUFFER (octave_idx_type, ni, num_voronoi_regions);
203 for (octave_idx_type i = 0; i < num_voronoi_regions; i++)
204 ni[i] = 0;
205
206 k = 0;
207
208 FORALLvertices
209 {
210 if (qh->hull_dim == 3)
211 qh_order_vertexneighbors (qh, vertex);
212
213 bool infinity_seen = false;
214
215 facetT *neighbor, * *neighborp;
216
217 FOREACHneighbor_ (vertex)
218 {
219 if (neighbor->upperdelaunay)
220 {
221 if (! infinity_seen)
222 {
223 infinity_seen = true;
224 ni[k]++;
225 }
226 }
227 else
228 {
229 neighbor->seen = true;
230 ni[k]++;
231 }
232 }
233
234 k++;
235 }
236
237 // If Qhull finds fewer regions than points, we will pad the end
238 // of the at_inf and C arrays so that they always contain at least
239 // as many elements as the given points array.
240
241 // FIXME: is it possible (or does it make sense) for
242 // num_voronoi_regions to ever be larger than num_points?
243
244 octave_idx_type nr = (num_points > num_voronoi_regions
245 ? num_points : num_voronoi_regions);
246
247 boolMatrix at_inf (nr, 1, false);
248
249 // The list of Voronoi vertices. The first element is always
250 // Inf.
251 Matrix F (num_voronoi_vertices+1, dim);
252
253 for (octave_idx_type d = 0; d < dim; d++)
254 F(0, d) = numeric_limits<double>::Inf ();
255
256 // The cell array of vectors of indices into F that represent the
257 // vertices of the Voronoi regions (cells).
258
259 Cell C (nr, 1);
260
261 // Now loop through the list of vertices again and store the
262 // coordinates of the Voronoi vertices and the lists of indices
263 // for the cells.
264
265 FORALLfacets
266 {
267 facet->seen = false;
268 }
269
270 octave_idx_type i = 0;
271 k = 0;
272
273 FORALLvertices
274 {
275 if (qh->hull_dim == 3)
276 qh_order_vertexneighbors (qh, vertex);
277
278 bool infinity_seen = false;
279
280 octave_idx_type idx = qh_pointid (qh, vertex->point);
281
282 octave_idx_type num_vertices = ni[k++];
283
284 // Qhull seems to sometimes produces regions with a single
285 // vertex. Is that a bug? How can a region have just one
286 // vertex? Let's skip it.
287
288 if (num_vertices == 1)
289 continue;
290
291 RowVector facet_list (num_vertices);
292
293 octave_idx_type m = 0;
294
295 facetT *neighbor, * *neighborp;
296
297 FOREACHneighbor_(vertex)
298 {
299 if (neighbor->upperdelaunay)
300 {
301 if (! infinity_seen)
302 {
303 infinity_seen = true;
304 facet_list(m++) = 1;
305 at_inf(idx) = true;
306 }
307 }
308 else
309 {
310 if (! neighbor->seen)
311 {
312 i++;
313 for (octave_idx_type d = 0; d < dim; d++)
314 F(i, d) = neighbor->center[d];
315
316 neighbor->seen = true;
317 neighbor->visitid = i;
318 }
319
320 facet_list(m++) = neighbor->visitid + 1;
321 }
322 }
323
324 C(idx) = facet_list;
325 }
326
327 retval = ovl (F, C, at_inf);
328
329 return retval;
330
331#else
332
333 octave_unused_parameter (args);
334
335 std::string caller
336 = (args.length () > 0
337 ? args(0).xstring_value ("__voronoi__: CALLER must be a string")
338 : "__voronoi__");
339
340 err_disabled_feature (caller, "Qhull");
341
342#endif
343}
344
345/*
346## No test needed for internal helper function.
347%!assert (1)
348*/
349
350OCTAVE_END_NAMESPACE(octave)
#define C(a, b)
Definition Faddeeva.cc:256
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
Definition Cell.h:41
Matrix transpose() const
Definition dMatrix.h:138
Array< std::string > cellstr_value() const
Definition ov.h:991
bool is_string() const
Definition ov.h:637
bool isempty() const
Definition ov.h:601
std::string string_value(bool force=false) const
Definition ov.h:983
bool iscellstr() const
Definition ov.h:607
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
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217