stripack_sys/ffi.rs
1use std::ffi::{c_double, c_int};
2
3unsafe extern "C" {
4 /**
5 Adds a node to a triangulation of the convex hull of nodes `1, ..., k-1`, producing a
6 triangulation of the convex hull of nodes `1, ..., k`.
7
8 The algorithm consists of the following steps: node `k` is located relative to the
9 triangulation ([trfind]), its index is added to the data structure ([intadd] or [bdyadd]), and a sequence of swaps ([swptst] and [swap]) are applied to the arcs opposite `k` so that all arcs incident on node `k` and opposite node `k` are locally optimal (statisfy the circumcircle test).
10
11 Thus, if a Delaunay triangulation of nodes `1` through `k-1` is input, a Delaunay
12 triangulation of nodes `1` through `k` will be output.
13
14 # Arguments
15
16 * `nst` - Input. The index of a node at which [trfind] begins its search. Search time depends on
17 the proximity of this node to `k`. If `nst < 1`, the search is begun at node `k-1`.
18
19 * `k` - Input. The nodal index (index for `x`, `y`, and `z`, and `lend`) of the new node
20 to be added. `4 <= k`.
21
22 * `x[k]`, `y[k]`, `z[k]` - Input. The coordinates of the nodes.
23 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[k]`, `lnew` - Input/Output. On input, the data
24 structure associated with the triangulation of nodes `1` to `k-1`. On output, the data has been
25 updated to include node `k`. The array lengths are assumed to be large enough to add node `k`.
26 Refer to [trmesh].
27 * `ier` - Output. Error indicator:
28 * `0` if no errors were encountered.
29 * `-1` if `k` is outside its valid range on input.
30 * `-2` if all nodes (including `k`) are collinear (lie on a common geodesic).
31 * `l` if nodes `l` and `k` coincide for some `l < k`
32 */
33 #[link_name = "addnod_"]
34 pub fn addnod(
35 nst: *const c_int,
36 k: *const c_int,
37 x: *const c_double,
38 y: *const c_double,
39 z: *const c_double,
40 list: *mut c_int,
41 lptr: *mut c_int,
42 lend: *mut c_int,
43 lnew: *mut c_int,
44 ier: *mut c_int,
45 );
46
47 /**
48 Computes the arc cosine function, with argument truncation.
49
50 If you call your system `acos` routine with an input argument that is outside the range `[-1.0,
51 1.0]` you may get an unpleasant surprise. This routine truncates arguments outside the range.
52 # Arguments
53
54 * `c` - Input. The argument
55
56 # Returns
57 * An angle whose cosine is `c`
58 */
59 #[link_name = "arc_cosine_"]
60 pub fn arc_cosine(c: *const c_double) -> c_double;
61
62 /**
63 Computes the area of a spherical triangle on the unit sphere.
64
65 # Arguments
66 * `v1[3]`, `v2[3]`, `v3[3]` - Input. The Cartesian coordinates of unit vectors (the three triangle
67 vertices in any order). These vectors, if nonzero, are implicitly scaled to have length `1`.
68
69 # Returns
70 The area of the spherical triangle defined by `v1`, `v2`, and `v3`, in the range `0` to
71 `2*PI` (the area of a hemisphere). `0` if and only if `v1`, `v2`, and `v3` lie in (or close to)
72 a plane containing the origin.
73
74 # Safety
75 - All pointers must be valid and properly aligned
76 - Arrays `v1`, `v2`, `v3` must have length == `3`
77 */
78 #[link_name = "areas_"]
79 pub fn areas(v1: *const c_double, v2: *const c_double, v3: *const c_double) -> c_double;
80
81 /**
82 Adds a boundary node to a triangulation.
83
84 This subroutine adds a boundary node to a triangulation of a set
85 of `kk - 1` points on the unit sphere. The data structure is
86 updated with the insertion of node `kk`, but no optimizaiton is
87 performed.
88
89 This routine is identical to the similarly named routine in
90 TRIPACK.
91
92 # Arguments
93
94 * `kk` - Input. The index of a node to be connected to the
95 sequence of all visible boundary nodes. 1 <= `kk` and
96 `kk` must not be equal to `i1` or `i2`.
97 * `i1` - Input. The first (rightmost as viewed from `kk`)
98 boundary node in the triangulation that is visible from
99 node `kk` (the line segment `kk-i1` intersects no arcs).
100 * `i2` - Input. The last (leftmost) boundary node that is visible
101 from node `kk`. `i1` and `i2` may be determined by [trfind].
102 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]`, `lnew` - Input/output. The
103 triangulation data structure created by [trmesh]. Nodes `i1` and `i2` must be included in the triangulation. On output, the data structure is updated with the addition of node `kk`. Node `kk` is connected to `i1`, `i2`, and all boundary nodes in between.
104 */
105 #[link_name = "bdyadd_"]
106 pub fn bdyadd(
107 kk: *const c_int,
108 i1: *const c_int,
109 i2: *const c_int,
110 list: *mut c_int,
111 lptr: *mut c_int,
112 lend: *mut c_int,
113 lnew: *mut c_int,
114 );
115
116 /**
117 Returns the boundary nodes of a triangulation.
118
119 Given a triangulation of `n` nodes on the
120 unit sphere created by [trmesh], this subroutine returns an array containing the indexes (if any) of the counterclockwise sequence of boundary nodes, that is, the nodes on the boundary of the convex hull of the set of nodes. The boundary is empty if the nodes do not lie in a single hemisphere. The numbers of boundary nodes, arcs, and triangles are also returned.
121
122 # Arguments
123
124 * `n` - Input. The number of nodes in the triangulation. `3 <= n`.
125 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input. The data structure
126 defining the triangulation, created by [trmesh].
127 * `nodes` - Output. The ordered sequence of `nb` boundary node indexes in the range `1` to `n`.
128 For safety, the dimension of `nodes` should be `n`.
129 * `nb` - Output. The number of boundary nodes.
130 `na`, `nt` - Output. The number of arcs and triangles, repectively, in the triangulation.
131 */
132 #[link_name = "bnodes_"]
133 pub fn bnodes(
134 n: *const c_int,
135 list: *const c_int,
136 lptr: *const c_int,
137 lend: *const c_int,
138 nodes: *mut c_int,
139 nb: *mut c_int,
140 na: *mut c_int,
141 nt: *mut c_int,
142 );
143
144 /**
145 Returns the circumcenter of a spherical triangle.
146
147 Returns the circumcenter of a spherical triangle on the unit sphere: the point on the
148 sphere surface that is equally distant from the three triangle vertices and lies in the same
149 hemisphere, where distance is taken to be arc-length on the sphere surface.
150
151 # Arguments
152 * `v1[3]`, `v2[3]`, `v3[3]` - Input. The coordinates of the three triangle vertices (unit
153 vectors) in counterclockwise order.
154 * `c[3]` - Output. The coordinates of the circumcenter unless `0 < ier`, which in case `c` is
155 not defined. `c = (v2 - v1) X (v3 - v1)` normalized to a unit vector.
156 * `ier` - Output. Error indicator:
157 * `0`, if no errors were encountered.
158 * `1`, if `v1`, `v2`, and `v3` lie on a common line: `(v2 - v1) X (v3 - v1) = 0`.
159 */
160 #[link_name = "circum_"]
161 pub fn circum(
162 v1: *const c_double,
163 v2: *const c_double,
164 v3: *const c_double,
165 c: *mut c_double,
166 ier: *mut c_int,
167 );
168
169 /**
170 Connects an exterior node to boundary nodes, covering the sphere.
171
172 This subroutine connects an exterior node `kk` to all boundary nodes of a triangulation of `kk - 1` points on the unit sphere, producing a triangulation that covers the sphere. The data structure is updated with the addition of node `kk`, but no optimization is performed. All boundary nodes must be visible from node `kk`.
173
174 # Arguments
175
176 * `kk` - Input. Index of the node to be connected to the set of all boundary nodes. `4 <= kk`.
177 * `n0` - Input. Index of a boundary node (in the range `1` to `kk - 1`). `n0` may be
178 determined by [trfind].
179 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]`, `lnew` - Input/output. The
180 triangulation data structure created by [trmesh]. Node `n0` must be included in the triangulation. On output, updated with the addition of node `kk` as the last entry. The updated triangulation contains no boundary nodes.
181 */
182 #[link_name = "covsph_"]
183 pub fn covsph(
184 kk: *const c_int,
185 n0: *const c_int,
186 list: *mut c_int,
187 lptr: *mut c_int,
188 lend: *mut c_int,
189 lnew: *mut c_int,
190 );
191
192 /**
193 Returns triangle circumcenters and other information.
194
195 Given a Delaunay triangulation of nodes on the surface of the unit sphere, this subroutine returns the set of triangle circumcenters corresponding to Voronoi vertices, along with the circumradii and a list of triangle indexes `listc` stored in one-to-one correspondence with `list/lptr` entries.
196
197 A triangle circumcenter is the point (unit vector) lying at the same angular distance from the three vertices and contained in the same hemisphere as the vertices. (Note that the negative of a circumcenter is also equidistant from the vertices.) If the triangulation covers the surface, the Voronoi vertices are the circumenters of the triangles in the Delaunay triangulation. `lptr`, `lend`, and `lnew` are not altered in this case.
198
199 On the other hand, if the nodes are contained in a single hemisphere, the triangulation is implicitly extended to the entire surface by adding pseudo-arcs (of length greater than 180 degrees) between boundary nodes forming pseudo-triangles whose 'circumcenters' are included in the list. This extension to the triangulation actually consists of a triangulation of the set of boundary nodes in which the swap test is reversed (a non-empty circumcircle test). The negative circumcenters are stored as the pseudo-triangle 'circumcenters'. `listc`, `lptr`, `lend`, and `lnew` contain a data structure corresponding to the extended triangulation (Voronoi diagram), but `list` is not altered in this case. Thus, if it is necessary to retain the original (unextended) triangulation data structure, copies of `lptr` and `lnew` must be saved before calling this routine.
200
201 # Arguments
202
203 * `n` - Input. The number of nodes in the triangulation `3 <= n`. Note that, if `n = 3`,
204 there are only two Voronoi vertices separated by 180 degrees, and the Voronoi regions are not well defined.
205 * `ncol` - Input. The number of columns reserved for `ltri`. This must be at least `nb - 2`,
206 where `nb` is the number of boundary nodes.
207 * `x[n]`, `y[n]`, `z[n]` - Input. The coordinates of the nodes (unit vectors).
208 * `list[6 * (n - 2)]` - Input. The set of adjacency lists. Refer to [trmesh].
209 * `lend[n]` - Input. The set of pointers to ends of adjacency lists. Refer to [trmesh].
210 * `lptr[6 * (n - 2)]` - Input/output. Pointers associated with `list`. Refer to [trmesh]. On
211 output, pointers associated with `listc`. Updated for the addition of pseudo-triangles if the original triangulation contains boundary nodes (`0 < nb`).
212 * `lnew` - Input/output. On input, a pointer to the first empty location in `list` and `lptr` (list length plus one). On output, pointer to the first empty location in `listc` and `lptr` (list length plus one). `lnew` is not altered if `nb = 0`.
213 * `ltri[6][ncol]` - Output. Triangle list whose first `nb - 2` columns contain the indexes
214 of a clockwise-ordered sequence of vertices (first three rows) followed by the `ltri` column indexes of the triangles opposite the vertices (or 0 denoting the exterior region) in the last three rows. This array is not generally of any further use outside this routine.
215 * `listc[3 * nt]` - Output. Where `nt = 2 * n - 4` is the number of triangles in the
216 triangulation (after extending it to cover the entire surface if necessary). Contains the triangle indexes (indexes to `xc`, `yc`, `zc`, and `rc`) stored in 1-1 correspondence with `list`/`lptr` entries (or entries that would be stored in `list` for the extended triangulation): the index of triangle (`n1`, `n2`, `n3`) is stored in `listc[k]`, `listc[l]`, and `listc[m]`, where `list[k]`, `list[l]`, and `list[m]` are the indexes of `n2` as a neighbor of `n1`, `n3` as a neighbor of `n2`, and `n1` as a neighbor of `n3`. The Voronoi region associated with a node is defined by the CCW-ordered sequence of circumcenters in one-to-one correspondence with its adjacency list (in the extended triangulation).
217 * `nb` - Output. The number of boundary nodes unless `ier = 1`.
218 * `xc[2 * n - 4]`, `yc[2 * n - 4]`, `zc[2 * n - 4]`, the coordinates of the triangle
219 circumcenters (Voronoi vertices). `xc[i]**2 + yc[i]**2 + zc[i]**2 = 1`. The first `nb - 2`
220 entries correspond to pseudo-triangles if `0 < nb`.
221 * `rc[2 * n - 4]` - Output. The circumradii (the arc lengths or angles between the
222 circumcenters and associated triangle vertices) in 1-1 correspondence with circumcenters.
223 * `ier` - Output. Error indicator:
224 * `0`, if no errors were encountered.
225 * `1`, if `n < 3`.
226 * `2`, if `ncol < nb - 2`.
227 * `3`, if a triangle is degenerate (has vertices lying on a common geodesic).
228 */
229 #[link_name = "crlist_"]
230 pub fn crlist(
231 n: *const c_int,
232 ncol: *const c_int,
233 x: *const c_double,
234 y: *const c_double,
235 z: *const c_double,
236 list: *const c_int,
237 lend: *const c_int,
238 lptr: *mut c_int,
239 lnew: *mut c_int,
240 ltri: *mut c_int,
241 listc: *mut c_int,
242 nb: *mut c_int,
243 xc: *mut c_double,
244 yc: *mut c_double,
245 zc: *mut c_double,
246 rc: *mut c_double,
247 ier: *mut c_int,
248 );
249
250 /**
251 Deletes a boundary arc from a triangulation.
252
253 This subroutine deletes a boundary arc from a triangulation. It may be used to remove a null triangle from the convex hull boundary. Note, however, that if the union of triangles is rendered nonconvex, subroutines [delnod], [edge], and [trfind] (and hence [addnod]) may fail. Also, function [nearnd] should not be called following an arc deletion.
254
255 This routine is identical to the similarly named routine in TRIPACK.
256
257 # Arguments
258
259 * `n` - Input. The number of nodes in the triangulation. `4 <= n`.
260 * `io1`, `io2` - Input. Indexes (in the range of `1` to `n`) of a pair of adjacent boundary
261 nodes defining the arc to be removed.
262 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]`, `lnew` - Input/output. The triangulation
263 data structure created by [trmesh]. On output, updated with the removal of arc `io1-io2` unless `0 < ier`.
264 * `ier` - Output. Error indicator:
265 * `0`, if no errors were encountered.
266 * `1`, if `n`, `io1`, or `io2` is outside its valid range, or `io1 = io2`.
267 * `2`, if `io1-io2` is not a boundary arc.
268 * `3`, if the node opposite `io1-io2` is already a boundary node, and thus `io1` or `io2` has only two neighbors or a deletion would result in two triangulations sharing a single node.
269 * `4`, if one of the nodes is a neighbor of the other, but not vice versa, implying an invalid triangulation data structure.
270 */
271 #[link_name = "delarc_"]
272 pub fn delarc(
273 n: *const c_int,
274 io1: *const c_int,
275 io2: *const c_int,
276 list: *mut c_int,
277 lptr: *mut c_int,
278 lend: *mut c_int,
279 lnew: *mut c_int,
280 ier: *mut c_int,
281 );
282
283 /**
284 Deletes a neighbor from the adjacency list.
285
286 This subroutine deletes a neighbor `nb` from the adjacency list of node `n0` (but `n0` is not deleted from the adjacency list of `nb`) and, if `nb` is a boundary node, makes `n0` a boundary node.
287
288 For pointer (`list` index) `lph` to `nb` as a neighbor of `n0`, the empty `list`, `lptr` location `lph` is filled in with the values at `lnew - 1`, pointer `lnew - 1` (in `lptr` and possibly in `lend`) is changed to `lph`, and `lnew` is decremented.
289
290 This requires a search of `lend` and `lptr` entailing an expected operation count of O(n).
291
292 This routine is identical to the similarly named routine in TRIPACK.
293
294 # Arguments
295
296 * `n0`, `nb` - Input. Indexes, in the range `1` to `n`, of a pair of nodes such that `nb` is
297 a neighbor of `n0`. (`n0` need not be a neighbor of `nb`.)
298 * `n` - Input. The number of nodes in the triangulation. `3 <= n`.
299 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]`, `lnew` - Input/Output. The data structure defining
300 the triangulation. On output, updated with the removal of `nb` from the adjacency list of
301 `n0` unless `lph < 0`.
302 * `lph` - Input/output. List pointer to the hole (`nb` as a neighbor of `n0`) filled in by
303 the values at `lnew - 1` or error indicator:
304 `> 0`, if no errors were encountered.
305 `-1`, if `n0`, `nb`, or `n` is outside its valid range.
306 `-2`, if `nb` is not a neighbor of `n0`.
307 */
308 #[link_name = "delnb_"]
309 pub fn delnb(
310 n0: *const c_int,
311 nb: *const c_int,
312 n: *const c_int,
313 list: *mut c_int,
314 lptr: *mut c_int,
315 lend: *mut c_int,
316 lnew: *mut c_int,
317 lph: *mut c_int,
318 );
319
320 /**
321 Deletes a node from a triangulation.
322
323 This subroutine deletes node `k` (along with all arcs incident on node `k`) from a triangulation of `n` nodes on the unit sphere, and inserts arcs as necessary to produce a triangulation of the remaining `n - 1` nodes. If a Delaunay triangulation is input, a Delaunay triangulation will result, and thus, [delnod] reverses the effect of a call to [addnod].
324
325 Note that the deletion may result in all remaining nodes being collinear. This situation is not flagged.
326
327 # Arguments
328
329 * `k` - Input. The index (for `x`, `y`, and `z`) of the node to be deleted. `1 <= k <= n`.
330 * `n` - Input/output. The number of nodes in the triangulation. `4 <= n`. Note that `n` will
331 be decremented following the deletion.
332 * `x[n]`, `y[n]`, `z[n]` - Input/output. The coordinates of the nodes in the triangulation.
333 On output, updated with elements `k + 1, ..., n + 1` shifted up one position, thus overwriting element `k`, unless `1 <= ier <= 4`.
334 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]`, `lnew` - Input/output. The data
335 structure defining the triangulation, created by [trmesh]. On output, updated to reflect the deletion unless `1 <= ier <= 4`. Note that the data structure may have been altered if `3 < ier`.
336 * `lwk` - Input/output. The number of columns reserved for `iwk`. `lkw` must be at least
337 `nnb-3`, where `nnb` is the number of neighbors of node `k`, including an extra pseudo-node if `k` is a boundary node. On output, the number of `iwk` columns required unless `ier = 1` or `ier = 3`.
338 * `iwk[2][lwk]` - Output. The indexes of the endpoints of the new arcs added unless `lwk = 0` or `1 <= ier <= 4`. (Arcs are associated with columns.)
339 * `ier` - Output. Error indicator:
340 * `0`, if no errors were encountered
341 * `1`, if `k` or `n` is outside its valid range or `lwk < 0` on input.
342 * `2`, if more space is required in `iwk`. Refer to `lwk`.
343 * `3`, if the triangulation data structure is invalid on input.
344 * `4`, if `k` indexes an interior node with four or more neighbors, none of which can be swapped out due to collinearity, and `k` cannot therefore be deleted.
345 * `5`, if an error flag (other than `ier = 1`) was returned by [optim]. An error message is written to the standard output unit in this case.
346 * `6`, if error flag `1` was returned by [optim]. This is not necessarily an error, but the arcs may not be optimal.
347 */
348 #[link_name = "delnod_"]
349 pub fn delnod(
350 k: *const c_int,
351 n: *const c_int,
352 x: *const c_double,
353 y: *const c_double,
354 z: *const c_double,
355 list: *mut c_int,
356 lptr: *mut c_int,
357 lend: *mut c_int,
358 lnew: *mut c_int,
359 lwk: *mut c_int,
360 iwk: *mut c_int,
361 ier: *mut c_int,
362 );
363
364 /**
365 Swaps arcs to force two nodes to be adjacent.
366
367 Given a triangulation of `n` nodes and a pair of nodal indexes `in1` and `in2`, this routine
368 swaps arcs as necessary to force `in1` and `in2` to be adjacent. Only arcs which intersect `in1
369 -in2` are swapped out. If a Delaunay triangulation is input, the resulting triangulation is as
370 close as possible to a Delaunay triangulation in the sense that all arcs other than `in1-in2`
371 are locally optimal.
372
373 A sequence of calls to [edge] may be used to force the presence of a set of edges defining the
374 boundary of a non-convex and/or multiply connected region, or to introduce barriers into the
375 triangulation. Note that [getnp] will not necessarily return closest nodes if the triangulation
376 has been constrained by a call to [edge]. However, this is appropriate in some applications,
377 such as triangle-based interpolation on a nonconvex domain.
378
379 # Arguments
380
381 * `in1`, `in2` - Input. The indexes (of `x`, `y`, and `z`) in the range `1` to `n` defining a pair of
382 nodes to be connected by an arc.
383 * `x[n]`, `y[n]`, `z[n]` - Input. The coordinates of the nodes.
384 * `lwk` - Input/output. On input, the number of columns reserved for `iwk`. This must be at
385 least `ni`, the number of arcs that intersect `in1-in2`. (`ni` is bounded by `n - 3`.) On
386 output, the number of arcs which intersect `in1-in2` (but not more than the input value of `lwk`) unless `ier = 1` or `ier = 3`. `lwk = 0` if and only if `in1` and `in2` were adjacent (or `lwk = 0`) on input.
387 * `iwk[2 * lwk]` - Output. The indexes of the endpoints of the new arcs other than `in1-in2`
388 unless `0 < ier` or `lwk = 0`. New arcs to the left of `in1->in2` are stored in the first `k-1`
389 columns (left portion of `iwk`), column `k` contains zeros, and new arcs to the right of
390 `in1->in2` occupy columns `k + 1, ..., lwk`. (`k` can be determined by searching `iwk` for the
391 zeros.)
392 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input/output. The data structure
393 defining the triangulation, created by [trmesh]. On output, updated if necessary to refelct the
394 presence of an arc connecting `in1` and `in2` unless `0 < ier`. The data structure has been
395 altered if `4 <= ier`.
396 * `ier` - Output. Error indicator:
397 * `0`, if no errors were encountered
398 * `1`, if `in1 < 1`, `in2 < 1`, `in1 = in2`, or `lwk < 0` on input.
399 * `2`, if more space is required in `iwk`. Refer to `lwk`.
400 * `3`, if `in1` and `in2` could not be connected due to either an invalid data structure or
401 collinear nodes (and floating point error).
402 * `4`, if an error flag other than `ier = 1` was returned by [optim]
403 * `5`, if error flag `1` was returned by [optim]. This is not necessarily an error, but the arcs
404 other than `in1-in2` may not be optimal
405 */
406 #[link_name = "edge_"]
407 pub fn edge(
408 in1: *const c_int,
409 in2: *const c_int,
410 x: *const c_double,
411 y: *const c_double,
412 z: *const c_double,
413 lwk: *mut c_int,
414 iwk: *mut c_int,
415 list: *mut c_int,
416 lptr: *mut c_int,
417 lend: *mut c_int,
418 ier: *mut c_int,
419 );
420
421 /**
422 Gets the next nearest node to a given node.
423
424 Given a Delaunay triangulation of `n` nodes on the unit sphere and an array `npts` containing the indexes of `l-1` nodes ordered by angular distance from `npts[1]`, this routine sets `npts[l]` to the index of the next node in the sequence -- the node, other than `npts[1], ..., npts[l - 1]`, that is closest to `npts[1]`. Thus, the ordered sequence of `k` closest nodes to `n1` (including `n1`) may be determined by `k - 1` calls to `getnp` with `npts[1] = n1` and `l = 2,3,..., k` for `k >= 2`.
425
426 The algorithm uses the property of a Delaunay triangulation that the `k`-th closest node to `n1` is a neighbor of one of the `k-1` closest nodes to `n1`.
427
428 # Arguments
429
430 * `x[n]`, `y[n]`, `z[n]` - Input. The coordinates of the nodes.
431 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input. The triangulation data
432 structure, created by [trmesh].
433 * `l` - Input. The number of nodes in the sequence on output. `2 <= l <= n`.
434 * `npts[l]` - Input/output. On input, the indexes of the `l - 1` closest nodes to `npts[1]`
435 in the first `l - 1` locations. On output, updated with the index of the `l`-th closest
436 node to `npts[1]` in position `l` unless `ier = 1`.
437 * `df` - Output. The value of an increasing function (negative cosine) of the angular
438 distance between `npts[1]` and `npts[l]` unless `ier = 1`.
439 * `ier` - Output. Error indicator:
440 `0`, if no errors were encountered.
441 `1`, if `l < 2`.
442 */
443 #[link_name = "getnp_"]
444 pub fn getnp(
445 x: *const c_double,
446 y: *const c_double,
447 z: *const c_double,
448 list: *const c_int,
449 lptr: *const c_int,
450 lend: *const c_int,
451 l: *const c_int,
452 npts: *mut c_int,
453 df: *mut c_double,
454 ier: *mut c_int,
455 );
456
457 /**
458 Inserts `k` as a neighbor of `n1`.
459
460 This subroutine inserts `k` as a neighbor of `n1` following `n2`,
461 where `lp` is the `list` pointer of `n2` as a neighbor of `n1`.
462 Note that, if `n2` is the last neighbor of `n1`, K will become
463 the first neighbor (even if `n1` is a boundary node).
464
465 This routine is identical to the similarly named routine in
466 TRIPACK.
467
468 # Arguments
469
470 * `k` - Input. The index of the node to be inserted.
471 * `lp` - Input. The `list` pointer of `n2` as a neighbor of `n1`.
472 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lnew` - Input/Output.
473 The data structure defining the triangulation, created by [trmesh]. On output, updated with the addition of node `k`
474 */
475 #[link_name = "insert_"]
476 pub fn insert(
477 k: *const c_int,
478 lp: *const c_int,
479 list: *mut c_int,
480 lptr: *mut c_int,
481 lnew: *mut c_int,
482 );
483
484 /**
485 Determines if a point is inside a polygonal region.
486
487 This function locates a point `p` relative to a polygonal region `r` on the surface of the unit sphere, returning `inside = true` if and only if `p` is contained in `r`. `r` is defined by a cyclically ordered sequence of vertices which form a positively-oriented simple closed curve. Adjacent vertices need not be distinct but the curve must not be self-intersecting. Also, while polygon edges are by definition restricted to a single hemisphere, `r` is not so restricted. Its interior is the region to the left as the vertices are traversed in order.
488
489 The algorithm consists of selecting a point `q` in `r` and then finding all points at which the great circle defined by `p` and `q` intersects the boundary of `r`. `p` lies inside `r` if and only if there is an even number of intersection points between `q` and `p`. `q` is taken to be a point immediately to the left of a directed boundary edge -- the first one that results in no consistency-check failures.
490
491 If `p` is close to the polygon boundary, the problem is ill-conditioned and the decision may be incorrect. Also, an incorrect decision may result from a poor choice of `q` (if, for example, a boundary edge lies on the great circle defined by `p` and `q`). A more reliable result could be obtained by a sequence of calls to [inside] with the vertices cyclically permuted before each call (to alter the choice of `q`).
492
493 # Arguments
494
495 * `p[3]` - Input. The coordinates of the point (unit vector) to be located.
496 * `lv` - Input. The length of the arrays `xv`, `yv`, and `zv`.
497 * `xv[lv]`, `yv[lv]`, `zv[lv]` - Input. The coordinates of unit vectors (points on the unit sphere).
498 * `nv` - Input. The number of vertices in the polygon. `3 <= nv <= lv`.
499 * `listv[nv]` - Input. The indexes (for `xv`, `yv`, and `zv`) of a cyclically-ordered (and
500 CCW-ordered) sequence of vertices that define `r`. The last vertex (indexed by `listv[nv]`) is followed by the first (indexed by `listv[1]`). `listv` entries must be in the range `1` to `lv`.
501 * `ier` - Output. Error indicator:
502 * `0`, if no errors were encountered.
503 * `1`, if `lv` or `nv` is outside its valid range.
504 * `2`, if a `listv` entry is outside its valid range.
505 * `3`, if the polygon boundary was found to be self-intersecting. This error will not necessarily be detected.
506 * `4`, if every choice of `q` (one for each boundary edge) led to failure of some internal consistency check. The most likely cause of this error is invalid input: `p = (0, 0, 0)`, a null or self-intersecting polygon, etc.
507
508 # Returns
509
510 True if and only if `p` lies inside `r` unless `ier != 0`, in which case the value is not altered.
511
512 */
513 #[link_name = "inside_"]
514 pub fn inside(
515 p: *const c_double,
516 lv: *const c_int,
517 xv: *const c_double,
518 yv: *const c_double,
519 zv: *const c_double,
520 nv: *const c_int,
521 listv: *const c_int,
522 ier: *mut c_int,
523 ) -> bool;
524
525 /**
526 Adds an interior node to a triangulation.
527
528 This subroutine adds an interior node to a triangulation of a set of points on the unit sphere. The data structure is updated with the insertion of node `kk` into the triangle whose vertices are `i1`, `i2`, and `i3`. No optimization of the triangulation is performed.
529
530 This routine is identical to the similarly named routine in TRIPACK.
531
532 # Arguments
533
534 * `kk` - Input. The index of the node to be inserted. `1 <= kk` and `kk` must not be equal
535 to `i1`, `i2`, or `i3`.
536 * `i1`, `i2`, `i3` - Input. Indexes of the counterclockwise-orderd sequence of vertices of a
537 triangle which contains node `kk`.
538 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]`, `lnew` - Input/output. The data structure
539 defining the triangulation, created by [trmesh]. Triangle (`i1`, `i2`, `i3`) must be included in the triangulation. On output, updated with the addition of node `kk`. `kk` will be connected to nodes `i1`, `i2`, and `i3`.
540 */
541 #[link_name = "intadd_"]
542 pub fn intadd(
543 kk: *const c_int,
544 i1: *const c_int,
545 i2: *const c_int,
546 i3: *const c_int,
547 list: *mut c_int,
548 lptr: *mut c_int,
549 lend: *mut c_int,
550 lnew: *mut c_int,
551 );
552
553 /**
554 Finds the intersection of two great circles.
555
556 Given a great circle `c` and points `p1` and `p2` defining an arc `a` on the surface of the unit sphere, where `a` is the shorter of the two portions of the great circle `c12` associated with `p1` and `p2`, this subroutine returns the point of intersection `p` between `c` and `c12` that is closer to `a`. Thus, if `p1` and `p2` lie in opposite hemispheres defined by `c`, `p` is the point of intersection of `c` with `a`.
557
558 # Arguments
559
560 * `p1[3]`, `p2[3]` - Input. The coordinates of unit vectors.
561 * `cn[3]` - Input. The coordinates of a nonzero vector which defines `c` as the intersection
562 of the plane whose normal is `cn` with the unit sphere. Thus, if `c` is to be the great
563 circle defined by `p` and `q`, `cn` should be `p X q`.
564 * `p` - Output. Point of intersection defined above unless `ier` is not 0, in which case `p`
565 is not altered.
566 * `ier` - Output. Error indicator:
567 * `0`, if no errors were encountered.
568 * `1`, if `<cn, p1> = <cn, p2>`. This occurs if and only if `p1 = p2` or `cn = 0` or there are two intersection points at the same distance from `a`.
569 * `2` if `p2 = -p1` and the definition of `a` is therefore ambiguous.
570 */
571 #[link_name = "intrsc_"]
572 pub fn intrsc(
573 p1: *const c_double,
574 p2: *const c_double,
575 cn: *const c_double,
576 p: *mut c_double,
577 ier: *mut c_int,
578 );
579
580 /**
581 Returns a random integer between `1` and `n`.
582
583 This function returns a uniformly distributed pseudorandom integer in the range `1` to `n`.
584
585 # Arguments
586
587 * `n` - Input. The maximum value to be returned.
588 * `ix`, `iy`, `iz` - Input/output. The Seeds initialized to values in the range `1` to
589 `30,000` before the first call to `jrand`, and not altered between subsequent calls (unless a sequence of random numbers is to be repeated by reinitializing the seeds).
590
591 # Returns
592 A random integer in the range `1` to `n`.
593 */
594 #[link_name = "jrand_"]
595 pub fn jrand(n: *const c_int, ix: *mut c_int, iy: *mut c_int, iz: *mut c_int) -> c_int;
596
597 /**
598 Determines whether a node is left of a plane through the origin.
599
600 This function determines whether node `n0` is in the (closed) left hemisphere defined by the
601 plane containing `n1`, `n2`, and the origin, where left is defined relative to an observer at
602 `n1` facing `n2`.
603
604 # Arguments
605
606 * `x1`, `y1`, `z1` - Input. The coordinates of `n1`.
607 * `x2`, `y2`, `z2` - Input. The coordinates of `n2`.
608 * `x0`, `y0`, `z0` - Input. The coordinates of `n0`.
609
610 # Returns
611 True if and only if `n0` is in the closed left hemisphere.
612 */
613 #[link_name = "left_"]
614 pub fn left(
615 x1: *const c_double,
616 y1: *const c_double,
617 z1: *const c_double,
618 x2: *const c_double,
619 y2: *const c_double,
620 z2: *const c_double,
621 x0: *const c_double,
622 y0: *const c_double,
623 z0: *const c_double,
624 ) -> bool;
625
626 /**
627 Returns the index of `nb` in the adjacency list.
628
629 This function returns the index (`list` pointer) of `nb` in the adjacency list for `n0`, where `lpl = lend[n0]`. This function is identical to the similarly named function in TRIPACK.
630
631 # Arguments
632 * `lpl` - Input. Is `lend[n0]`
633 * `nb` - Input. The index of the node whose pointer is to be returned. `nb` must be connected to
634 `n0`.
635 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]` - Input. The data structure defining the
636 triangulation, created by [trmesh].
637
638 # Returns
639 Pointer `lstptr` such that `list[lstptr] = nb` or `list[lstptr] = -nb`, unless `nb`
640 is not a neighbor of `n0`, in which case `lstptr = lpl`.
641 */
642 #[link_name = "lstptr_"]
643 pub fn lstptr(
644 lpl: *const c_int,
645 nb: *const c_int,
646 list: *const c_int,
647 lptr: *const c_int,
648 ) -> c_int;
649
650 /**
651 Returns the number of neighbors of a node.
652
653 This function returns the number of neighbors of a node `n0` in a triangulation created by [trmesh].
654
655 The number of neighbors also gives the order of the Voronoi polygon containing the point. Thus, a neighbor count of `6` means the node is contained in a `6`-sided Voronoi region.
656
657 This function is identical to the similarly named function in TRIPACK.
658
659 # Arguments
660
661 * `lpl` - Input. Pointer to the last neighbor of `n0`.
662 * `lptr[6 * (n - 2)]` - Input. Pointers associated with `list`.
663
664 # Returns
665
666 The number of neighbors of `n0`.
667 */
668 #[link_name = "nbcnt_"]
669 pub fn nbcnt(lpl: *const c_int, lptr: *const c_int) -> c_int;
670
671 /**
672 Returns the nearest node to a given point.
673
674 Given a point `p` on the surface of the unit sphere and a Delaunay triangulation created by [trmesh], this function returns the index of the nearest triangulation node to `p`.
675
676 The algorithm consists of implicitly adding `p` to the triangulation, finding the nearest neighbor to `p`, and implicitly deleting `p` from the triangulation. Thus, it is based on the fact that, if `p` is a node in a Delaunay triangulation, the nearest node to `p` is a neighbor of `p`.
677
678 For large values of `n`, this procedure will be faster than the naive approach of computing the distance from `p` to every node.
679
680 Note that the number of candidates for [nearnd] (neighbors of `p`) is limited to `lmax` defined in the arguments below.
681
682 # Arguments
683
684 * `p[3]` - Input. The Cartesian coordinates of the point `p` to be located relative to the
685 * triangulation. It is assumed that `p[0]**2 + p[1]**2 + p[2]**2 = 1`, that is, that the
686 point lies on the unit sphere.
687 * `ist` - Input. The index of the node at which the search is to begin. The search time
688 * depends on the proximity of this node to `p`. If no good candidate is known, any value
689 between `1` and `n` will do.
690 * `n` - Input. The number of nodes in the triangulation. `3 <= n`.
691 * `x[n]`, `y[n]`, `z[n]` - Input. The Cartesian coordinates of the nodes.
692 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input. The data structure defining
693 the triangulation, created by [trmesh].
694 * `al` - Output. The arc length between `p` and node [nearnd]. Because both points are on
695 the unit sphere, this is also the angular separation in radians.
696
697 # Returns
698 The index of the nearest node to `p`. Will be `0` if `n < 3` or the triangulation data structure is invalid.
699 */
700 #[link_name = "nearnd_"]
701 pub fn nearnd(
702 p: *const c_double,
703 ist: *const c_int,
704 n: *const c_int,
705 x: *const c_double,
706 y: *const c_double,
707 z: *const c_double,
708 list: *const c_int,
709 lptr: *const c_int,
710 lend: *const c_int,
711 al: *mut c_double,
712 ) -> c_int;
713
714 /**
715 Optimizes the quadrilateral portion of a triangulation.
716
717 Given a set of `na` triangulation arcs, this subroutine optimizes the portion of the
718 triangulation consisting of the quadrilaterals (pairs of adjacent triangles) which have the arcs
719 as diagonals by applying the circumcircle test and appropriate swaps to the arcs.
720
721 An iteration consists of applying the swap test and swaps to all `na` arcs in the order in which
722 they are stored. The iteration is repeated until no swap occurs or `nit` iterations have
723 been performed. The bound on the number of iterations may be necessary to prevent an infinite
724 loop caused by cycling (reversing the effect of a previous swap) due to floating point
725 inaccuracy when four or more nodes are nearly cocircular.
726
727 # Arguments
728
729 * `x[*]`, `y[*]`, `z[*]` - Input. The nodal coordinates.
730 * `na` - Input. The number of arcs in the set. `na >= 0`.
731 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input/output. The data structure
732 defining the triangulation, created by [trmesh]. On output, updated to reflect the swaps.
733 * `nit` - Input/output. On input, the maximum number of iterations to be performed. `nit = 4 *
734 na` should be sufficient. `nit >= 1`. On output, the number of iterations performed.
735 * `iwk[2][na]` - Input/output. The nodal indexes of the arc endpoints (paris of endpoints are
736 stored in columns). On output, endpoint indexes of the new set of arcs reflecting the swaps.
737 * `ier` - Output. Error indicator:
738 * `0`, if no errors were encountered.
739 * `1`, if a swap occurred on the last of `maxit` iterations, where `maxit` is the value of
740 `nit` on input. The new set of arcs is not necessarily optimal in this case.
741 * `2`, if `na < 0` or `nit < 1` on input
742 * `3`, if `iwk[2][i]` is not a neighbor of `iwk[1][i]` for some `i` in the range `1` to `na`. A
743 swap may have occurred in this case.
744 * `4`, if a zero pointer was returned by subroutine [swap].
745 */
746 #[link_name = "optim_"]
747 pub fn optim(
748 x: *const c_double,
749 y: *const c_double,
750 z: *const c_double,
751 na: *const c_int,
752 list: *mut c_int,
753 lptr: *mut c_int,
754 lend: *mut c_int,
755 nit: *mut c_int,
756 iwk: *mut c_int,
757 ier: *mut c_int,
758 );
759
760 /**
761 Normalizes each R83 in an R83Vec to have unit norm.
762
763 # Arguments
764
765 * `n` - Input. The number of nodes in the triangulation.
766 * `x[n]`, `y[n]`, `z[n]` - Input/output. On input, the unnormalized coordinates of the
767 triangulation. On output, the normalized coordinates of the triangulation (unit vectors).
768 */
769 #[link_name = "r83vec_normalize_"]
770 pub fn r83vec_normalize(n: *const c_int, x: *mut c_double, y: *mut c_double, z: *mut c_double);
771
772 /**
773 Converts from Cartesian to spherical coordinates (latitude, longitude, radius).
774
775 # Arguments
776 * `px`, `py`, `pz` - Input. The coordinates of `p`
777 * `plat` - Output. The latitude of `p` in the range `-PI/2` to `PI/2`, or `0` if `pnrm = 0`.
778 * `plon` - Output. The longitude of `p` in the range `-PI` to `PI`, or `0` if `p` lies on the
779 Z-axis.
780 * `pnrm` - Output. The magnitude (Euclidean norm) of `p`.
781
782 # Safety
783 - All pointers must be valid and properly aligned
784 */
785 #[link_name = "scoord_"]
786 pub fn scoord(
787 px: *const c_double,
788 py: *const c_double,
789 pz: *const c_double,
790 plat: *mut c_double,
791 plon: *mut c_double,
792 pnrm: *mut c_double,
793 );
794
795 /**
796 Replaces the diagonal arc of a quadrilateral with the other diagonal.
797
798 Given a triangulation of a set of points on the unit sphere, this subroutine replaces a diagonal
799 arc in a strictly convex quadrilateral (defined by a pair of adjacent triangles) with the other
800 diagonal. Equivalently, a pair of adjacent triangles is replaced by another pair having the same
801 union.
802
803 # Arguments
804
805 * `in1`, `in2`, `io1`, `io2` - Input. Nodal indexes of the vertices of the quadrilateral. `io1 -
806 io2` is replaced by `in1 - in2`. (`io1`, `io2`, `in1`) and (`io2`, `io1`, `in2`) must be
807 triangles on input.
808
809 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input/output. The data structure
810 defining the triangulation, created by [trmesh]. On output, updated with the swap; triangles
811 (`io1`, `io2`, `in1`) and (`io2`, `io1`, `in2`) are replaced by (`in1`, `in2`, `io2`) and (`in2`, `in1`, `io1`) unless `lp21 = 0`.
812 * `lp21` - Output. Index of `in1` as a neighbor of `in2` after the swap is performed unless `in1` and `in2` are adjacent on input, in which case `lp21 = 0`.
813 */
814 #[link_name = "swap_"]
815 pub fn swap(
816 in1: *const c_int,
817 in2: *const c_int,
818 io1: *const c_int,
819 io2: *const c_int,
820 list: *mut c_int,
821 lptr: *mut c_int,
822 lend: *mut c_int,
823 lp21: *mut c_int,
824 );
825
826 /**
827 Decides whether to replace a diagonal arc by the other in a quadrilateral.
828
829 The decision will be to swap (`swptst = true`) if and only if `n4` lies above the plane (in the half-space
830 not containing the origin) defined by (`n1`, `n2`, `n3`), or equivalently, if the projection of
831 `n4` onto this plane is interior to the circumcircle of (`n1`, `n2`, `n3`). The decision will be
832 for no swap if the quadrilateral is not strictly convex.
833
834 # Arguments
835
836 * `n1`, `n2`, `n3`, `n4` - Input. The indexes of the four nodes defining the quadrilateral with
837 `n1` adjacent to `n2`, and (`n1`, `n2`, `n3`) in counterclockwise order. The arc connecting `n1`
838 to `n2` should be replaced by an arc connection `n3` to `n4` if `swptst = true`. Refer to
839 subroutine [swap].
840 * `x[n]`, `y[n]`, `z[n]` - Input. The coordinates of the nodes.
841
842 # Returns
843
844 True if and only if the arc connecting `n1` and `n2` should be swapped for an arc connecting `n3` and `n4`.
845 */
846 #[link_name = "swptst_"]
847 pub fn swptst(
848 n1: *const c_int,
849 n2: *const c_int,
850 n3: *const c_int,
851 n4: *const c_int,
852 x: *const c_double,
853 y: *const c_double,
854 z: *const c_double,
855 ) -> bool;
856
857 /**
858 Transform spherical coordinates into Cartesian coordinates
859 on the unit sphere for input to [trmesh].
860
861 Storage for X and Y may coincide with storage for `rlat` and `rlon` if the latter need not be saved.
862
863 # Arguments
864 * `n` - Input. The number of nodes (points on the unit sphere) whose coordinates are to be transformed
865 * `rlat` - Input. The latitudes of the nodes in radians.
866 * `rlon` - Input. The longitudes of the nodes in radians.
867 * `x`, `y`, and `z` - Output. The coordinates in the range `-1` to `1`. `x[i]**2 + y[i]**2 + z[i]**2 = 1` for `i` = 1 to `n`
868 # Safety
869 - All pointers must be valid and properly aligned
870 - Arrays `rlat`, `rlon`, `x`, `y`, `z` must have length >= `*n`
871 - Arrays `x`, `y`, `z` must not overlap with `rlat`, `rlon`
872 - `n` must be > 0
873 */
874 #[link_name = "trans_"]
875 pub fn trans(
876 n: *const c_int,
877 rlat: *const c_double,
878 rlon: *const c_double,
879 x: *mut c_double,
880 y: *mut c_double,
881 z: *mut c_double,
882 );
883
884 /**
885 Locates a point relative to a triangulation.
886
887 This subroutine locates a point `p` relative to a triangulation created by [trmesh]. If `p` is contained
888 in a triangle, the three vertex indexes and barycentric coordinates are returned. Otherwise, the
889 indexes of the visible boundary nodes are returned.
890
891 # Arguments
892 * `nst` - Input. The index of a node at which [trfind] begins its search. Search time depends on
893 the proximity of this node to `p`.
894 * `p[3]` - Input. The x, y, and z coordinates (in that order) of the point `p` to be located.
895 * `n` - Input. The number of nodes in the triangulation. `3 <= n`.
896 * `x[n]`, `y[n]`, `z[n]`, the coordinates of the triangulation nodes (unit vectors).
897 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input. The data structure defining the
898 triangulation, created by [trmesh].
899 `b1`, `b2`, `b3` - Output. The unnormalized barycentric coordinates of the central projection of
900 `p` onto the underlying planar triangle if `p` is in the convex hull of the nodes. These
901 parameters are not altered if `i1 = 0`.
902 * `i1`, `i2`, `i3` - Output. The counterclockwise-ordered vertex indexes of a triangle
903 containing `p` if `p` is contained in a triangle. If `p` is not in the convex hull of
904 the nodes, `i1` and `i2` are the rightmost and leftmost (boundary) nodes that are visible from
905 `p`, and `i3 = 0`. (If all boundary nodes are visible from `p`, then `i1` and `i2` coincide.)
906 `i1 = i2 = i3 = 0` if `p` and all of the nodes are coplanar (lie on a common great circle).
907 */
908 #[link_name = "trfind_"]
909 pub fn trfind(
910 nst: *const c_int,
911 p: *const c_double,
912 n: *const c_int,
913 x: *const c_double,
914 y: *const c_double,
915 z: *const c_double,
916 list: *const c_int,
917 lptr: *const c_int,
918 lend: *const c_int,
919 b1: *mut c_double,
920 b2: *mut c_double,
921 b3: *mut c_double,
922 i1: *mut c_int,
923 i2: *mut c_int,
924 i3: *mut c_int,
925 );
926
927 /**
928 Convert a triangulation data structure into a triangle list.
929
930 # Arguments
931 * `n` - Input. The number of nodes in the triangulation. `3 <= n`.
932 * `list`, `lptr`, `lend` - Input. Linked list data structure defining the triangulation.
933 * `nrow` - Input. The number of rows (entries per triangle) reserved for the triangle list `ltri`. The
934 value must be 6 if only the vertex indexes and neighboring triangle indexes are to be stored, or
935 if arc indexes are also to be assigned and stored. Refer to `ltri`.
936 * `nt` - Output. The number of triangles in the triangulation unless `ier != 0`, which
937 in case `nt = 0`. `nt = 2n - nb - 2` if `nb >= 3` or `2n-4` if `nb = 0`, where `nb` is the
938 number of boundary nodes.
939 * `ltri` - Output. The second dimension of `ltri` must be at least `nt`, where `nt` will be at
940 most `2*n - 4`. The `j`-th column contains the vertex nodal indexes (first three rows),
941 neighboring triangle indxes (second three rows), and, if `nrow = 9`, arc indexes (last three
942 rows) associated with triangle `j` for `j = 1, ..., nt`. The vertices are ordered counterclockwise
943 with the first vertex taken to be the one with smallest index. Thus `ltri[2][j]` and `ltri[3][j]` are larger than `ltri[1, j]` and index adjacent neighbors of node `ltri[1][j]`. For `i = 1, 2, 3`, `ltri[i + 3][j]` and `ltri[i + 6][j]` index the triangle and arc, respectively, which are opposite (not shared by) node `ltri[i][j]`, with `ltri[i + 3, j] = 0` if `ltri[i + 6][j]` indexes a boundary arc. Vertex indexes range from `1` to `n`, triangle indexes from `0` to `nt`, and, if included, arc indexes from `1` to `na`, where `na = 3n - nb - 3` if `nb >= 3` or `3n - 6` if `nb = 0`. The triangles are ordered on first (smallest) vertex indexes.
944 * ier - Output. Error indicator.
945 * `0`, if no errors were encountered.
946 * `1`, if `n` or `nrow` is outside its valid range on input.
947 * `2`, if the triangulation data structure (`list`, `lptr`, `lend`) is invalid. Note, however, that
948 these arrays are not completely tested for validity.
949 */
950 #[link_name = "trlist_"]
951 pub fn trlist(
952 n: *const c_int,
953 list: *const c_int,
954 lptr: *const c_int,
955 lend: *const c_int,
956 nrow: *const c_int,
957 nt: *mut c_int,
958 ltri: *mut c_int,
959 ier: *mut c_int,
960 );
961
962 /**
963 Converts a triangulation data structure to a triangle list.
964
965 This subroutine converts a triangulation data structure from the linked list created by [trmesh] to a triangle list.
966
967 It is a version of [trlist] for the special case where the triangle list should only include the nodes that define each triangle.
968
969 # Arguments
970
971 * `n` - Input. The number of nodes in the triangulation.
972 * `list[6 * (n - 2)]`, `lptr[6 * (n - 2)]`, `lend[n]` - Input. The linked list data
973 structure defining the triangulation. Refer to [trmesh].
974 * `nt` - Output. The number of triangles in the triangulation unless `ier != 0`, in which
975 case `nt = 0`. `nt = 2n - nb - 2` if `nb >= 3` or `2n - 4` if `nb = 0`, where `nb` is the number of boundary nodes.
976 * `ltri[3][*]` - Output. The second dimension of `ltri` must be at least `nt`, where `nt`
977 will be at most `2 * n - 4`. The `j`-th column contains the vertex nodal indexes
978 associated with triangle `j` for `j = 1, ..., nt`. The vertices are ordered counterclockwise with the first vertex taken to be the one with the smallest index. Thus, `ltri[2][j]` and `ltri[3][j]` are larger than `ltri[1][j]` and index adjacent neighbors of node `ltri[1][j]`. The triangles are ordered on first (smallest) vertex indexes.
979 * `ier` - Output. Error indicator:
980 * `0`, if no errors were encountered.
981 * `1`, if `n` is outside its valid range on input.
982 * `2`, if the triangulation data structure (`list`, `lptr`, `lend`) is invalid. Note, however, that these arrays are not completely tested for validity.
983 */
984 #[link_name = "trlist2_"]
985 pub fn trlist2(
986 n: *const c_int,
987 list: *const c_int,
988 lptr: *const c_int,
989 lend: *const c_int,
990 nt: *mut c_int,
991 ltri: *mut c_int,
992 ier: *mut c_int,
993 );
994
995 /**
996 Creates a Delaunay triangulation on the unit sphere.
997
998 The Delaunay triangulation is defined as a set of (spherical) triangles with the following five
999 properties:
1000 1. The triangle vertices are nodes.
1001 2. No triangle contains a node other than its vertices.
1002 3. The interiors of the triangles are pairwise disjoint.
1003 4. The union of triangles is the convex hull of the set of nodes (the smallest convex set that
1004 contains the nodes). If the nodes are not contained in a single hemisphere, their convex hull
1005 is the entire sphere and there are no boundary nodes. Otherwise, there are at least three
1006 boundary nodes.
1007 5. The interior of the circumcircle of each triangle contains no node.
1008
1009 The first four properties define a triangulation, and the last property results in a
1010 triangulation which is as close as possible to equiangular in a certain sense and which is
1011 uniquely defined unless four or more nodes lie in a common plane. This property makes the
1012 triangulation well-suited for solving closest-point problems and for triangle based
1013 interpolation.
1014
1015 Provided the nodes are randomly ordered, the algorithm has expected time complexity O(N*log(N))
1016 for most nodal distributions. Note, however, that the complexity may be as high as O(N**2) if,
1017 for example, the nodes are ordered on increasing latitude.
1018
1019 Spherical coordinates (latitude and longitude) may be converted to Cartesian coordinates by
1020 [trans].
1021
1022 The following is a list of the software package modules which a user may wish to call directly:
1023 * [addnod] - Updates the triangulation by appending a new node.
1024 * [areas] - Returns the area of a spherical triangle
1025 * [bnodes] - Returns an array containing the index of the boundary nodes (if any) in
1026 counterclockwise order. Counts of boundary nodes, triangles, and arcs are also returned.
1027 * [circum] - Returns the circumcenter of a spherical triangle.
1028 * [crlist] - Returns the set of triangle circumcenters (Voronoi vertices) and circumradii
1029 associated with a triangulation.
1030 * [delarc] - Deletes a boundary arc from a triangulation.
1031 * [edge] - Forces an arbitrary pair of nodes to be connected by an arc in the triangulation.
1032 * [getnp] - Determines the ordered sequence of L closest nodes to a given node, along with the
1033 associated distances.
1034 * [inside] - Locates a point relative to a polygon on the surface of the sphere.
1035 * [intrsc] - Returns the point of intersection between a pair of great circle arcs.
1036 * [jrand] - Generates a uniformly distributed pseudo-random integer.
1037 * [left] - Locates a point relative to a great circle
1038 * [nearnd] - Returns the index of the nearest node to an arbitrary point, along with its squared
1039 distance.
1040 * [scoord] - Converts a point from Cartesian coordinates to spherical coordinates.
1041 * [trans] - Transforms spherical coordinates into Cartesian coordinates on the unit sphere for
1042 input to [trmesh]
1043 * [trlist] - Converts the triangulation data structure to a triangle list more suitable for use in
1044 a finite element code.
1045
1046 # Arguments
1047 * `n` - Input. The number of nodes in the triangulation. `3 <= n`.
1048 * `x[n]`, `y[n]`, `z[n]` - Input. The coordinates of distinct nodes. `(x[k], y[k], z[k])` is referred to as node `k`, and `k` is referred to as a nodal index. It is required that `x[k]**2 + y[k]**2 + z[k]**2 = 1` for all `k`. The first three nodes must not be collinear (lie on a common great circle).
1049 * `list` - Output. `6 * (n - 2)` nodal indexes which, along with `lptr`, `lend`, and `lnew`
1050 define the triangulation as a set of `n` adjacency lists; counterclockwise-ordered sequences of
1051 neighboring nodes such that the first and last neighbors of a boundary node are boundary nodes
1052 (the first neighbor of an interior node is arbitrary). In order to distinguish between interior
1053 and boundary nodes, the last neighbor of each boundary node is represented by the negative of
1054 its index.
1055 * `lptr` - Output. Set of pointers (`list` indexes) in one-to-one correspondence with the
1056 elements of `list`. `list[lptr[i]]` indexes the node which follows `list[i]` in cyclical
1057 counterclockwise order (the first neighbor follows the last neighbor).
1058 * `lend` - Output. `n` pointers to adjacency lists. `lend[k]` points to the last neighbor of
1059 node `k`. `list[lend[k]] < 0` if and only if `k` is a boundary node.
1060 * `lnew` - Output. Pointer to the first empty location in `list` and `lptr` (list length plus
1061 one). `list`, `lptr`, `lend` and `lnew` are not altered if `ier < 0`, and are incomplete if `0 <
1062 ier`.
1063 * `near` - Workspace. An array of `n` integers used to efficiently determine the nearest
1064 triangulation node to each unprocessed node for use by [addnod].
1065 * `next` - Workspace. An array of `n` integers used to efficiently determine the nearest triangulation node
1066 to each unprocessed node for use by [addnod].
1067 * `dist` - Workspace. An array of `n` floats used to efficiently determine the neareast
1068 triangulation node to each unprocessed node for use by [addnod].
1069 * `ier` - Output. An integer error indicator:
1070 * `0`, if no errors were encountered.
1071 * `-1`, if `n < 3` on input.
1072 * `-2`, if the first three nodes are collinear.
1073 * `L`, if nodes L and M coincide for some L < M. The data structure represents a triangulation
1074 of nodes 1 to M-1 in this case.
1075 */
1076 #[link_name = "trmesh_"]
1077 pub fn trmesh(
1078 n: *const c_int,
1079 x: *const c_double,
1080 y: *const c_double,
1081 z: *const c_double,
1082 list: *mut c_int,
1083 lptr: *mut c_int,
1084 lend: *mut c_int,
1085 lnew: *mut c_int,
1086 near: *mut c_int,
1087 next: *mut c_int,
1088 dist: *mut c_double,
1089 ier: *mut c_int,
1090 );
1091
1092}
1093
1094#[cfg(test)]
1095mod test {
1096 use proptest::prelude::*;
1097 use rstest::{fixture, rstest};
1098
1099 use super::*;
1100 use std::{
1101 collections::HashSet,
1102 f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, PI, TAU},
1103 };
1104
1105 #[rstest]
1106 #[case(1, &[FRAC_PI_2], &[0.0], &[0.0], &[0.0], &[1.0])]
1107 #[case(1, &[-FRAC_PI_2], &[0.0], &[0.0], &[0.0], &[-1.0])]
1108 #[case(1, &[0.0], &[0.0], &[1.0], &[0.0], &[0.0])]
1109 #[case(1, &[0.0], &[FRAC_PI_2], &[0.0], &[1.0], &[0.0])]
1110 #[case(1, &[0.0], &[PI], &[-1.0], &[0.0], &[0.0])]
1111 #[case(1, &[0.0], &[-FRAC_PI_2], &[0.0], &[-1.0], &[0.0])]
1112 #[case(1, &[std::f64::consts::FRAC_PI_4], &[0.0], &[FRAC_1_SQRT_2], &[0.0], &[FRAC_1_SQRT_2])]
1113 #[case(1, &[FRAC_PI_4], &[FRAC_PI_4], &[0.5], &[0.5], &[FRAC_1_SQRT_2])]
1114 #[case(3, &[FRAC_PI_2, -FRAC_PI_2, 0.0], &[0.0, 0.0, 0.0], &[0.0, 0.0, 1.0], &[0.0, 0.0, 0.0], &[1.0, -1.0, 0.0])]
1115 fn test_trans(
1116 #[case] n: i32,
1117 #[case] rlat: &[f64],
1118 #[case] rlon: &[f64],
1119 #[case] expected_x: &[f64],
1120 #[case] expected_y: &[f64],
1121 #[case] expected_z: &[f64],
1122 ) {
1123 let mut x = vec![0.0; n as usize];
1124 let mut y = vec![0.0; n as usize];
1125 let mut z = vec![0.0; n as usize];
1126
1127 unsafe {
1128 trans(
1129 &raw const n,
1130 rlat.as_ptr(),
1131 rlon.as_ptr(),
1132 x.as_mut_ptr(),
1133 y.as_mut_ptr(),
1134 z.as_mut_ptr(),
1135 );
1136 }
1137
1138 for i in 0..n as usize {
1139 assert!((x[i] - expected_x[i]).abs() < f64::EPSILON);
1140 assert!((y[i] - expected_y[i]).abs() < f64::EPSILON);
1141 assert!((z[i] - expected_z[i]).abs() < f64::EPSILON);
1142 }
1143 }
1144
1145 #[rstest]
1146 #[case(&[1.0, 0.0, 0.0], &[0.0, 1.0, 0.0], &[0.0, 0.0, 1.0], FRAC_PI_2)]
1147 #[case(&[1.0, 0.0, 0.0], &[0.0, -1.0, 0.0], &[0.0, 0.0, 1.0], FRAC_PI_2)]
1148 #[case(&[-1.0, 0.0, 0.0], &[0.0, 1.0, 0.0], &[0.0, 0.0, -1.0], FRAC_PI_2)]
1149 #[case(&[1.0, 0.0, 0.0], &[0.0, 1.0, 0.0], &[0.0, 0.0, -1.0], FRAC_PI_2)]
1150 #[case(&[1.0, 0.0, 0.0], &[1.0, 0.0, 0.0], &[1.0, 0.0, 0.0], 0.0)]
1151 #[case(&[1.0, 0.0, 0.0], &[0.0, 1.0, 0.0], &[-1.0, 0.0, 0.0], 0.0)]
1152 #[case(&[FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0], &[FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0], &[0.0, 0.0, 1.0], FRAC_PI_2)]
1153 fn test_areas(
1154 #[case] v1: &[f64; 3],
1155 #[case] v2: &[f64; 3],
1156 #[case] v3: &[f64; 3],
1157 #[case] expected_area: f64,
1158 ) {
1159 let area = unsafe { areas(v1.as_ptr(), v2.as_ptr(), v3.as_ptr()) };
1160 assert!(
1161 (area - expected_area).abs() < f64::EPSILON,
1162 "expected {expected_area} got {area}"
1163 );
1164 }
1165
1166 #[rstest]
1167 #[case(1.0, 0.0, 0.0, 0.0, 0.0, 1.0)]
1168 #[case(0.0, 1.0, 0.0, 0.0, FRAC_PI_2, 1.0)]
1169 #[case(-1.0, 0.0, 0.0, 0.0, PI, 1.0)]
1170 #[case(0.0, -1.0, 0.0, 0.0, -FRAC_PI_2, 1.0)]
1171 #[case(0.0, 0.0, 1.0, FRAC_PI_2, 0.0, 1.0)]
1172 #[case(0.0, 0.0, -1.0, -FRAC_PI_2, 0.0, 1.0)]
1173 #[case(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0, 0.0, FRAC_PI_4, 1.0)]
1174 #[case(0.5, 0.5, FRAC_1_SQRT_2, FRAC_PI_4, FRAC_PI_4, 1.0)]
1175 #[case(2.0, 0.0, 0.0, 0.0, 0.0, 2.0)]
1176 #[case(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)]
1177 fn test_scoord(
1178 #[case] px: f64,
1179 #[case] py: f64,
1180 #[case] pz: f64,
1181 #[case] expected_plat: f64,
1182 #[case] expected_plon: f64,
1183 #[case] expected_pnrm: f64,
1184 ) {
1185 let mut plat = 0.0;
1186 let mut plon = 0.0;
1187 let mut pnrm = 0.0;
1188
1189 unsafe {
1190 scoord(
1191 &raw const px,
1192 &raw const py,
1193 &raw const pz,
1194 &raw mut plat,
1195 &raw mut plon,
1196 &raw mut pnrm,
1197 );
1198 };
1199
1200 assert!((expected_plat - plat).abs() < f64::EPSILON);
1201 assert!((expected_plon - plon).abs() < f64::EPSILON);
1202 assert!((expected_pnrm - pnrm).abs() < f64::EPSILON);
1203
1204 let n = 1;
1205 let mut x = 0.0;
1206 let mut y = 0.0;
1207 let mut z = 0.0;
1208
1209 unsafe {
1210 trans(
1211 &raw const n,
1212 &raw const plat,
1213 &raw const plon,
1214 &raw mut x,
1215 &raw mut y,
1216 &raw mut z,
1217 );
1218 };
1219 if pnrm == 0.0 {
1220 return;
1221 }
1222
1223 let norm = (x * x + y * y + z * z).sqrt();
1224
1225 assert!(
1226 (x / norm - px / pnrm).abs() < f64::EPSILON,
1227 "expected: {px} got: {x}"
1228 );
1229 assert!(
1230 (y / norm - py / pnrm).abs() < f64::EPSILON,
1231 "expected: {py} got: {y}"
1232 );
1233 assert!(
1234 (z / norm - pz / pnrm).abs() < f64::EPSILON,
1235 "expected: {pz} got: {z}"
1236 );
1237 }
1238
1239 #[rstest]
1240 #[case(2, &[1.0, 0.0], &[0.0, 1.0], &[0.0, 0.0], -1)]
1241 fn test_trmesh(
1242 #[case] n: i32,
1243 #[case] x_in: &[f64],
1244 #[case] y_in: &[f64],
1245 #[case] z_in: &[f64],
1246 #[case] expected_ier: i32,
1247 ) {
1248 let mut x = Vec::with_capacity(n as usize);
1249 let mut y = Vec::with_capacity(n as usize);
1250 let mut z = Vec::with_capacity(n as usize);
1251
1252 for i in 0..n as usize {
1253 let norm = (x_in[i].powi(2) + y_in[i].powi(2) + z_in[i].powi(2)).sqrt();
1254 x.push(x_in[i] / norm);
1255 y.push(y_in[i] / norm);
1256 z.push(z_in[i] / norm);
1257 }
1258
1259 let list_size = if n >= 3 { 6 * (n - 2) } else { 0 } as usize;
1260
1261 let mut list = vec![0i32; list_size];
1262 let mut lptr = vec![0i32; list_size];
1263 let mut lend = vec![0i32; n as usize];
1264 let mut lnew = 0i32;
1265
1266 let mut near = vec![0i32; n as usize];
1267 let mut next = vec![0i32; n as usize];
1268 let mut dist = vec![0.0f64; n as usize];
1269 let mut ier = 0i32;
1270
1271 unsafe {
1272 trmesh(
1273 &raw const n,
1274 x.as_ptr(),
1275 y.as_ptr(),
1276 z.as_ptr(),
1277 list.as_mut_ptr(),
1278 lptr.as_mut_ptr(),
1279 lend.as_mut_ptr(),
1280 &raw mut lnew,
1281 near.as_mut_ptr(),
1282 next.as_mut_ptr(),
1283 dist.as_mut_ptr(),
1284 &raw mut ier,
1285 );
1286 }
1287
1288 assert_eq!(ier, expected_ier);
1289
1290 if expected_ier != 0 {
1291 return;
1292 }
1293
1294 assert!(lnew > 0, "lnew should be positive");
1295 assert!(lnew as usize <= list_size, "lnew should be within bounds");
1296
1297 for i in 0..n as usize {
1298 let lend_val = lend[i];
1299 assert!(lend_val > 0, "lend[{i}] should be positive");
1300 }
1301 }
1302
1303 #[fixture]
1304 fn tetrahedron() -> (Vec<f64>, Vec<f64>, Vec<f64>) {
1305 let tx = vec![
1306 0.816_496_580_9,
1307 -0.408_248_290_50,
1308 -0.408_248_290_590_5,
1309 0.0,
1310 ];
1311 let ty = vec![0.0, FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0];
1312 let tz = vec![
1313 -0.577_350_269_2,
1314 0.577_350_269_2,
1315 0.577_350_269_2,
1316 -0.577_350_269_2,
1317 ];
1318 (tx, ty, tz)
1319 }
1320
1321 #[fixture]
1322 fn octahedron() -> (Vec<f64>, Vec<f64>, Vec<f64>) {
1323 let ox = vec![1.0, -1.0, 0.0, 0.0, 0.0, 0.0];
1324 let oy = vec![0.0, 0.0, 1.0, -1.0, 0.0, 0.0];
1325 let oz = vec![0.0, 0.0, 0.0, 0.0, 1.0, -1.0];
1326
1327 (ox, oy, oz)
1328 }
1329
1330 #[rstest]
1331 #[case(4, 6, 4)]
1332 #[case(4, 9, 4)]
1333 #[case(6, 6, 8)]
1334 #[case(6, 9, 8)]
1335 #[case(5, 6, 0)]
1336 fn test_trlist(
1337 #[case] n: i32,
1338 #[case] nrow: i32,
1339 #[case] expected_nt: i32,
1340 tetrahedron: (Vec<f64>, Vec<f64>, Vec<f64>),
1341 octahedron: (Vec<f64>, Vec<f64>, Vec<f64>),
1342 ) {
1343 let x = if n == 4 { tetrahedron.0 } else { octahedron.0 };
1344 let y = if n == 4 { tetrahedron.1 } else { octahedron.1 };
1345 let z = if n == 4 { tetrahedron.2 } else { octahedron.2 };
1346
1347 let list_size = (6 * (n - 2)) as usize;
1348 let mut list = vec![0i32; list_size];
1349 let mut lptr = vec![0i32; list_size];
1350 let mut lend = vec![0i32; n as usize];
1351 let mut lnew = 0i32;
1352
1353 let mut near = vec![0i32; n as usize];
1354 let mut next = vec![0i32; n as usize];
1355 let mut dist = vec![0.0f64; n as usize];
1356 let mut ier = 0i32;
1357
1358 unsafe {
1359 trmesh(
1360 &raw const n,
1361 x.as_ptr(),
1362 y.as_ptr(),
1363 z.as_ptr(),
1364 list.as_mut_ptr(),
1365 lptr.as_mut_ptr(),
1366 lend.as_mut_ptr(),
1367 &raw mut lnew,
1368 near.as_mut_ptr(),
1369 next.as_mut_ptr(),
1370 dist.as_mut_ptr(),
1371 &raw mut ier,
1372 );
1373 };
1374
1375 assert_eq!(ier, 0, "trmesh failed");
1376
1377 let max_triangles = (2 * n - 4) as usize;
1378 let mut ltri = vec![0i32; (nrow as usize) * max_triangles];
1379 let mut nt = 0i32;
1380 let mut ier2 = 0i32;
1381
1382 unsafe {
1383 trlist(
1384 &raw const n,
1385 list.as_ptr(),
1386 lptr.as_ptr(),
1387 lend.as_ptr(),
1388 &raw const nrow,
1389 &raw mut nt,
1390 ltri.as_mut_ptr(),
1391 &raw mut ier2,
1392 );
1393 }
1394
1395 if nrow == 6 || nrow == 9 {
1396 assert_eq!(ier2, 0, "trlist should succeed");
1397 assert_eq!(nt, expected_nt, "triangle count mismatch");
1398 let v1 = ltri[0];
1399 assert!(v1 >= 1 && v1 <= n, "invalid vertex index");
1400 return;
1401 }
1402
1403 assert_eq!(ier2, 1, "should fail with inalid nrow");
1404 assert_eq!(nt, 0, "nt should be 0 on error");
1405 }
1406
1407 fn fibonacci_sphere(n: usize) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
1408 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
1409 let mut x_points = Vec::with_capacity(n);
1410 let mut y_points = Vec::with_capacity(n);
1411 let mut z_points = Vec::with_capacity(n);
1412
1413 for i in 0..n {
1414 let y = 1.0 - (i as f64 / (n as f64 - 1.0)) * 2.0;
1415 let radius = (1.0 - y * y).sqrt();
1416 let theta = 2.0 * std::f64::consts::PI * i as f64 * phi;
1417 let x = radius * theta.cos();
1418 let z = radius * theta.sin();
1419
1420 x_points.push(x);
1421 y_points.push(y);
1422 z_points.push(z);
1423 }
1424
1425 (x_points, y_points, z_points)
1426 }
1427
1428 #[fixture]
1429 fn hemisphere_fixed() -> (Vec<f64>, Vec<f64>, Vec<f64>) {
1430 let x = vec![1.0, 0.0, -1.0, 0.0, 0.0];
1431 let y = vec![0.0, 1.0, 0.0, -1.0, 0.0];
1432 let z = vec![0.0, 0.0, 0.0, 0.0, 1.0];
1433
1434 (x, y, z)
1435 }
1436
1437 fn hemisphere(n: usize) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
1438 let (x, y, z) = fibonacci_sphere(n * 2);
1439
1440 let mut hx = Vec::with_capacity(n);
1441 let mut hy = Vec::with_capacity(n);
1442 let mut hz = Vec::with_capacity(n);
1443
1444 for i in 0..x.len() {
1445 if z[i] >= 0.0 && hx.len() < n {
1446 hx.push(x[i]);
1447 hy.push(y[i]);
1448 hz.push(z[i]);
1449 }
1450 }
1451
1452 (hx, hy, hz)
1453 }
1454
1455 #[rstest]
1456 #[case(4, &[], 0, 6, 4)]
1457 #[case(6, &[], 0, 12, 8)]
1458 #[case(5, &[1, 2, 3, 4], 4, 9, 4)]
1459 fn test_bnodes(
1460 #[case] n: i32,
1461 #[case] expected_boundary: &[i32],
1462 #[case] expected_nb: i32,
1463 #[case] expected_na: i32,
1464 #[case] expected_nt: i32,
1465 tetrahedron: (Vec<f64>, Vec<f64>, Vec<f64>),
1466 hemisphere_fixed: (Vec<f64>, Vec<f64>, Vec<f64>),
1467 ) {
1468 let (x, y, z) = if n == 4 {
1469 tetrahedron
1470 } else if n == 5 {
1471 hemisphere_fixed
1472 } else {
1473 fibonacci_sphere(n as usize)
1474 };
1475
1476 let list_size = (6 * (n - 2)) as usize;
1477 let mut list = vec![0i32; list_size];
1478 let mut lptr = vec![0i32; list_size];
1479 let mut lend = vec![0i32; n as usize];
1480 let mut lnew = 0i32;
1481
1482 let mut near = vec![0i32; n as usize];
1483 let mut next = vec![0i32; n as usize];
1484 let mut dist = vec![0.0f64; n as usize];
1485 let mut ier = 0i32;
1486
1487 unsafe {
1488 trmesh(
1489 &raw const n,
1490 x.as_ptr(),
1491 y.as_ptr(),
1492 z.as_ptr(),
1493 list.as_mut_ptr(),
1494 lptr.as_mut_ptr(),
1495 lend.as_mut_ptr(),
1496 &raw mut lnew,
1497 near.as_mut_ptr(),
1498 next.as_mut_ptr(),
1499 dist.as_mut_ptr(),
1500 &raw mut ier,
1501 );
1502 };
1503
1504 assert_eq!(ier, 0, "trmesh failed");
1505
1506 let mut nodes = vec![0i32; n as usize];
1507 let mut nb = 0i32;
1508 let mut na = 0i32;
1509 let mut nt = 0i32;
1510
1511 unsafe {
1512 bnodes(
1513 &raw const n,
1514 list.as_ptr(),
1515 lptr.as_ptr(),
1516 lend.as_ptr(),
1517 nodes.as_mut_ptr(),
1518 &raw mut nb,
1519 &raw mut na,
1520 &raw mut nt,
1521 );
1522 };
1523
1524 assert_eq!(nb, expected_nb, "boundary node count mismatch");
1525 assert_eq!(na, expected_na, "arc count mismatch");
1526 assert_eq!(nt, expected_nt, "triangle count mismatch");
1527
1528 if nb > 0 {
1529 for i in 0..nb as usize {
1530 assert_eq!(nodes[i], expected_boundary[i], "boundary node {i} mismatch");
1531 }
1532 }
1533 }
1534
1535 fn create_triangulation(
1536 n: i32,
1537 x: &[f64],
1538 y: &[f64],
1539 z: &[f64],
1540 ) -> (Vec<i32>, Vec<i32>, Vec<i32>, i32) {
1541 let list_size = (6 * (n - 2)) as usize;
1542 let mut list = vec![0i32; list_size];
1543 let mut lptr = vec![0i32; list_size];
1544 let mut lend = vec![0i32; n as usize];
1545 let mut lnew = 0i32;
1546
1547 let mut near = vec![0i32; n as usize];
1548 let mut next = vec![0i32; n as usize];
1549 let mut dist = vec![0.0f64; n as usize];
1550 let mut ier = 0i32;
1551
1552 unsafe {
1553 trmesh(
1554 &raw const n,
1555 x.as_ptr(),
1556 y.as_ptr(),
1557 z.as_ptr(),
1558 list.as_mut_ptr(),
1559 lptr.as_mut_ptr(),
1560 lend.as_mut_ptr(),
1561 &raw mut lnew,
1562 near.as_mut_ptr(),
1563 next.as_mut_ptr(),
1564 dist.as_mut_ptr(),
1565 &raw mut ier,
1566 );
1567 };
1568
1569 assert_eq!(ier, 0, "trmesh failed");
1570
1571 (list, lptr, lend, lnew)
1572 }
1573
1574 proptest! {
1575 #[test]
1576 fn trfind_locates_any_interior_point(px in -1.0f64..1.0, py in -1.0f64..1.0, pz in -1.0f64..1.0, n in 6..20) {
1577 let norm = (px * px + py * py + pz * pz).sqrt();
1578 if norm < f64::EPSILON {
1579 return Ok(());
1580 }
1581 let p = [px / norm, py / norm, pz / norm];
1582 let (x, y, z) = fibonacci_sphere(n as usize);
1583 let (list, lptr, lend, _) = create_triangulation(n, &x, &y, &z);
1584
1585 let nst = 1;
1586 let mut b1 = 0.0;
1587 let mut b2 = 0.0;
1588 let mut b3 = 0.0;
1589 let mut i1 = 0i32;
1590 let mut i2 = 0i32;
1591 let mut i3 = 0i32;
1592
1593 unsafe {
1594 trfind(
1595 &raw const nst,
1596 p.as_ptr(),
1597 &raw const n,
1598 x.as_ptr(),
1599 y.as_ptr(),
1600 z.as_ptr(),
1601 list.as_ptr(),
1602 lptr.as_ptr(),
1603 lend.as_ptr(),
1604 &raw mut b1,
1605 &raw mut b2,
1606 &raw mut b3,
1607 &raw mut i1,
1608 &raw mut i2,
1609 &raw mut i3,
1610 );
1611 };
1612
1613 prop_assert!(i3 > 0, "i3 should be > 0");
1614 prop_assert!(i1 >= 1 && i1 <= n, "i1 should be a valid node index");
1615 prop_assert!(i2 >= 1 && i2 <= n, "i2 should be a valid node index");
1616 prop_assert!(i3 >= 1 && i3 <= n, "i3 should be a valid node index");
1617 prop_assert!(b1 > 0.0 && b2 > 0.0 && b3 > 0.0, "barycentric coords should be positive for an interior point");
1618 }
1619 }
1620
1621 fn unit_vector() -> impl Strategy<Value = [f64; 3]> {
1622 (-1.0f64..1.0, -1.0f64..1.0, -1.0f64..1.0).prop_map(|(x, y, z)| {
1623 let norm = (x * x + y * y + z * z).sqrt();
1624 if norm < f64::EPSILON {
1625 [1.0, 0.0, 0.0]
1626 } else {
1627 [x / norm, y / norm, z / norm]
1628 }
1629 })
1630 }
1631
1632 fn spherical_distance(a: &[f64], b: &[f64]) -> f64 {
1633 (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])
1634 .clamp(-1.0, 1.0)
1635 .acos()
1636 }
1637
1638 fn normalize(v: &[f64]) -> [f64; 3] {
1639 let norm = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
1640 if norm < f64::EPSILON {
1641 [1.0, 0.0, 0.0]
1642 } else {
1643 [v[0] / norm, v[1] / norm, v[2] / norm]
1644 }
1645 }
1646
1647 proptest! {
1648 #[test]
1649 fn circum_equidistant(v1 in unit_vector(), v2 in unit_vector(), v3 in unit_vector()) {
1650 let mut c = [0.0; 3];
1651 let mut ier = 0i32;
1652
1653 unsafe {
1654 circum(
1655 v1.as_ptr(),
1656 v2.as_ptr(),
1657 v3.as_ptr(),
1658 c.as_mut_ptr(),
1659 &raw mut ier
1660 );
1661 };
1662
1663 if ier != 0 {
1664 return Ok(());
1665 }
1666
1667 let d1 = spherical_distance(&c, &v1);
1668 let d2 = spherical_distance(&c, &v2);
1669 let d3 = spherical_distance(&c, &v3);
1670
1671 prop_assert!((d1 - d2).abs() < 1e-10, "d1={d1} d2={d2}");
1672 prop_assert!((d2 - d3).abs() < 1e-10, "d2={d2} d3={d3}");
1673 prop_assert!((d1 - d3).abs() < 1e-10, "d1={d1} d3={d3}");
1674 }
1675
1676 #[test]
1677 fn circum_on_unit_sphere(v1 in unit_vector(), v2 in unit_vector(), v3 in unit_vector()) {
1678 let mut c = [0.0; 3];
1679 let mut ier = 0i32;
1680
1681 unsafe {
1682 circum(
1683 v1.as_ptr(),
1684 v2.as_ptr(),
1685 v3.as_ptr(),
1686 c.as_mut_ptr(),
1687 &raw mut ier
1688 );
1689 };
1690
1691 if ier != 0 {
1692 return Ok(());
1693 }
1694
1695 let norm = (c[0] * c[0] + c[1] * c[1] + c[2] * c[2]).sqrt();
1696 prop_assert!((norm - 1.0).abs() < 1e-10, "norm: {norm}");
1697 }
1698
1699 #[test]
1700 fn circum_collinear_error(value in 0f64..TAU) {
1701 let v1 = [value, 0.0, 0.0];
1702 let v2 = [value * 2.0, 0.0, 0.0];
1703 let v3 = [value * 3.0, 0.0, 0.0];
1704
1705 let mut c = [0.0; 3];
1706 let mut ier = 0i32;
1707
1708 unsafe {
1709 circum(
1710 v1.as_ptr(),
1711 v2.as_ptr(),
1712 v3.as_ptr(),
1713 c.as_mut_ptr(),
1714 &raw mut ier
1715 );
1716 };
1717
1718 prop_assert!(ier == 1, "expected 1 got {ier}");
1719 }
1720 }
1721
1722 fn add_node_to_triangulation(
1723 nst: i32,
1724 k: i32,
1725 x: &[f64],
1726 y: &[f64],
1727 z: &[f64],
1728 list: &mut [i32],
1729 lptr: &mut [i32],
1730 lend: &mut [i32],
1731 lnew: &mut i32,
1732 ) -> i32 {
1733 let mut ier = 0i32;
1734 unsafe {
1735 addnod(
1736 &raw const nst,
1737 &raw const k,
1738 x.as_ptr(),
1739 y.as_ptr(),
1740 z.as_ptr(),
1741 list.as_mut_ptr(),
1742 lptr.as_mut_ptr(),
1743 lend.as_mut_ptr(),
1744 &raw mut *lnew,
1745 &raw mut ier,
1746 );
1747 };
1748
1749 ier
1750 }
1751
1752 #[rstest]
1753 fn test_addnod_adds_fourth_node_to_tetrahedron(tetrahedron: (Vec<f64>, Vec<f64>, Vec<f64>)) {
1754 let mut x = tetrahedron.0[0..3].to_vec();
1755 let mut y = tetrahedron.1[0..3].to_vec();
1756 let mut z = tetrahedron.2[0..3].to_vec();
1757
1758 let n = 3i32;
1759 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n, &x, &y, &z);
1760
1761 x.push(tetrahedron.0[3]);
1762 y.push(tetrahedron.1[3]);
1763 z.push(tetrahedron.2[3]);
1764
1765 let new_list_size = 6 * (n + 1 - 2) as usize;
1766 list.resize(new_list_size, 0);
1767 lptr.resize(new_list_size, 0);
1768 lend.resize(n as usize + 1, 0);
1769
1770 let k = n + 1;
1771 let nst = 1i32;
1772
1773 let add_ier = add_node_to_triangulation(
1774 nst, k, &x, &y, &z, &mut list, &mut lptr, &mut lend, &mut lnew,
1775 );
1776
1777 assert_eq!(add_ier, 0, "addnod should succeed");
1778 assert!(lnew > 0, "lnew should be positive after adding node");
1779 }
1780
1781 proptest! {
1782 #[test]
1783 fn test_arc_cosine(c in -10.0f64..10.0f64) {
1784 let acos = unsafe { arc_cosine(&raw const c) };
1785 prop_assert!((acos - c.clamp(-1.0, 1.0).acos()).abs() < f64::EPSILON);
1786 }
1787 }
1788
1789 fn scalar_triple_product(n0: &[f64], n1: &[f64], n2: &[f64]) -> f64 {
1790 let cross_x = n1[1] * n2[2] - n1[2] * n2[1];
1791 let cross_y = n1[2] * n2[0] - n1[0] * n2[2];
1792 let cross_z = n1[0] * n2[1] - n1[1] * n2[0];
1793
1794 n0[0] * cross_x + n0[1] * cross_y + n0[2] * cross_z
1795 }
1796
1797 fn is_left(n1: &[f64], n2: &[f64], n0: &[f64]) -> bool {
1798 unsafe {
1799 left(
1800 &raw const n1[0],
1801 &raw const n1[1],
1802 &raw const n1[2],
1803 &raw const n2[0],
1804 &raw const n2[1],
1805 &raw const n2[2],
1806 &raw const n0[0],
1807 &raw const n0[1],
1808 &raw const n0[2],
1809 )
1810 }
1811 }
1812
1813 proptest! {
1814 #[test]
1815 fn left_agrees_with_scalar_triple_product(n1 in unit_vector(), n2 in unit_vector(), n0 in unit_vector()) {
1816 let stp = scalar_triple_product(&n0, &n1, &n2);
1817 let result = is_left(&n1, &n2, &n0);
1818
1819 prop_assert_eq!(result, stp >= -f64::EPSILON);
1820 }
1821
1822 #[test]
1823 fn left_boundary_points_return_true(n1 in unit_vector(), n2 in unit_vector(), coeff1 in -1.0f64..1.0, coeff2 in -1.0f64..1.0) {
1824 if coeff1.abs() < f64::EPSILON || coeff2.abs() < f64::EPSILON {
1825 return Ok(());
1826 }
1827 let n0 = normalize(&[
1828 coeff1 * n1[0] + coeff2 * n2[0],
1829 coeff1 * n1[1] + coeff2 * n2[1],
1830 coeff1 * n1[2] + coeff2 * n2[2],
1831 ]);
1832
1833 let stp = scalar_triple_product(&n0, &n1, &n2);
1834 if stp.abs() < 1e-10 {
1835 return Ok(());
1836 }
1837 let result = is_left(&n1, &n2, &n0);
1838 prop_assert_eq!(result, stp >= 0.0);
1839 }
1840 }
1841
1842 fn should_swap(n1: i32, n2: i32, n3: i32, n4: i32, x: &[f64], y: &[f64], z: &[f64]) -> bool {
1843 unsafe {
1844 swptst(
1845 &raw const n1,
1846 &raw const n2,
1847 &raw const n3,
1848 &raw const n4,
1849 x.as_ptr(),
1850 y.as_ptr(),
1851 z.as_ptr(),
1852 )
1853 }
1854 }
1855
1856 fn swap_determinant(n1: &[f64], n2: &[f64], n3: &[f64], n4: &[f64]) -> f64 {
1857 let dx1 = n1[0] - n4[0];
1858 let dy1 = n1[1] - n4[1];
1859 let dz1 = n1[2] - n4[2];
1860
1861 let dx2 = n2[0] - n4[0];
1862 let dy2 = n2[1] - n4[1];
1863 let dz2 = n2[2] - n4[2];
1864
1865 let dx3 = n3[0] - n4[0];
1866 let dy3 = n3[1] - n4[1];
1867 let dz3 = n3[2] - n4[2];
1868
1869 dx3 * (dy2 * dz1 - dy1 * dz2) - dy3 * (dx2 * dz1 - dx1 * dz2)
1870 + dz3 * (dx2 * dy1 - dx1 * dy2)
1871 }
1872
1873 proptest! {
1874 #[test]
1875 fn test_swptst(
1876 n1 in unit_vector(),
1877 n2 in unit_vector(),
1878 n3 in unit_vector(),
1879 n4 in unit_vector()
1880 ) {
1881 let x = vec![n1[0], n2[0], n3[0], n4[0]];
1882 let y = vec![n1[1], n2[1], n3[1], n4[1]];
1883 let z = vec![n1[2], n2[2], n3[2], n4[2]];
1884
1885 let det = swap_determinant(&n1, &n2, &n3, &n4);
1886 let result = should_swap(1, 2, 3, 4, &x, &y, &z);
1887
1888 prop_assert_eq!(result, det > 0.0);
1889 }
1890 }
1891
1892 fn find_node_pointer(lpl: i32, nb: i32, list: &[i32], lptr: &[i32]) -> i32 {
1893 unsafe { lstptr(&raw const lpl, &raw const nb, list.as_ptr(), lptr.as_ptr()) }
1894 }
1895
1896 fn check_triangulation(n: i32, list: &[i32], lend: &[i32], lptr: &[i32]) {
1897 for node in 1..=n {
1898 let lpl = lend[(node - 1) as usize];
1899 assert!(lpl > 0, "Node {node} should have valid lend after edge");
1900
1901 let mut current = lpl;
1902 let mut count = 0;
1903 loop {
1904 let neighbor = list[(current - 1) as usize].abs();
1905 assert!(
1906 neighbor >= 1 && neighbor <= n,
1907 "Node {node} neighbor {neighbor} out of range"
1908 );
1909 count += 1;
1910 current = lptr[(current - 1) as usize];
1911 if current == lpl {
1912 break;
1913 }
1914 assert!(count <= 6 * (n - 2), "Infinite loop in adjacency list");
1915 }
1916 }
1917 }
1918
1919 proptest! {
1920 fn lsptr_finds_all_neighbors_in_triangulation(n in 6..50i32) {
1921 let (x, y, z) = fibonacci_sphere(n as usize);
1922 let (list, lptr, lend, _) = create_triangulation(n, &x, &y, &z);
1923
1924 for node_idx in 1..=n {
1925 let lpl = lend[(node_idx - 1) as usize];
1926
1927 let mut current = lpl;
1928 let mut neighbor_count = 0;
1929
1930 loop {
1931 let neighbor = list[(current - 1) as usize].abs();
1932
1933 let found_ptr = find_node_pointer(lpl, neighbor, &list, &lptr);
1934
1935 prop_assert!(found_ptr > 0 && found_ptr <= 6 * (n - 2), "Node {node_idx}: lstptr should return valid poiter for neighbor {neighbor}");
1936 prop_assert_eq!(list[(found_ptr - 1) as usize].abs(), neighbor, "Node {}: lstptr should find correct neighbor {} at position {}", node_idx, neighbor, found_ptr);
1937
1938 let mut traversal_ptr = lpl;
1939 let mut found_in_traversal = false;
1940 for _ in 0..(6 * (n - 2)) {
1941 if traversal_ptr == found_ptr {
1942 found_in_traversal = true;
1943 break;
1944 }
1945 traversal_ptr = lptr[(traversal_ptr - 1) as usize];
1946 if traversal_ptr == lpl {
1947 break;
1948 }
1949 }
1950
1951 prop_assert!(found_in_traversal, "Node {node_idx}: lstptr result {found_ptr} should be reachable from lpl {lpl}");
1952
1953 neighbor_count += 1;
1954 current = lptr[(current - 1) as usize];
1955
1956 if current == lpl {
1957 break;
1958 }
1959 }
1960
1961 prop_assert!(neighbor_count >= 2, "Node {node_idx} should have at least 2 neighbors, found {neighbor_count}");
1962 }
1963 }
1964 }
1965
1966 proptest! {
1967 #[test]
1968 fn test_swap(n in 6..20i32) {
1969 let (x, y, z) = fibonacci_sphere(n as usize);
1970 let (mut list, mut lptr, mut lend, _) = create_triangulation(n, &x, &y, &z);
1971
1972 let nrow = 6i32;
1973 let max_triangles = (2 * n - 4) as usize;
1974 let mut ltri = vec![0i32; (nrow as usize) * max_triangles];
1975 let mut nt = 0i32;
1976 let mut ier = 0i32;
1977
1978 unsafe {
1979 trlist(
1980 &raw const n,
1981 list.as_ptr(),
1982 lptr.as_ptr(),
1983 lend.as_ptr(),
1984 &raw const nrow,
1985 &raw mut nt,
1986 ltri.as_mut_ptr(),
1987 &raw mut ier,
1988 );
1989 }
1990
1991 prop_assert_eq!(ier, 0, "trlist failed");
1992 prop_assert!(nt >= 2, "Need at least 2 triangles for a swap");
1993
1994
1995 'outer: for t1 in 0..(nt as usize) {
1996 for t2 in (t1 + 1)..(nt as usize) {
1997 let v1_0 = ltri[t1 * 6 + 0];
1998 let v1_1 = ltri[t1 * 6 + 1];
1999 let v1_2 = ltri[t1 * 6 + 2];
2000
2001 let v2_0 = ltri[t2 * 6 + 0];
2002 let v2_1 = ltri[t2 * 6 + 1];
2003 let v2_2 = ltri[t2 * 6 + 2];
2004
2005 let verts1 = [v1_0, v1_1, v1_2];
2006 let verts2 = [v2_0, v2_1, v2_2];
2007
2008 let mut shared = vec![];
2009 let mut unique1 = None;
2010 let mut unique2 = None;
2011
2012 for &v in &verts1 {
2013 if verts2.contains(&v) {
2014 shared.push(v);
2015 } else {
2016 unique1 = Some(v);
2017 }
2018 }
2019
2020 for &v in &verts2 {
2021 if !verts1.contains(&v) {
2022 unique2 = Some(v);
2023 }
2024 }
2025
2026 if !(shared.len() == 2 && unique1.is_some() && unique2.is_some()) {
2027 continue;
2028 }
2029
2030 let io1 = shared[0];
2031 let io2 = shared[1];
2032 let in1 = unique1.unwrap();
2033 let in2 = unique2.unwrap();
2034
2035 let lpl_io1 = lend[(io1 - 1) as usize];
2036 let ptr_io2 = find_node_pointer(lpl_io1, io2, &list, &lptr);
2037
2038 if !(ptr_io2 > 0 && list[(ptr_io2 - 1) as usize].abs() == io2) {
2039 continue;
2040 }
2041
2042 let lpl_in1 = lend[(in1 - 1) as usize];
2043 let ptr_in2_check = find_node_pointer(lpl_in1, in2, &list, &lptr);
2044 if list[(ptr_in2_check - 1) as usize].abs() == in2 {
2045 continue;
2046 }
2047
2048 if !should_swap(in1, in2, io1, io2, &x, &y, &z) {
2049 continue;
2050 }
2051
2052 let mut lp21 = 0i32;
2053 unsafe {
2054 swap(
2055 &raw const in1,
2056 &raw const in2,
2057 &raw const io1,
2058 &raw const io2,
2059 list.as_mut_ptr(),
2060 lptr.as_mut_ptr(),
2061 lend.as_mut_ptr(),
2062 &raw mut lp21,
2063 );
2064 };
2065
2066 prop_assert!(lp21 > 0, "Swap should succeed with lp21 > 0");
2067
2068 let lpl_in2 = lend[(in2 - 1) as usize];
2069 let ptr_in1 = find_node_pointer(lpl_in2, in1, &list, &lptr);
2070 prop_assert!(ptr_in1 > 0, "IN1 should be neighbor of IN2 after swap");
2071 prop_assert_eq!(list[(ptr_in1 - 1) as usize].abs(), in1);
2072
2073 break 'outer;
2074 }
2075 }
2076 }
2077 }
2078
2079 proptest! {
2080 #[test]
2081 fn test_optim(n in 8..30i32) {
2082 let (x, y, z) = fibonacci_sphere(n as usize);
2083 let (mut list, mut lptr, mut lend, _) = create_triangulation(n, &x, &y, &z);
2084
2085 let mut na = 0i32;
2086 let max_arcs = (3 * n) as usize;
2087 let mut iwk = vec![0i32; 2 * max_arcs];
2088
2089 for node in 1..n {
2090 let lpl = lend[(node - 1) as usize];
2091 let mut current = lpl;
2092
2093 loop {
2094 let neighbor = list[(current - 1) as usize].abs();
2095 if node < neighbor {
2096 iwk[na as usize * 2] = node;
2097 iwk[na as usize * 2 + 1] = neighbor;
2098 na += 1;
2099 if na >= 20 {
2100 break;
2101 }
2102 }
2103
2104 current = lptr[(current - 1) as usize];
2105 if current == lpl {
2106 break;
2107 }
2108 }
2109
2110 if na >= 20 {
2111 break;
2112 }
2113 }
2114
2115 prop_assert!(na > 0, "Should have at least one arc to optimize");
2116
2117 let mut nit = 100i32;
2118 let mut ier = 0i32;
2119
2120 unsafe {
2121 optim(
2122 x.as_ptr(),
2123 y.as_ptr(),
2124 z.as_ptr(),
2125 &raw const na,
2126 list.as_mut_ptr(),
2127 lptr.as_mut_ptr(),
2128 lend.as_mut_ptr(),
2129 &raw mut nit,
2130 iwk.as_mut_ptr(),
2131 &raw mut ier,
2132 );
2133 }
2134
2135 prop_assert!(ier == 0 || ier == 1, "optim failed");
2136
2137 check_triangulation(n, &list, &lend, &lptr);
2138
2139 if ier == 0 {
2140 prop_assert!(nit < 100, "Converged but used all iterations");
2141 }
2142 }
2143 }
2144
2145 proptest! {
2146 #[test]
2147 fn test_edge(n in 8..25i32) {
2148 let (x, y, z) = fibonacci_sphere(n as usize);
2149 let (mut list, mut lptr, mut lend, _) = create_triangulation(n, &x, &y, &z);
2150
2151 let mut in1 = 1i32;
2152 let mut in2 = 2i32;
2153 let mut found_non_adjacent = false;
2154
2155 'search: for i in 1..=n {
2156 let lpl = lend[(i - 1) as usize];
2157 let mut current = lpl;
2158 let mut neighbors = vec![];
2159
2160 loop {
2161 let neighbor = list[(current - 1) as usize].abs();
2162 neighbors.push(neighbor);
2163 current = lptr[(current - 1) as usize];
2164 if current == lpl {
2165 break;
2166 }
2167 }
2168
2169 for j in (i + 1)..=n {
2170 if !neighbors.contains(&j) && i != j {
2171 in1 = i;
2172 in2 = j;
2173 found_non_adjacent = true;
2174 break 'search;
2175 }
2176 }
2177 }
2178
2179 if !found_non_adjacent {
2180 return Ok(());
2181 }
2182
2183 let max_lwk = (n - 3) as usize;
2184 let mut lwk = max_lwk as i32;
2185 let mut iwk = vec![0i32; 2 * max_lwk];
2186 let mut ier = 0i32;
2187
2188 unsafe {
2189 edge(
2190 &raw const in1,
2191 &raw const in2,
2192 x.as_ptr(),
2193 y.as_ptr(),
2194 z.as_ptr(),
2195 &raw mut lwk,
2196 iwk.as_mut_ptr(),
2197 list.as_mut_ptr(),
2198 lptr.as_mut_ptr(),
2199 lend.as_mut_ptr(),
2200 &raw mut ier
2201 );
2202 }
2203
2204 prop_assert!(ier == 0 || ier == 5, "edge failed");
2205
2206 let lpl_in1 = lend[(in1 - 1) as usize];
2207 let ptr_in2 = find_node_pointer(lpl_in1, in2, &list, &lptr);
2208 let is_adjacent = list[(ptr_in2 - 1) as usize].abs() == in2;
2209 prop_assert!(is_adjacent, "Nodes {in1} and {in2} should be adjacent after edge, lwk={lwk}");
2210
2211 check_triangulation(n, &list, &lend, &lptr);
2212
2213 prop_assert!(lwk >= 0 && lwk <= max_lwk as i32, "lwk should be in valid range [0, {max_lwk}], got {lwk}");
2214 }
2215 }
2216
2217 proptest! {
2218 #[test]
2219 fn test_insert(n in 6..15i32, insertion_point in 1..3usize) {
2220 let (x, y, z) = fibonacci_sphere(n as usize);
2221 let (mut list, mut lptr, lend, mut lnew) = create_triangulation(n, &x, &y, &z);
2222
2223 let lpl = lend[0];
2224
2225 let mut lp = lpl;
2226 for _ in 0..insertion_point {
2227 lp = lptr[(lp - 1) as usize];
2228 }
2229
2230 let k = n;
2231
2232 let mut current = lpl;
2233 let mut already_neighbor = false;
2234 loop {
2235 if list[(current - 1) as usize].abs() == k as i32 {
2236 already_neighbor = true;
2237 break;
2238 }
2239
2240 current = lptr[(current - 1) as usize];
2241 if current == lpl {
2242 break;
2243 }
2244 }
2245
2246 if already_neighbor {
2247 return Ok(());
2248 }
2249
2250 let lnew_before = lnew;
2251
2252 let current_size = list.len();
2253 list.resize(current_size + 1, 0);
2254 lptr.resize(current_size + 1, 0);
2255
2256 unsafe {
2257 insert(
2258 &raw const k,
2259 &raw const lp,
2260 list.as_mut_ptr(),
2261 lptr.as_mut_ptr(),
2262 &raw mut lnew
2263 );
2264 }
2265
2266 prop_assert_eq!(lnew, lnew_before + 1, "lnew should increment by 1");
2267 prop_assert_eq!(list[(lnew_before - 1) as usize], k, "k should be at position {}", lnew_before);
2268 let next_after_insert = lptr[(lp - 1) as usize];
2269 prop_assert_eq!(next_after_insert, lnew_before, "lp should now point ot new entry");
2270
2271 let next_of_new = lptr[(lnew_before - 1) as usize];
2272 prop_assert!(next_of_new > 0 && next_of_new <= list.len() as i32, "New entry should point to valid position");
2273 }
2274 }
2275
2276 #[test]
2277 fn test_bdyadd() {
2278 let (x, y, z) = hemisphere_fixed();
2279 let n_base = 5i32;
2280
2281 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n_base, &x, &y, &z);
2282
2283 let mut nodes = vec![0i32; n_base as usize];
2284 let mut nb = 0i32;
2285 let mut na = 0i32;
2286 let mut nt = 0i32;
2287
2288 unsafe {
2289 bnodes(
2290 &raw const n_base,
2291 list.as_ptr(),
2292 lptr.as_ptr(),
2293 lend.as_ptr(),
2294 nodes.as_mut_ptr(),
2295 &raw mut nb,
2296 &raw mut na,
2297 &raw mut nt,
2298 );
2299 }
2300
2301 if nb < 2 || lnew as usize >= list.len() {
2302 return;
2303 }
2304
2305 let i1 = nodes[0];
2306 let i2 = nodes[1];
2307 let kk = n_base + 1;
2308
2309 let new_size = list.len() + 10;
2310 list.resize(new_size, 0);
2311 lptr.resize(new_size, 0);
2312 lend.resize(n_base as usize + 1, 0);
2313
2314 let lnew_before = lnew;
2315
2316 unsafe {
2317 bdyadd(
2318 &raw const kk,
2319 &raw const i1,
2320 &raw const i2,
2321 list.as_mut_ptr(),
2322 lptr.as_mut_ptr(),
2323 lend.as_mut_ptr(),
2324 &raw mut lnew,
2325 );
2326 }
2327
2328 assert!(lnew > lnew_before, "bdyadd should increment lnew");
2329 assert!(
2330 lend[(kk - 1) as usize] > 0,
2331 "New node should have valid lend"
2332 );
2333
2334 let lpl_kk = lend[(kk - 1) as usize];
2335 let ptr_i1 = find_node_pointer(lpl_kk, i1, &list, &lptr);
2336 assert!(ptr_i1 > 0, "i1 should be a neighbor of kk");
2337 assert_eq!(list[(ptr_i1 - 1) as usize].abs(), i1);
2338
2339 let ptr_i2 = find_node_pointer(lpl_kk, i2, &list, &lptr);
2340 assert!(ptr_i2 > 0, "i2 should be a neighbor of kk");
2341 assert_eq!(list[(ptr_i2 - 1) as usize].abs(), i2);
2342
2343 check_triangulation(kk, &list, &lend, &lptr);
2344 }
2345
2346 proptest! {
2347 #[test]
2348 fn test_intadd(n in 6..20i32) {
2349 let (mut x, mut y, mut z) = fibonacci_sphere(n as usize);
2350 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n, &x, &y, &z);
2351
2352 let nrow = 6i32;
2353 let max_triangles = (2 * n - 4) as usize;
2354 let mut ltri = vec![0i32; (nrow as usize) * max_triangles];
2355 let mut nt = 0i32;
2356 let mut ier = 0i32;
2357
2358 unsafe {
2359 trlist(
2360 &raw const n,
2361 list.as_ptr(),
2362 lptr.as_ptr(),
2363 lend.as_ptr(),
2364 &raw const nrow,
2365 &raw mut nt,
2366 ltri.as_mut_ptr(),
2367 &raw mut ier,
2368 );
2369 }
2370
2371 if ier != 0 || nt < 1 {
2372 return Ok(());
2373 }
2374
2375 let i1 = ltri[0];
2376 let i2 = ltri[1];
2377 let i3 = ltri[2];
2378
2379 let x1 = x[(i1 - 1) as usize];
2380 let y1 = y[(i1 - 1) as usize];
2381 let z1 = z[(i1 - 1) as usize];
2382 let x2 = x[(i2 - 1) as usize];
2383 let y2 = y[(i2 - 1) as usize];
2384 let z2 = z[(i2 - 1) as usize];
2385 let x3 = x[(i3 - 1) as usize];
2386 let y3 = y[(i3 - 1) as usize];
2387 let z3 = z[(i3 - 1) as usize];
2388
2389 let px = 0.25 * x1 + 0.25 * x2 + 0.5 * x3;
2390 let py = 0.25 * y1 + 0.25 * y2 + 0.5 * y3;
2391 let pz = 0.25 * z1 + 0.25 * z2 + 0.5 * z3;
2392
2393 let [px, py, pz] = normalize(&[px, py, pz]);
2394 let p = [px, py, pz];
2395 let mut b1 = 0.0;
2396 let mut b2 = 0.0;
2397 let mut b3 = 0.0;
2398 let mut tf_i1 = 0i32;
2399 let mut tf_i2 = 0i32;
2400 let mut tf_i3 = 0i32;
2401 let nst = 1i32;
2402
2403 unsafe {
2404 trfind(
2405 &raw const nst,
2406 p.as_ptr(),
2407 &raw const n,
2408 x.as_ptr(),
2409 y.as_ptr(),
2410 z.as_ptr(),
2411 list.as_ptr(),
2412 lptr.as_ptr(),
2413 lend.as_ptr(),
2414 &raw mut b1,
2415 &raw mut b2,
2416 &raw mut b3,
2417 &raw mut tf_i1,
2418 &raw mut tf_i2,
2419 &raw mut tf_i3,
2420 );
2421 }
2422
2423 if tf_i3 == 0 {
2424 return Ok(());
2425 }
2426
2427 let kk = n + 1;
2428 x.push(px);
2429 y.push(py);
2430 z.push(pz);
2431
2432 let new_list_size = list.len() + 6;
2433 list.resize(new_list_size, 0);
2434 lptr.resize(new_list_size, 0);
2435 lend.resize(kk as usize, 0);
2436
2437 let lnew_before = lnew;
2438
2439 unsafe {
2440 intadd(
2441 &raw const kk,
2442 &raw const tf_i1,
2443 &raw const tf_i2,
2444 &raw const tf_i3,
2445 list.as_mut_ptr(),
2446 lptr.as_mut_ptr(),
2447 lend.as_mut_ptr(),
2448 &raw mut lnew,
2449 );
2450 }
2451
2452 prop_assert_eq!(lnew, lnew_before + 6, "intadd should increment lnew by 3");
2453 prop_assert!(lend[(kk - 1) as usize] > 0, "New node should have valid lend");
2454
2455 for &vertex in &[tf_i1, tf_i2, tf_i3] {
2456 let lpl_kk = lend[(kk - 1) as usize];
2457 let ptr = find_node_pointer(lpl_kk, vertex, &list, &lptr);
2458 prop_assert!(ptr > 0, "Vertex {vertex} should be a neighbor of kk");
2459 prop_assert_eq!(list[(ptr - 1) as usize].abs(), vertex, "kk should have vertex {} as a neighbor", vertex);
2460 }
2461
2462 for &vertex in &[tf_i1, tf_i2, tf_i3] {
2463 let lpl_vertex = lend[(vertex - 1) as usize];
2464 let ptr = find_node_pointer(lpl_vertex, kk, &list, &lptr);
2465 prop_assert!(ptr > 0, "kk should be a neighbor of vertex {}", vertex);
2466 prop_assert_eq!(list[(ptr - 1) as usize].abs(), kk, "Vertex {} should have kk as a neighbor", vertex);
2467 }
2468
2469 check_triangulation(kk, &list, &lend, &lptr);
2470 }
2471 }
2472
2473 proptest! {
2474 #[test]
2475 fn test_covsph(n in 5..25i32) {
2476 let (x, y, z) = hemisphere(n as usize);
2477 let n_hemi = x.len() as i32;
2478
2479 if n_hemi < 4 {
2480 return Ok(());
2481 }
2482
2483 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n_hemi, &x, &y, &z);
2484
2485 let mut nodes = vec![0i32; n_hemi as usize];
2486 let mut nb = 0i32;
2487 let mut na = 0i32;
2488 let mut nt = 0i32;
2489
2490 unsafe {
2491 bnodes(
2492 &raw const n_hemi,
2493 list.as_ptr(),
2494 lptr.as_ptr(),
2495 lend.as_ptr(),
2496 nodes.as_mut_ptr(),
2497 &raw mut nb,
2498 &raw mut na,
2499 &raw mut nt,
2500 );
2501 }
2502
2503 if nb < 3 {
2504 return Ok(());
2505 }
2506
2507 let new_size = list.len() + (2 * nb as usize);
2508 list.resize(new_size, 0);
2509 lptr.resize(new_size, 0);
2510 lend.resize(n_hemi as usize + 1, 0);
2511
2512 let n0 = nodes[0];
2513 let kk = n_hemi + 1;
2514
2515 unsafe {
2516 covsph(
2517 &raw const kk,
2518 &raw const n0,
2519 list.as_mut_ptr(),
2520 lptr.as_mut_ptr(),
2521 lend.as_mut_ptr(),
2522 &raw mut lnew,
2523 );
2524 }
2525
2526 for i in 0..nb as usize {
2527 let lpl = lend[(nodes[i] - 1) as usize];
2528 prop_assert!(list[(lpl - 1) as usize] > 0);
2529 }
2530
2531 let lpl_kk = lend[(kk - 1) as usize];
2532 for i in 0..nb as usize {
2533 let ptr = find_node_pointer(lpl_kk, nodes[i], &list, &lptr);
2534 prop_assert!(ptr > 0);
2535 }
2536
2537 check_triangulation(kk, &list, &lend, &lptr);
2538 }
2539 }
2540
2541 proptest! {
2542 #[test]
2543 fn test_delnod(mut n in 5..25i32) {
2544 let (mut x, mut y, mut z) = fibonacci_sphere(n as usize);
2545 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n, &x, &y, &z);
2546
2547 let k = n / 2;
2548 let starting_n = n;
2549
2550 let lwk_max = n;
2551 let mut lwk = lwk_max;
2552 let mut iwk = vec![0i32; 2 * lwk_max as usize];
2553 let mut ier = 0i32;
2554
2555 unsafe {
2556 delnod(
2557 &raw const k,
2558 &raw mut n,
2559 x.as_mut_ptr(),
2560 y.as_mut_ptr(),
2561 z.as_mut_ptr(),
2562 list.as_mut_ptr(),
2563 lptr.as_mut_ptr(),
2564 lend.as_mut_ptr(),
2565 &raw mut lnew,
2566 &raw mut lwk,
2567 iwk.as_mut_ptr(),
2568 &raw mut ier,
2569 );
2570 }
2571
2572 prop_assert!(ier == 0 || ier == 6, "delnod failed");
2573 prop_assert_eq!(n, starting_n - 1, "n should be decremented by 1");
2574
2575 check_triangulation(n, &list, &lend, &lptr);
2576 }
2577 }
2578
2579 proptest! {
2580 #[test]
2581 fn test_jrand(n in 100..150, mut ix in 1..30000, mut iy in 1..30000, mut iz in 1..30000) {
2582 let x = unsafe { jrand(&raw const n, &raw mut ix, &raw mut iy, &raw mut iz) };
2583 prop_assert!(x <= n);
2584 }
2585 }
2586
2587 proptest! {
2588 #[test]
2589 fn test_nearnd(n in 5..25i32, p in unit_vector()) {
2590 let (x, y, z) = fibonacci_sphere(n as usize);
2591 let (list, lptr, lend, _) = create_triangulation(n, &x, &y, &z);
2592
2593 let mut al = 0.0;
2594 let ist = 1;
2595 let nearest = unsafe {
2596 nearnd(
2597 p.as_ptr(),
2598 &raw const ist,
2599 &raw const n,
2600 x.as_ptr(),
2601 y.as_ptr(),
2602 z.as_ptr(),
2603 list.as_ptr(),
2604 lptr.as_ptr(),
2605 lend.as_ptr(),
2606 &raw mut al,
2607 )
2608 };
2609
2610 let mut naive_nearest = 0;
2611 let mut naive_nearest_distance = f64::MAX;
2612 for i in 0..x.len() {
2613 let dist = ((p[0] - x[i]).powi(2) + (p[1] - y[i]).powi(2) + (p[2] - z[i]).powi(2)).sqrt();
2614 if dist < naive_nearest_distance {
2615 naive_nearest = i;
2616 }
2617 naive_nearest_distance = naive_nearest_distance.min(dist);
2618 }
2619 prop_assert_eq!(nearest, naive_nearest as i32 + 1);
2620 prop_assert!((naive_nearest_distance - al).abs() < 0.10, "Incorrect al value: {al}, expected: {naive_nearest_distance}");
2621
2622 }
2623 }
2624
2625 proptest! {
2626 #[test]
2627 fn test_getnp(n in 10..25i32, l in 3..8i32) {
2628 let (x, y, z) = fibonacci_sphere(n as usize);
2629 let (list, lptr, mut lend, _) = create_triangulation(n, &x, &y, &z);
2630 let mut npts = vec![0i32; l as usize];
2631 npts[0] = n / 2;
2632 let mut distances = vec![0.0f64; l as usize];
2633 distances[0] = -1.0;
2634
2635 for i in 2..=l {
2636 let current_l = i;
2637 let mut df = 0.0f64;
2638 let mut ier = 0i32;
2639
2640 unsafe {
2641 getnp(
2642 x.as_ptr(),
2643 y.as_ptr(),
2644 z.as_ptr(),
2645 list.as_ptr(),
2646 lptr.as_ptr(),
2647 lend.as_mut_ptr(),
2648 &raw const current_l,
2649 npts.as_mut_ptr(),
2650 &raw mut df,
2651 &raw mut ier,
2652 );
2653 }
2654
2655 prop_assert_eq!(ier, 0, "getnp failed");
2656 prop_assert!(df >= -1.0 && df <= 1.0, "df: {df}");
2657 distances[(i - 1) as usize] = df;
2658 prop_assert!(df >= distances[(i - 2) as usize], "distance should be increasing: df={df}, prev={}", distances[(i - 2) as usize]);
2659 }
2660
2661 prop_assert_eq!(npts.into_iter().collect::<HashSet<_>>().len(), l as usize, "All {} nodes in sequence should be distinct", l);
2662 }
2663 }
2664
2665 proptest! {
2666 #[test]
2667 fn test_nbcnt(n in 6..30i32) {
2668 let (x, y, z) = fibonacci_sphere(n as usize);
2669 let (_, lptr, lend, _) = create_triangulation(n, &x, &y, &z);
2670
2671 for node_idx in 1..=n {
2672 let lpl = lend[(node_idx - 1) as usize];
2673 let count = unsafe { nbcnt(&raw const lpl, lptr.as_ptr()) };
2674 let mut manual_count = 0;
2675 let mut current = lpl;
2676 loop {
2677 manual_count += 1;
2678 current = lptr[(current - 1) as usize];
2679 if current == lpl {
2680 break;
2681 }
2682 prop_assert!(manual_count <= 6 * (n - 2), "infinite loop detected");
2683 }
2684 prop_assert_eq!(count, manual_count, "nbcnt shoudl match manula count for node {}", node_idx);
2685 }
2686 }
2687 }
2688
2689 proptest! {
2690 #[test]
2691 fn test_delnb(n in 6..25i32) {
2692 let (x, y, z) = fibonacci_sphere(n as usize);
2693 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n, &x, &y, &z);
2694
2695 let n0 = n / 2;
2696 let lpl_n0 = lend[(n0 - 1) as usize];
2697
2698 let first_neighbor_pos = lptr[(lpl_n0 - 1) as usize];
2699 let nb = list[(first_neighbor_pos - 1) as usize];
2700
2701 if nb < 1 || nb > n {
2702 return Ok(());
2703 }
2704
2705 let lnew_before = lnew;
2706 let mut lph = 0i32;
2707
2708 unsafe {
2709 delnb(
2710 &raw const n0,
2711 &raw const nb,
2712 &raw const n,
2713 list.as_mut_ptr(),
2714 lptr.as_mut_ptr(),
2715 lend.as_mut_ptr(),
2716 &raw mut lnew,
2717 &raw mut lph,
2718 );
2719 }
2720
2721 prop_assert!(lph > 0, "delnb failed");
2722 prop_assert!(lnew <= lnew_before, "lnew should decrease or stay the same, got {lnew} expected <= {lnew_before}");
2723
2724 check_triangulation(n, &list, &lend, &lptr);
2725 }
2726 }
2727
2728 proptest! {
2729 #[test]
2730 fn test_delarc(n in 5..25i32) {
2731 let (x, y, z) = hemisphere(n as usize);
2732 let (mut list, mut lptr, mut lend, mut lnew) = create_triangulation(n, &x, &y, &z);
2733
2734 let mut nodes = vec![0i32; n as usize];
2735 let mut nb = 0i32;
2736 let mut na = 0i32;
2737 let mut nt = 0i32;
2738
2739 unsafe {
2740 bnodes(
2741 &raw const n,
2742 list.as_ptr(),
2743 lptr.as_ptr(),
2744 lend.as_ptr(),
2745 nodes.as_mut_ptr(),
2746 &raw mut nb,
2747 &raw mut na,
2748 &raw mut nt,
2749 );
2750 }
2751
2752 if nb < 2 {
2753 return Ok(());
2754 }
2755
2756 let io1 = nodes[0];
2757 let io2 = nodes[1];
2758
2759 let lpl_io1 = lend[(io1 - 1) as usize];
2760 let ptr_io2 = find_node_pointer(lpl_io1, io2, &list, &lptr);
2761 let are_neighbors = list[(ptr_io2 - 1) as usize].abs() == io2;
2762
2763 if !are_neighbors {
2764 return Ok(());
2765 }
2766
2767 let mut ier = 0i32;
2768
2769 unsafe {
2770 delarc(
2771 &raw const n,
2772 &raw const io1,
2773 &raw const io2,
2774 list.as_mut_ptr(),
2775 lptr.as_mut_ptr(),
2776 lend.as_mut_ptr(),
2777 &raw mut lnew,
2778 &raw mut ier,
2779 );
2780 }
2781
2782 prop_assert_eq!(ier, 0, "delarc failed");
2783
2784 let lpl_io1_new = lend[(io1 - 1) as usize];
2785 let ptr_io2_new = find_node_pointer(lpl_io1_new, io2, &list, &lptr);
2786 let still_neighbors = list[(ptr_io2_new - 1) as usize].abs() == io2;
2787 prop_assert!(!still_neighbors, "io1 and io2 should no longer be neighbors");
2788
2789 check_triangulation(n, &list, &lend, &lptr);
2790 }
2791 }
2792
2793 proptest! {
2794 #[test]
2795 fn test_inside(n in 6..25i32, p in unit_vector()) {
2796 let (x, y, z) = fibonacci_sphere(n as usize);
2797
2798 let nv = 4i32;
2799 let listv = vec![1i32, 2, 3, 4];
2800
2801 let mut ier = 0i32;
2802
2803 let _ = unsafe {
2804 inside(
2805 p.as_ptr(),
2806 &raw const n,
2807 x.as_ptr(),
2808 y.as_ptr(),
2809 z.as_ptr(),
2810 &raw const nv,
2811 listv.as_ptr(),
2812 &raw mut ier,
2813 )
2814 };
2815
2816 prop_assert_eq!(ier, 0, "inside failed");
2817 }
2818 }
2819
2820 proptest! {
2821 #[test]
2822 fn test_crlist(n in 5i32..25i32) {
2823 let (x, y, z) = fibonacci_sphere(n as usize);
2824 let (list, lptr, lend, lnew) = create_triangulation(n, &x, &y, &z);
2825
2826 if lnew <= 0 {
2827 return Ok(());
2828 }
2829
2830 let nrow = 6i32;
2831 let max_triangles = (2 * n - 4) as usize;
2832 let mut ltri = vec![0i32; (nrow as usize) * max_triangles];
2833
2834
2835 let ncol = n;
2836 let max_centers = (2 * n - 4) as usize;
2837 let mut listc = vec![0i32; 3 * max_triangles];
2838 let mut nb = 0i32;
2839 let mut xc = vec![0.0f64; max_centers];
2840 let mut yc = vec![0.0f64; max_centers];
2841 let mut zc = vec![0.0f64; max_centers];
2842 let mut rc = vec![0.0f64; max_centers];
2843 let mut ier_crlist = 0i32;
2844 let mut lptr_copy = lptr.clone();
2845 let mut lnew_copy = lnew;
2846
2847 unsafe {
2848 crlist(
2849 &raw const n,
2850 &raw const ncol,
2851 x.as_ptr(),
2852 y.as_ptr(),
2853 z.as_ptr(),
2854 list.as_ptr(),
2855 lend.as_ptr(),
2856 lptr_copy.as_mut_ptr(),
2857 &raw mut lnew_copy,
2858 ltri.as_mut_ptr(),
2859 listc.as_mut_ptr(),
2860 &raw mut nb,
2861 xc.as_mut_ptr(),
2862 yc.as_mut_ptr(),
2863 zc.as_mut_ptr(),
2864 rc.as_mut_ptr(),
2865 &raw mut ier_crlist,
2866 );
2867 };
2868
2869 if nb > 0 {
2870 return Ok(());
2871 }
2872
2873 prop_assert_eq!(ier_crlist, 0, "crlist failed");
2874
2875 let nt = 2 * n - 4;
2876 for i in 0..nt as usize {
2877 let norm_sq = xc[i] * xc[i] + yc[i] * yc[i] + zc[i] * zc[i];
2878 prop_assert!((norm_sq - 1.0).abs() < 1e-10, "circumcenter {i} should be a unit vector, norm_sq={norm_sq}");
2879
2880 let base = i * 6;
2881 let v1 = (ltri[base] - 1) as usize;
2882 let v2 = (ltri[base + 1] - 1) as usize;
2883 let v3 = (ltri[base + 2] - 1) as usize;
2884
2885 if v1 >= n as usize || v2 >= n as usize || v3 >= n as usize {
2886 continue;
2887 }
2888
2889 let c = [xc[i], yc[i], zc[i]];
2890 let p1 = [x[v1], y[v1], z[v1]];
2891 let p2 = [x[v2], y[v2], z[v2]];
2892 let p3 = [x[v3], y[v3], z[v3]];
2893
2894 let d1 = spherical_distance(&c, &p1);
2895 let d2 = spherical_distance(&c, &p2);
2896 let d3 = spherical_distance(&c, &p3);
2897
2898 prop_assert!((d1 - d2).abs() < 1e-9 && (d2 - d3).abs() < 1e-9, "circumcenter {i} not equidistant: d1={d1}, d2={d2}, d3={d3}, rc={}", rc[i]);
2899 prop_assert!((rc[i] - d1).abs() < 1e-9, "circumradius mismatch for triangle {i}: rc={}, actual distance={d1}", rc[i]);
2900 }
2901 }
2902 }
2903
2904 fn cross_product(v1: &[f64; 3], v2: &[f64; 3]) -> [f64; 3] {
2905 [
2906 v1[1] * v2[2] - v1[2] * v2[1],
2907 v1[2] * v2[0] - v1[0] * v2[2],
2908 v1[0] * v2[1] - v1[1] * v2[0],
2909 ]
2910 }
2911
2912 fn norm(v: &[f64; 3]) -> f64 {
2913 (v[0].powi(2) + v[1].powi(2) + v[2].powi(2)).sqrt()
2914 }
2915
2916 fn dot(v1: &[f64; 3], v2: &[f64; 3]) -> f64 {
2917 v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
2918 }
2919
2920 proptest! {
2921 #[test]
2922 fn test_intrsc(p1 in unit_vector(), p2 in unit_vector(), q in unit_vector()) {
2923 let c12 = cross_product(&p1, &p2);
2924 let norm_c12 = norm(&c12);
2925 if norm_c12 < f64::EPSILON {
2926 return Ok(());
2927 }
2928 let c12 = normalize(&c12);
2929
2930 let cn = cross_product(&p1, &q);
2931 let norm_cn = norm(&cn);
2932 if norm_cn < f64::EPSILON {
2933 return Ok(());
2934 }
2935
2936 let mut p = [0f64; 3];
2937 let mut ier = 0i32;
2938
2939 unsafe {
2940 intrsc(
2941 p1.as_ptr(),
2942 p2.as_ptr(),
2943 cn.as_ptr(),
2944 p.as_mut_ptr(),
2945 &raw mut ier,
2946 );
2947 };
2948
2949 prop_assert_eq!(ier, 0, "intrsc failed");
2950
2951 let norm_p = norm(&p);
2952 prop_assert!((norm_p - 1.0).abs() < 1e-10, "intersection point should be on unit sphere, norm={norm_p}");
2953
2954 let dot_p_cn = dot(&p, &cn);
2955 prop_assert!(dot_p_cn.abs() < 1e-10, "intersection should be perpendicular to cn, dot={dot_p_cn}");
2956
2957 let dot_p_c12 = dot(&p, &c12);
2958 prop_assert!(dot_p_c12.abs() < 1e-10, "intersection should be perpendicular to c12 normal, dot={dot_p_c12}");
2959 }
2960 }
2961
2962 proptest! {
2963 #[test]
2964 fn test_r83vec_normalize(x in -10.0f64..10.0, y in -10.0f64..10.0, z in -10.0f64..10.0f64) {
2965 let expected_vector = normalize(&[x, y, z]);
2966 let mut xv = [x];
2967 let mut yv = [y];
2968 let mut zv = [z];
2969 let n = 1i32;
2970 unsafe {
2971 r83vec_normalize(
2972 &raw const n,
2973 xv.as_mut_ptr(),
2974 yv.as_mut_ptr(),
2975 zv.as_mut_ptr()
2976 );
2977 }
2978
2979 prop_assert!((xv[0] - expected_vector[0]).abs() < 1e-10, "normalize failed. Expected x={} got x={}", expected_vector[0], xv[0]);
2980 prop_assert!((yv[0] - expected_vector[1]).abs() < 1e-10, "normalize failed. Expected y={} got y={}", expected_vector[1], yv[0]);
2981 prop_assert!((zv[0] - expected_vector[2]).abs() < 1e-10, "normalize failed. Expected z={} got z={}", expected_vector[2], zv[0]);
2982 }
2983 }
2984
2985 proptest! {
2986 #[test]
2987 fn test_trlist2(n in 5i32..25i32) {
2988 let (x, y, z) = fibonacci_sphere(n as usize);
2989 let (list, lptr, lend, lnew) = create_triangulation(n, &x, &y, &z);
2990
2991 if lnew <= 0 {
2992 return Ok(());
2993 }
2994
2995 let max_triangles = (2 * n - 4) as usize;
2996 let mut ltri = vec![0i32; 3 * max_triangles];
2997 let mut nt = 0i32;
2998 let mut ier = 0i32;
2999
3000 unsafe {
3001 trlist2(
3002 &raw const n,
3003 list.as_ptr(),
3004 lptr.as_ptr(),
3005 lend.as_ptr(),
3006 &raw mut nt,
3007 ltri.as_mut_ptr(),
3008 &raw mut ier,
3009 );
3010 }
3011
3012 prop_assert_eq!(ier, 0, "trlist2 failed");
3013 prop_assert!(nt > 0, "should have at least one triangle");
3014 prop_assert!(nt as usize <= max_triangles, "nt exceeds maximum");
3015
3016 let expected_nt = 2 * n - 4;
3017 prop_assert_eq!(nt, expected_nt, "triangle count mismatch: expected {} for n={}, got {}", expected_nt, n, nt);
3018
3019 for t in 0..nt as usize {
3020 let base = t * 3;
3021 let v1 = ltri[base];
3022 let v2 = ltri[base + 1];
3023 let v3 = ltri[base + 2];
3024
3025 prop_assert!(v1 >= 1 && v1 <= n, "invalid v1: {v1}");
3026 prop_assert!(v2 >= 1 && v2 <= n, "invalid v2: {v2}");
3027 prop_assert!(v3 >= 1 && v3 <= n, "invalid v3: {v3}");
3028
3029 prop_assert_ne!(v1, v2, "duplicate vertices in triangle");
3030 prop_assert_ne!(v2, v3, "duplicate vertices in triangle");
3031 prop_assert_ne!(v1, v3, "duplicate vertices in triangle");
3032
3033 prop_assert!(v1 < v2, "v1 should be smallest: v1={v1}, v2={v2}");
3034 prop_assert!(v1 < v3, "v1 should be smallest: v1={v1}, v3={v3}");
3035 }
3036
3037 for t in 1..nt as usize {
3038 let prev_base = (t - 1) * 3;
3039 let curr_base = t * 3;
3040 let prev_v1 = ltri[prev_base];
3041 let curr_v1 = ltri[curr_base];
3042
3043 prop_assert!(curr_v1 >= prev_v1, "triangles not ordered: {prev_v1} before {curr_v1}");
3044 }
3045 }
3046 }
3047}