Blender  V2.93
BLI_mesh_intersect_test.cc
Go to the documentation of this file.
1 /* Apache License, Version 2.0 */
2 
3 #include "testing/testing.h"
4 
5 #include <algorithm>
6 #include <fstream>
7 #include <iostream>
8 
9 #include "PIL_time.h"
10 
11 #include "BLI_array.hh"
12 #include "BLI_math_mpq.hh"
13 #include "BLI_mesh_intersect.hh"
14 #include "BLI_mpq3.hh"
15 #include "BLI_task.h"
16 #include "BLI_vector.hh"
17 
18 #define DO_REGULAR_TESTS 1
19 #define DO_PERF_TESTS 0
20 
21 #ifdef WITH_GMP
22 namespace blender::meshintersect::tests {
23 
24 constexpr bool DO_OBJ = false;
25 
26 /* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */
27 class IMeshBuilder {
28  public:
29  IMesh imesh;
30  IMeshArena arena;
31 
32  /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */
33  static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */
34 
35  static int edge_index(int face_index, int facepos)
36  {
37  return face_index * MAX_FACE_LEN + facepos;
38  }
39 
40  static std::pair<int, int> face_and_pos_for_edge_index(int e_index)
41  {
42  return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN);
43  }
44 
45  /*
46  * Spec should have form:
47  * #verts #faces
48  * mpq_class mpq_class mpq_clas [#verts lines]
49  * int int int ... [#faces lines; indices into verts for given face]
50  */
51  IMeshBuilder(const char *spec)
52  {
53  std::istringstream ss(spec);
54  std::string line;
55  getline(ss, line);
56  std::istringstream hdrss(line);
57  int nv, nf;
58  hdrss >> nv >> nf;
59  if (nv == 0 || nf == 0) {
60  return;
61  }
62  arena.reserve(nv, nf);
63  Vector<const Vert *> verts;
64  Vector<Face *> faces;
65  bool spec_ok = true;
66  int v_index = 0;
67  while (v_index < nv && spec_ok && getline(ss, line)) {
68  std::istringstream iss(line);
69  mpq_class p0;
70  mpq_class p1;
71  mpq_class p2;
72  iss >> p0 >> p1 >> p2;
73  spec_ok = !iss.fail();
74  if (spec_ok) {
75  verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index));
76  }
77  ++v_index;
78  }
79  if (v_index != nv) {
80  spec_ok = false;
81  }
82  int f_index = 0;
83  while (f_index < nf && spec_ok && getline(ss, line)) {
84  std::istringstream fss(line);
85  Vector<const Vert *> face_verts;
86  Vector<int> edge_orig;
87  int fpos = 0;
88  while (spec_ok && fss >> v_index) {
89  if (v_index < 0 || v_index >= nv) {
90  spec_ok = false;
91  continue;
92  }
93  face_verts.append(verts[v_index]);
94  edge_orig.append(edge_index(f_index, fpos));
95  ++fpos;
96  }
97  if (fpos < 3) {
98  spec_ok = false;
99  }
100  if (spec_ok) {
101  Face *facep = arena.add_face(face_verts, f_index, edge_orig);
102  faces.append(facep);
103  }
104  ++f_index;
105  }
106  if (f_index != nf) {
107  spec_ok = false;
108  }
109  if (!spec_ok) {
110  std::cout << "Bad spec: " << spec;
111  return;
112  }
113  imesh = IMesh(faces);
114  }
115 };
116 
117 /* Return a const Face * in mesh with verts equal to v0, v1, and v2, in
118  * some cyclic order; return nullptr if not found.
119  */
120 static const Face *find_tri_with_verts(const IMesh &mesh,
121  const Vert *v0,
122  const Vert *v1,
123  const Vert *v2)
124 {
125  Face f_arg({v0, v1, v2}, 0, NO_INDEX);
126  for (const Face *f : mesh.faces()) {
127  if (f->cyclic_equal(f_arg)) {
128  return f;
129  }
130  }
131  return nullptr;
132 }
133 
134 /* How many instances of a triangle with v0, v1, v2 are in the mesh? */
135 static int count_tris_with_verts(const IMesh &mesh, const Vert *v0, const Vert *v1, const Vert *v2)
136 {
137  Face f_arg({v0, v1, v2}, 0, NO_INDEX);
138  int ans = 0;
139  for (const Face *f : mesh.faces()) {
140  if (f->cyclic_equal(f_arg)) {
141  ++ans;
142  }
143  }
144  return ans;
145 }
146 
147 /* What is the starting position, if any, of the edge (v0, v1), in either order, in f? -1 if none.
148  */
149 static int find_edge_pos_in_tri(const Vert *v0, const Vert *v1, const Face *f)
150 {
151  for (int pos : f->index_range()) {
152  int nextpos = f->next_pos(pos);
153  if (((*f)[pos] == v0 && (*f)[nextpos] == v1) || ((*f)[pos] == v1 && (*f)[nextpos] == v0)) {
154  return static_cast<int>(pos);
155  }
156  }
157  return -1;
158 }
159 
160 # if DO_REGULAR_TESTS
161 TEST(mesh_intersect, Mesh)
162 {
163  Vector<const Vert *> verts;
164  Vector<Face *> faces;
165  IMeshArena arena;
166 
167  verts.append(arena.add_or_find_vert(mpq3(0, 0, 1), 0));
168  verts.append(arena.add_or_find_vert(mpq3(1, 0, 1), 1));
169  verts.append(arena.add_or_find_vert(mpq3(0.5, 1, 1), 2));
170  faces.append(arena.add_face(verts, 0, {10, 11, 12}));
171 
172  IMesh mesh(faces);
173  const Face *f = mesh.face(0);
174  EXPECT_TRUE(f->is_tri());
175 }
176 
177 TEST(mesh_intersect, OneTri)
178 {
179  const char *spec = R"(3 1
180  0 0 0
181  1 0 0
182  1/2 1 0
183  0 1 2
184  )";
185 
186  IMeshBuilder mb(spec);
187  IMesh imesh = trimesh_self_intersect(mb.imesh, &mb.arena);
188  imesh.populate_vert();
189  EXPECT_EQ(imesh.vert_size(), 3);
190  EXPECT_EQ(imesh.face_size(), 1);
191  const Face &f_in = *mb.imesh.face(0);
192  const Face &f_out = *imesh.face(0);
193  EXPECT_EQ(f_in.orig, f_out.orig);
194  for (int i = 0; i < 3; ++i) {
195  EXPECT_EQ(f_in[i], f_out[i]);
196  EXPECT_EQ(f_in.edge_orig[i], f_out.edge_orig[i]);
197  }
198 }
199 
200 TEST(mesh_intersect, TriTri)
201 {
202  const char *spec = R"(6 2
203  0 0 0
204  4 0 0
205  0 4 0
206  1 0 0
207  2 0 0
208  1 1 0
209  0 1 2
210  3 4 5
211  )";
212 
213  /* Second triangle is smaller and congruent to first, resting on same base, partway along. */
214  IMeshBuilder mb(spec);
215  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
216  out.populate_vert();
217  EXPECT_EQ(out.vert_size(), 6);
218  EXPECT_EQ(out.face_size(), 6);
219  if (out.vert_size() == 6 && out.face_size() == 6) {
220  const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0));
221  const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0));
222  const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0));
223  const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0));
224  const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0));
225  const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0));
226  EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr);
227  EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr);
228  if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr &&
229  v5 != nullptr) {
230  EXPECT_EQ(v0->orig, 0);
231  EXPECT_EQ(v1->orig, 1);
232  const Face *f0 = find_tri_with_verts(out, v4, v1, v5);
233  const Face *f1 = find_tri_with_verts(out, v3, v4, v5);
234  const Face *f2 = find_tri_with_verts(out, v0, v3, v5);
235  const Face *f3 = find_tri_with_verts(out, v0, v5, v2);
236  const Face *f4 = find_tri_with_verts(out, v5, v1, v2);
237  EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr &&
238  f4 != nullptr);
239  /* For boolean to work right, there need to be two copies of the smaller triangle in the
240  * output. */
241  EXPECT_EQ(count_tris_with_verts(out, v3, v4, v5), 2);
242  if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) {
243  EXPECT_EQ(f0->orig, 0);
244  EXPECT_TRUE(f1->orig == 0 || f1->orig == 1);
245  EXPECT_EQ(f2->orig, 0);
246  EXPECT_EQ(f3->orig, 0);
247  EXPECT_EQ(f4->orig, 0);
248  }
249  int e03 = find_edge_pos_in_tri(v0, v3, f2);
250  int e34 = find_edge_pos_in_tri(v3, v4, f1);
251  int e45 = find_edge_pos_in_tri(v4, v5, f1);
252  int e05 = find_edge_pos_in_tri(v0, v5, f3);
253  int e15 = find_edge_pos_in_tri(v1, v5, f0);
254  EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1);
255  if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) {
256  EXPECT_EQ(f2->edge_orig[e03], 0);
257  EXPECT_TRUE(f1->edge_orig[e34] == 0 ||
258  f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN);
259  EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1);
260  EXPECT_EQ(f3->edge_orig[e05], NO_INDEX);
261  EXPECT_EQ(f0->edge_orig[e15], NO_INDEX);
262  }
263  }
264  }
265  if (DO_OBJ) {
266  write_obj_mesh(out, "tritri");
267  }
268 }
269 
270 TEST(mesh_intersect, TriTriReversed)
271 {
272  /* Like TriTri but with triangles of opposite orientation.
273  * This matters because projection to 2D will now need reversed triangles. */
274  const char *spec = R"(6 2
275  0 0 0
276  4 0 0
277  0 4 0
278  1 0 0
279  2 0 0
280  1 1 0
281  0 2 1
282  3 5 4
283  )";
284 
285  IMeshBuilder mb(spec);
286  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
287  out.populate_vert();
288  EXPECT_EQ(out.vert_size(), 6);
289  EXPECT_EQ(out.face_size(), 6);
290  if (out.vert_size() == 6 && out.face_size() == 6) {
291  const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0));
292  const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0));
293  const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0));
294  const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0));
295  const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0));
296  const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0));
297  EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr);
298  EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr);
299  if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr &&
300  v5 != nullptr) {
301  EXPECT_EQ(v0->orig, 0);
302  EXPECT_EQ(v1->orig, 1);
303  const Face *f0 = find_tri_with_verts(out, v4, v5, v1);
304  const Face *f1 = find_tri_with_verts(out, v3, v5, v4);
305  const Face *f2 = find_tri_with_verts(out, v0, v5, v3);
306  const Face *f3 = find_tri_with_verts(out, v0, v2, v5);
307  const Face *f4 = find_tri_with_verts(out, v5, v2, v1);
308  EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr &&
309  f4 != nullptr);
310  /* For boolean to work right, there need to be two copies of the smaller triangle in the
311  * output. */
312  EXPECT_EQ(count_tris_with_verts(out, v3, v5, v4), 2);
313  if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) {
314  EXPECT_EQ(f0->orig, 0);
315  EXPECT_TRUE(f1->orig == 0 || f1->orig == 1);
316  EXPECT_EQ(f2->orig, 0);
317  EXPECT_EQ(f3->orig, 0);
318  EXPECT_EQ(f4->orig, 0);
319  }
320  int e03 = find_edge_pos_in_tri(v0, v3, f2);
321  int e34 = find_edge_pos_in_tri(v3, v4, f1);
322  int e45 = find_edge_pos_in_tri(v4, v5, f1);
323  int e05 = find_edge_pos_in_tri(v0, v5, f3);
324  int e15 = find_edge_pos_in_tri(v1, v5, f0);
325  EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1);
326  if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) {
327  EXPECT_EQ(f2->edge_orig[e03], 2);
328  EXPECT_TRUE(f1->edge_orig[e34] == 2 ||
329  f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN + 2);
330  EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1);
331  EXPECT_EQ(f3->edge_orig[e05], NO_INDEX);
332  EXPECT_EQ(f0->edge_orig[e15], NO_INDEX);
333  }
334  }
335  }
336  if (DO_OBJ) {
337  write_obj_mesh(out, "tritrirev");
338  }
339 }
340 
341 TEST(mesh_intersect, TwoTris)
342 {
343  Array<mpq3> verts = {
344  mpq3(1, 1, 1), mpq3(1, 4, 1), mpq3(1, 1, 4), /* T0 */
345  mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(-4, 1, 3), /* T1 */
346  mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 5), /* T2 */
347  mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 3), /* T3 */
348  mpq3(1, 0, 0), mpq3(2, 4, 1), mpq3(-3, 2, 2), /* T4 */
349  mpq3(0, 2, 1), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T5 */
350  mpq3(1.5, 2, 0.5), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T6 */
351  mpq3(1, 0, 0), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T7 */
352  mpq3(1, 0, 0), mpq3(-3, 2, 2), mpq3(0, 1, 3), /* T8 */
353  mpq3(1, 0, 0), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T9 */
354  mpq3(3, -1, -1), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T10 */
355  mpq3(0, 0.5, 0.5), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T11 */
356  mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 3), /* T12 */
357  mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 4), /* T13 */
358  mpq3(2, 2, 5), mpq3(-3, 3, 5), mpq3(0, 3, 10), /* T14 */
359  mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-4, 2, 4), /* T15 */
360  mpq3(0, 1.5, 1), mpq3(1, 2.5, 1), mpq3(-1, 2, 2), /* T16 */
361  mpq3(3, 0, -2), mpq3(7, 4, -2), mpq3(-1, 2, 2), /* T17 */
362  mpq3(3, 0, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T18 */
363  mpq3(7, 4, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T19 */
364  mpq3(5, 2, -2), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T20 */
365  mpq3(2, 2, 0), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T21 */
366  mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-3, 0, 2), /* T22 */
367  mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-1, 2, 2), /* T23 */
368  mpq3(2, 2, 0), mpq3(4, 4, 0), mpq3(0, 3, 2), /* T24 */
369  mpq3(0, 0, 0), mpq3(-4, 2, 4), mpq3(4, 4, 0), /* T25 */
370  };
371  struct two_tri_test_spec {
372  int t0;
373  int t1;
374  int nv_out;
375  int nf_out;
376  };
377  Array<two_tri_test_spec> test_tris = Span<two_tri_test_spec>{
378  {0, 1, 8, 8}, /* 0: T1 pierces T0 inside at (1,11/6,13/6) and (1,11/5,2). */
379  {0, 2, 8, 8}, /* 1: T2 intersects T0 inside (1,11/5,2) and edge (1,7/3,8/3). */
380  {0, 3, 8, 7}, /* 2: T3 intersects T0 (1,11/5,2) and edge-edge (1,5/2,5/2). */
381  {4, 5, 6, 4}, /* 3: T5 touches T4 inside (0,2,1). */
382  {4, 6, 6, 3}, /* 4: T6 touches T4 on edge (3/2,2/1/2). */
383  {4, 7, 5, 2}, /* 5: T7 touches T4 on vert (1,0,0). */
384  {4, 8, 4, 2}, /* 6: T8 shared edge with T4 (1,0,0)(-3,2,2). */
385  {4, 9, 5, 3}, /* 7: T9 edge (1,0,0)(-1,1,1) is subset of T4 edge. */
386  {4, 10, 6, 4}, /* 8: T10 edge overlaps T4 edge with seg (-1,1,0)(1,0,0). */
387  {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */
388  {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */
389  {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */
390  {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */
391  /* Following are all coplanar cases. */
392  {15, 16, 6, 8}, /* 13: T16 inside T15. Note: dup'd tri is expected. */
393  {15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */
394  {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */
395  {15, 19, 8, 10}, /* 16: T19 intersects T15 at (3,3,0)(0,3,2). */
396  {15, 20, 12, 14}, /* 17: T20 intersects T15 on three edges, six intersects. */
397  {15, 21, 10, 11}, /* 18: T21 intersects T15 on three edges, touching one. */
398  {15, 22, 5, 4}, /* 19: T22 shares edge T15, one other outside. */
399  {15, 23, 4, 4}, /* 20: T23 shares edge T15, one other outside. */
400  {15, 24, 5, 4}, /* 21: T24 shares two edges with T15. */
401  {15, 25, 3, 2}, /* 22: T25 same T15, reverse orientation. */
402  };
403  static int perms[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
404 
405  const int do_only_test = -1; /* Make this negative to do all tests. */
406  for (int test = 0; test < test_tris.size(); ++test) {
407  if (do_only_test >= 0 && test != do_only_test) {
408  continue;
409  }
410  int tri1_index = test_tris[test].t0;
411  int tri2_index = test_tris[test].t1;
412  int co1_i = 3 * tri1_index;
413  int co2_i = 3 * tri2_index;
414 
415  const bool verbose = false;
416 
417  if (verbose) {
418  std::cout << "\nTest " << test << ": T" << tri1_index << " intersect T" << tri2_index
419  << "\n";
420  }
421 
422  const bool do_all_perms = true;
423  const int perm_limit = do_all_perms ? 3 : 1;
424 
425  for (int i = 0; i < perm_limit; ++i) {
426  for (int j = 0; j < perm_limit; ++j) {
427  if (do_all_perms && verbose) {
428  std::cout << "\nperms " << i << " " << j << "\n";
429  }
430  IMeshArena arena;
431  arena.reserve(2 * 3, 2);
432  Array<const Vert *> f0_verts(3);
433  Array<const Vert *> f1_verts(3);
434  for (int k = 0; k < 3; ++k) {
435  f0_verts[k] = arena.add_or_find_vert(verts[co1_i + perms[i][k]], k);
436  }
437  for (int k = 0; k < 3; ++k) {
438  f1_verts[k] = arena.add_or_find_vert(verts[co2_i + perms[i][k]], k + 3);
439  }
440  Face *f0 = arena.add_face(f0_verts, 0, {0, 1, 2});
441  Face *f1 = arena.add_face(f1_verts, 1, {3, 4, 5});
442  IMesh in_mesh({f0, f1});
443  IMesh out_mesh = trimesh_self_intersect(in_mesh, &arena);
444  out_mesh.populate_vert();
445  EXPECT_EQ(out_mesh.vert_size(), test_tris[test].nv_out);
446  EXPECT_EQ(out_mesh.face_size(), test_tris[test].nf_out);
447  bool constexpr dump_input = true;
448  if (DO_OBJ && i == 0 && j == 0) {
449  if (dump_input) {
450  std::string name = "test_tt_in" + std::to_string(test);
451  write_obj_mesh(in_mesh, name);
452  }
453  std::string name = "test_tt" + std::to_string(test);
454  write_obj_mesh(out_mesh, name);
455  }
456  }
457  }
458  }
459 }
460 
461 TEST(mesh_intersect, OverlapCluster)
462 {
463  /* Chain of 5 overlapping coplanar tris.
464  * Ordered so that clustering will make two separate clusters
465  * that it will have to merge into one cluster with everything. */
466  const char *spec = R"(15 5
467  0 0 0
468  1 0 0
469  1/2 1 0
470  1/2 0 0
471  3/2 0 0
472  1 1 0
473  1 0 0
474  2 0 0
475  3/2 1 0
476  3/2 0 0
477  5/2 0 0
478  2 1 0
479  2 0 0
480  3 0 0
481  5/2 1 0
482  0 1 2
483  3 4 5
484  9 10 11
485  12 13 14
486  6 7 8
487  )";
488 
489  IMeshBuilder mb(spec);
490  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
491  out.populate_vert();
492  EXPECT_EQ(out.vert_size(), 16);
493  EXPECT_EQ(out.face_size(), 18);
494  if (DO_OBJ) {
495  write_obj_mesh(out, "overlapcluster");
496  }
497 }
498 
499 TEST(mesh_intersect, TriCornerCross1)
500 {
501  /* A corner formed by 3 tris, and a 4th crossing two of them. */
502  const char *spec = R"(12 4
503  0 0 0
504  1 0 0
505  0 0 1
506  0 0 0
507  0 1 0
508  0 0 1
509  0 0 0
510  1 0 0
511  0 1 0
512  1 1 1/2
513  1 -2 1/2
514  -2 1 1/2
515  0 1 2
516  3 4 5
517  6 7 8
518  9 10 11
519  )";
520 
521  IMeshBuilder mb(spec);
522  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
523  out.populate_vert();
524  EXPECT_EQ(out.vert_size(), 10);
525  EXPECT_EQ(out.face_size(), 14);
526  if (DO_OBJ) {
527  write_obj_mesh(out, "test_tc_1");
528  }
529 }
530 
531 TEST(mesh_intersect, TriCornerCross2)
532 {
533  /* A corner formed by 3 tris, and a 4th coplanar with base. */
534  const char *spec = R"(12 4
535  0 0 0
536  1 0 0
537  0 0 1
538  0 0 0
539  0 1 0
540  0 0 1
541  0 0 0
542  1 0 0
543  0 1 0
544  1 1 0
545  1 -2 0
546  -2 1 0
547  0 1 2
548  3 4 5
549  6 7 8
550  9 10 11
551  )";
552 
553  IMeshBuilder mb(spec);
554  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
555  out.populate_vert();
556  EXPECT_EQ(out.vert_size(), 7);
557  EXPECT_EQ(out.face_size(), 8);
558  if (DO_OBJ) {
559  write_obj_mesh(out, "test_tc_2");
560  }
561 }
562 
563 TEST(mesh_intersect, TriCornerCross3)
564 {
565  /* A corner formed by 3 tris, and a 4th crossing all 3. */
566  const char *spec = R"(12 4
567  0 0 0
568  1 0 0
569  0 0 1
570  0 0 0
571  0 1 0
572  0 0 1
573  0 0 0
574  1 0 0
575  0 1 0
576  3/2 -1/2 -1/4
577  -1/2 3/2 -1/4
578  -1/2 -1/2 3/4
579  0 1 2
580  3 4 5
581  6 7 8
582  9 10 11
583  )";
584 
585  IMeshBuilder mb(spec);
586  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
587  out.populate_vert();
588  EXPECT_EQ(out.vert_size(), 10);
589  EXPECT_EQ(out.face_size(), 16);
590  if (DO_OBJ) {
591  write_obj_mesh(out, "test_tc_3");
592  }
593 }
594 
595 TEST(mesh_intersect, TetTet)
596 {
597  const char *spec = R"(8 8
598  0 0 0
599  2 0 0
600  1 2 0
601  1 1 2
602  0 0 1
603  2 0 1
604  1 2 1
605  1 1 3
606  0 1 2
607  0 3 1
608  1 3 2
609  2 3 0
610  4 5 6
611  4 7 5
612  5 7 6
613  6 7 4
614  )";
615 
616  IMeshBuilder mb(spec);
617  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
618  out.populate_vert();
619  EXPECT_EQ(out.vert_size(), 11);
620  EXPECT_EQ(out.face_size(), 20);
621  /* Expect there to be a triangle with these three verts, oriented this way, with original face 1.
622  */
623  const Vert *v1 = mb.arena.find_vert(mpq3(2, 0, 0));
624  const Vert *v8 = mb.arena.find_vert(mpq3(0.5, 0.5, 1));
625  const Vert *v9 = mb.arena.find_vert(mpq3(1.5, 0.5, 1));
626  EXPECT_TRUE(v1 && v8 && v9);
627  if (v1 && v8 && v9) {
628  const Face *f = mb.arena.find_face({v1, v8, v9});
629  EXPECT_NE(f, nullptr);
630  if (f != nullptr) {
631  EXPECT_EQ(f->orig, 1);
632  int v1pos = f->vert[0] == v1 ? 0 : (f->vert[1] == v1 ? 1 : 2);
633  EXPECT_EQ(f->edge_orig[v1pos], NO_INDEX);
634  EXPECT_EQ(f->edge_orig[(v1pos + 1) % 3], NO_INDEX);
635  EXPECT_EQ(f->edge_orig[(v1pos + 2) % 3], 1001);
636  EXPECT_EQ(f->is_intersect[v1pos], false);
637  EXPECT_EQ(f->is_intersect[(v1pos + 1) % 3], true);
638  EXPECT_EQ(f->is_intersect[(v1pos + 2) % 3], false);
639  }
640  }
641  if (DO_OBJ) {
642  write_obj_mesh(out, "test_tc_3");
643  }
644 }
645 
646 TEST(mesh_intersect, CubeCubeStep)
647 {
648  const char *spec = R"(16 24
649  0 -1 0
650  0 -1 2
651  0 1 0
652  0 1 2
653  2 -1 0
654  2 -1 2
655  2 1 0
656  2 1 2
657  -1 -1 -1
658  -1 -1 1
659  -1 1 -1
660  -1 1 1
661  1 -1 -1
662  1 -1 1
663  1 1 -1
664  1 1 1
665  0 1 3
666  0 3 2
667  2 3 7
668  2 7 6
669  6 7 5
670  6 5 4
671  4 5 1
672  4 1 0
673  2 6 4
674  2 4 0
675  7 3 1
676  7 1 5
677  8 9 11
678  8 11 10
679  10 11 15
680  10 15 14
681  14 15 13
682  14 13 12
683  12 13 9
684  12 9 8
685  10 14 12
686  10 12 8
687  15 11 9
688  15 9 13
689  )";
690 
691  IMeshBuilder mb(spec);
692  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
693  out.populate_vert();
694  EXPECT_EQ(out.vert_size(), 22);
695  EXPECT_EQ(out.face_size(), 56);
696  if (DO_OBJ) {
697  write_obj_mesh(out, "test_cubecubestep");
698  }
699 
700  IMeshBuilder mb2(spec);
701  IMesh out2 = trimesh_nary_intersect(
702  mb2.imesh, 2, [](int t) { return t < 12 ? 0 : 1; }, false, &mb2.arena);
703  out2.populate_vert();
704  EXPECT_EQ(out2.vert_size(), 22);
705  EXPECT_EQ(out2.face_size(), 56);
706  if (DO_OBJ) {
707  write_obj_mesh(out2, "test_cubecubestep_nary");
708  }
709 }
710 
711 TEST(mesh_intersect, RectCross)
712 {
713  const char *spec = R"(8 4
714  3/2 0 1
715  -3/2 0 1
716  -3/2 0 -1
717  3/2 0 -1
718  1 0 -5
719  -1 0 -5
720  1 0 5
721  -1 0 5
722  1 0 3
723  1 3 2
724  5 4 6
725  5 6 7
726  )";
727 
728  IMeshBuilder mb(spec);
729  IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
730  out.populate_vert();
731  EXPECT_EQ(out.vert_size(), 17);
732  EXPECT_EQ(out.face_size(), 28);
733  if (DO_OBJ) {
734  write_obj_mesh(out, "test_rectcross");
735  }
736 }
737 # endif
738 
739 # if DO_PERF_TESTS
740 
741 static void get_sphere_params(
742  int nrings, int nsegs, bool triangulate, int *r_num_verts, int *r_num_faces)
743 {
744  *r_num_verts = nsegs * (nrings - 1) + 2;
745  if (triangulate) {
746  *r_num_faces = 2 * nsegs + 2 * nsegs * (nrings - 2);
747  }
748  else {
749  *r_num_faces = nsegs * nrings;
750  }
751 }
752 
753 static void fill_sphere_data(int nrings,
754  int nsegs,
755  const double3 &center,
756  double radius,
757  bool triangulate,
758  MutableSpan<Face *> face,
759  int vid_start,
760  int fid_start,
761  IMeshArena *arena)
762 {
763  int num_verts;
764  int num_faces;
765  get_sphere_params(nrings, nsegs, triangulate, &num_verts, &num_faces);
766  BLI_assert(num_faces == face.size());
767  Array<const Vert *> vert(num_verts);
768  const bool nrings_even = (nrings % 2 == 0);
769  int half_nrings = nrings / 2;
770  const bool nsegs_even = (nsegs % 2) == 0;
771  const bool nsegs_four_divisible = (nsegs % 4 == 0);
772  int half_nsegs = nrings;
773  int quarter_nsegs = half_nsegs / 2;
774  double delta_phi = 2 * M_PI / nsegs;
775  double delta_theta = M_PI / nrings;
776  int fid = fid_start;
777  int vid = vid_start;
778  auto vert_index_fn = [nrings, num_verts](int seg, int ring) {
779  if (ring == 0) { /* Top vert. */
780  return num_verts - 2;
781  }
782  if (ring == nrings) { /* Bottom vert. */
783  return num_verts - 1;
784  }
785  return seg * (nrings - 1) + (ring - 1);
786  };
787  auto face_index_fn = [nrings](int seg, int ring) { return seg * nrings + ring; };
788  auto tri_index_fn = [nrings, nsegs](int seg, int ring, int tri) {
789  if (ring == 0) {
790  return seg;
791  }
792  if (ring < nrings - 1) {
793  return nsegs + 2 * (ring - 1) * nsegs + 2 * seg + tri;
794  }
795  return nsegs + 2 * (nrings - 2) * nsegs + seg;
796  };
797  Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
798  /*
799  * (x, y , z) is given from inclination theta and azimuth phi,
800  * where 0 <= theta <= pi; 0 <= phi <= 2pi.
801  * x = radius * sin(theta) cos(phi)
802  * y = radius * sin(theta) sin(phi)
803  * z = radius * cos(theta)
804  */
805  for (int s = 0; s < nsegs; ++s) {
806  double phi = s * delta_phi;
807  double sin_phi;
808  double cos_phi;
809  /* Avoid use of trig functions for pi/2 divisible angles. */
810  if (s == 0) {
811  /* phi = 0. */
812  sin_phi = 0.0;
813  cos_phi = 1.0;
814  }
815  else if (nsegs_even && s == half_nsegs) {
816  /* phi = pi. */
817  sin_phi = 0.0;
818  cos_phi = -1.0;
819  }
820  else if (nsegs_four_divisible && s == quarter_nsegs) {
821  /* phi = pi/2. */
822  sin_phi = 1.0;
823  cos_phi = 0.0;
824  }
825  else if (nsegs_four_divisible && s == 3 * quarter_nsegs) {
826  /* phi = 3pi/2. */
827  sin_phi = -1.0;
828  cos_phi = 0.0;
829  }
830  else {
831  sin_phi = sin(phi);
832  cos_phi = cos(phi);
833  }
834  for (int r = 1; r < nrings; ++r) {
835  double theta = r * delta_theta;
836  double r_sin_theta;
837  double r_cos_theta;
838  if (nrings_even && r == half_nrings) {
839  /* theta = pi/2. */
840  r_sin_theta = radius;
841  r_cos_theta = 0.0;
842  }
843  else {
844  r_sin_theta = radius * sin(theta);
845  r_cos_theta = radius * cos(theta);
846  }
847  double x = r_sin_theta * cos_phi + center[0];
848  double y = r_sin_theta * sin_phi + center[1];
849  double z = r_cos_theta + center[2];
850  const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++);
851  vert[vert_index_fn(s, r)] = v;
852  }
853  }
854  const Vert *vtop = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] + radius),
855  vid++);
856  const Vert *vbot = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] - radius),
857  vid++);
858  vert[vert_index_fn(0, 0)] = vtop;
859  vert[vert_index_fn(0, nrings)] = vbot;
860  for (int s = 0; s < nsegs; ++s) {
861  int snext = (s + 1) % nsegs;
862  for (int r = 0; r < nrings; ++r) {
863  int rnext = r + 1;
864  int i0 = vert_index_fn(s, r);
865  int i1 = vert_index_fn(s, rnext);
866  int i2 = vert_index_fn(snext, rnext);
867  int i3 = vert_index_fn(snext, r);
868  Face *f;
869  Face *f2 = nullptr;
870  if (r == 0) {
871  f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
872  }
873  else if (r == nrings - 1) {
874  f = arena->add_face({vert[i0], vert[i1], vert[i3]}, fid++, eid);
875  }
876  else {
877  if (triangulate) {
878  f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
879  f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid);
880  }
881  else {
882  f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid);
883  }
884  }
885  if (triangulate) {
886  int f_index = tri_index_fn(s, r, 0);
887  face[f_index] = f;
888  if (r != 0 && r != nrings - 1) {
889  int f_index2 = tri_index_fn(s, r, 1);
890  face[f_index2] = f2;
891  }
892  }
893  else {
894  int f_index = face_index_fn(s, r);
895  face[f_index] = f;
896  }
897  }
898  }
899 }
900 
901 static void spheresphere_test(int nrings, double y_offset, bool use_self)
902 {
903  /* Make two uvspheres with nrings rings ad 2*nrings segments. */
904  if (nrings < 2) {
905  return;
906  }
907  BLI_task_scheduler_init(); /* Without this, no parallelism. */
908  double time_start = PIL_check_seconds_timer();
909  IMeshArena arena;
910  int nsegs = 2 * nrings;
911  int num_sphere_verts;
912  int num_sphere_tris;
913  get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris);
914  Array<Face *> tris(2 * num_sphere_tris);
915  arena.reserve(6 * num_sphere_verts / 2, 8 * num_sphere_tris);
916  double3 center1(0.0, 0.0, 0.0);
917  fill_sphere_data(nrings,
918  nsegs,
919  center1,
920  1.0,
921  true,
922  MutableSpan<Face *>(tris.begin(), num_sphere_tris),
923  0,
924  0,
925  &arena);
926  double3 center2(0.0, y_offset, 0.0);
927  fill_sphere_data(nrings,
928  nsegs,
929  center2,
930  1.0,
931  true,
932  MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_sphere_tris),
933  num_sphere_verts,
934  num_sphere_verts,
935  &arena);
936  IMesh mesh(tris);
937  double time_create = PIL_check_seconds_timer();
938  // write_obj_mesh(mesh, "spheresphere_in");
939  IMesh out;
940  if (use_self) {
941  out = trimesh_self_intersect(mesh, &arena);
942  }
943  else {
944  int nf = num_sphere_tris;
945  out = trimesh_nary_intersect(
946  mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
947  }
948  double time_intersect = PIL_check_seconds_timer();
949  std::cout << "Create time: " << time_create - time_start << "\n";
950  std::cout << "Intersect time: " << time_intersect - time_create << "\n";
951  std::cout << "Total time: " << time_intersect - time_start << "\n";
952  if (DO_OBJ) {
953  write_obj_mesh(out, "spheresphere");
954  }
956 }
957 
958 static void get_grid_params(
959  int x_subdiv, int y_subdiv, bool triangulate, int *r_num_verts, int *r_num_faces)
960 {
961  *r_num_verts = x_subdiv * y_subdiv;
962  if (triangulate) {
963  *r_num_faces = 2 * (x_subdiv - 1) * (y_subdiv - 1);
964  }
965  else {
966  *r_num_faces = (x_subdiv - 1) * (y_subdiv - 1);
967  }
968 }
969 
970 static void fill_grid_data(int x_subdiv,
971  int y_subdiv,
972  bool triangulate,
973  double size,
974  const double3 &center,
975  double rot_deg,
976  MutableSpan<Face *> face,
977  int vid_start,
978  int fid_start,
979  IMeshArena *arena)
980 {
981  if (x_subdiv <= 1 || y_subdiv <= 1) {
982  return;
983  }
984  int num_verts;
985  int num_faces;
986  get_grid_params(x_subdiv, y_subdiv, triangulate, &num_verts, &num_faces);
987  BLI_assert(face.size() == num_faces);
988  Array<const Vert *> vert(num_verts);
989  auto vert_index_fn = [x_subdiv](int ix, int iy) { return iy * x_subdiv + ix; };
990  auto face_index_fn = [x_subdiv](int ix, int iy) { return iy * (x_subdiv - 1) + ix; };
991  auto tri_index_fn = [x_subdiv](int ix, int iy, int tri) {
992  return 2 * iy * (x_subdiv - 1) + 2 * ix + tri;
993  };
994  Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
995  double r = size / 2.0;
996  double delta_x = size / (x_subdiv - 1);
997  double delta_y = size / (y_subdiv - 1);
998  int vid = vid_start;
999  double cos_rot = cosf(rot_deg * M_PI / 180.0);
1000  double sin_rot = sinf(rot_deg * M_PI / 180.0);
1001  for (int iy = 0; iy < y_subdiv; ++iy) {
1002  double yy = iy * delta_y - r;
1003  for (int ix = 0; ix < x_subdiv; ++ix) {
1004  double xx = ix * delta_x - r;
1005  double x = center[0] + xx;
1006  double y = center[1] + yy;
1007  double z = center[2];
1008  if (rot_deg != 0.0) {
1009  x = center[0] + xx * cos_rot - yy * sin_rot;
1010  y = center[1] + xx * sin_rot + yy * cos_rot;
1011  }
1012  const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++);
1013  vert[vert_index_fn(ix, iy)] = v;
1014  }
1015  }
1016  int fid = fid_start;
1017  for (int iy = 0; iy < y_subdiv - 1; ++iy) {
1018  for (int ix = 0; ix < x_subdiv - 1; ++ix) {
1019  int i0 = vert_index_fn(ix, iy);
1020  int i1 = vert_index_fn(ix, iy + 1);
1021  int i2 = vert_index_fn(ix + 1, iy + 1);
1022  int i3 = vert_index_fn(ix + 1, iy);
1023  if (triangulate) {
1024  Face *f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
1025  Face *f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid);
1026  face[tri_index_fn(ix, iy, 0)] = f;
1027  face[tri_index_fn(ix, iy, 1)] = f2;
1028  }
1029  else {
1030  Face *f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid);
1031  face[face_index_fn(ix, iy)] = f;
1032  }
1033  }
1034  }
1035 }
1036 
1037 static void spheregrid_test(int nrings, int grid_level, double z_offset, bool use_self)
1038 {
1039  /* Make a uv-sphere and a grid.
1040  * The sphere is radius 1, has `nrings` rings and `2 * nrings` segments,
1041  * and is centered at (0,0,z_offset).
1042  * The plane is 4x4, has `2 ** grid_level` subdivisions x and y,
1043  * and is centered at the origin. */
1044  if (nrings < 2 || grid_level < 1) {
1045  return;
1046  }
1047  BLI_task_scheduler_init(); /* Without this, no parallelism. */
1048  double time_start = PIL_check_seconds_timer();
1049  IMeshArena arena;
1050  int num_sphere_verts;
1051  int num_sphere_tris;
1052  int nsegs = 2 * nrings;
1053  int num_grid_verts;
1054  int num_grid_tris;
1055  int subdivs = 1 << grid_level;
1056  get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris);
1057  get_grid_params(subdivs, subdivs, true, &num_grid_verts, &num_grid_tris);
1058  Array<Face *> tris(num_sphere_tris + num_grid_tris);
1059  arena.reserve(3 * (num_sphere_verts + num_grid_verts) / 2,
1060  4 * (num_sphere_tris + num_grid_tris));
1061  double3 center(0.0, 0.0, z_offset);
1062  fill_sphere_data(nrings,
1063  nsegs,
1064  center,
1065  1.0,
1066  true,
1067  MutableSpan<Face *>(tris.begin(), num_sphere_tris),
1068  0,
1069  0,
1070  &arena);
1071  fill_grid_data(subdivs,
1072  subdivs,
1073  true,
1074  4.0,
1075  double3(0, 0, 0),
1076  0.0,
1077  MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_grid_tris),
1078  num_sphere_verts,
1079  num_sphere_tris,
1080  &arena);
1081  IMesh mesh(tris);
1082  double time_create = PIL_check_seconds_timer();
1083  // write_obj_mesh(mesh, "spheregrid_in");
1084  IMesh out;
1085  if (use_self) {
1086  out = trimesh_self_intersect(mesh, &arena);
1087  }
1088  else {
1089  int nf = num_sphere_tris;
1090  out = trimesh_nary_intersect(
1091  mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
1092  }
1093  double time_intersect = PIL_check_seconds_timer();
1094  std::cout << "Create time: " << time_create - time_start << "\n";
1095  std::cout << "Intersect time: " << time_intersect - time_create << "\n";
1096  std::cout << "Total time: " << time_intersect - time_start << "\n";
1097  if (DO_OBJ) {
1098  write_obj_mesh(out, "spheregrid");
1099  }
1101 }
1102 
1103 static void gridgrid_test(int x_level_1,
1104  int y_level_1,
1105  int x_level_2,
1106  int y_level_2,
1107  double x_off,
1108  double y_off,
1109  double rot_deg,
1110  bool use_self)
1111 {
1112  /* Make two grids, each 4x4, with given subdivision levels in x and y,
1113  * and the second offset from the first by x_off, y_off, and rotated by rot_deg degrees. */
1114  BLI_task_scheduler_init(); /* Without this, no parallelism. */
1115  double time_start = PIL_check_seconds_timer();
1116  IMeshArena arena;
1117  int x_subdivs_1 = 1 << x_level_1;
1118  int y_subdivs_1 = 1 << y_level_1;
1119  int x_subdivs_2 = 1 << x_level_2;
1120  int y_subdivs_2 = 1 << y_level_2;
1121  int num_grid_verts_1;
1122  int num_grid_verts_2;
1123  int num_grid_tris_1;
1124  int num_grid_tris_2;
1125  get_grid_params(x_subdivs_1, y_subdivs_1, true, &num_grid_verts_1, &num_grid_tris_1);
1126  get_grid_params(x_subdivs_2, y_subdivs_2, true, &num_grid_verts_2, &num_grid_tris_2);
1127  Array<Face *> tris(num_grid_tris_1 + num_grid_tris_2);
1128  arena.reserve(3 * (num_grid_verts_1 + num_grid_verts_2) / 2,
1129  4 * (num_grid_tris_1 + num_grid_tris_2));
1130  fill_grid_data(x_subdivs_1,
1131  y_subdivs_1,
1132  true,
1133  4.0,
1134  double3(0, 0, 0),
1135  0.0,
1136  MutableSpan<Face *>(tris.begin(), num_grid_tris_1),
1137  0,
1138  0,
1139  &arena);
1140  fill_grid_data(x_subdivs_2,
1141  y_subdivs_2,
1142  true,
1143  4.0,
1144  double3(x_off, y_off, 0),
1145  rot_deg,
1146  MutableSpan<Face *>(tris.begin() + num_grid_tris_1, num_grid_tris_2),
1147  num_grid_verts_1,
1148  num_grid_tris_1,
1149  &arena);
1150  IMesh mesh(tris);
1151  double time_create = PIL_check_seconds_timer();
1152  // write_obj_mesh(mesh, "gridgrid_in");
1153  IMesh out;
1154  if (use_self) {
1155  out = trimesh_self_intersect(mesh, &arena);
1156  }
1157  else {
1158  int nf = num_grid_tris_1;
1159  out = trimesh_nary_intersect(
1160  mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
1161  }
1162  double time_intersect = PIL_check_seconds_timer();
1163  std::cout << "Create time: " << time_create - time_start << "\n";
1164  std::cout << "Intersect time: " << time_intersect - time_create << "\n";
1165  std::cout << "Total time: " << time_intersect - time_start << "\n";
1166  if (DO_OBJ) {
1167  write_obj_mesh(out, "gridgrid");
1168  }
1170 }
1171 
1172 TEST(mesh_intersect_perf, SphereSphere)
1173 {
1174  spheresphere_test(512, 0.5, false);
1175 }
1176 
1177 TEST(mesh_intersect_perf, SphereSphereSelf)
1178 {
1179  spheresphere_test(64, 0.5, true);
1180 }
1181 
1182 TEST(mesh_intersect_perf, SphereGrid)
1183 {
1184  spheregrid_test(512, 4, 0.1, false);
1185 }
1186 
1187 TEST(mesh_intersect_perf, SphereGridSelf)
1188 {
1189  spheregrid_test(64, 4, 0.1, true);
1190 }
1191 
1192 TEST(mesh_intersect_perf, GridGrid)
1193 {
1194  gridgrid_test(8, 2, 4, 2, 0.1, 0.1, 0.0, false);
1195 }
1196 
1197 TEST(mesh_intersect_perf, GridGridTilt)
1198 {
1199  gridgrid_test(8, 2, 4, 2, 0.0, 0.0, 1.0, false);
1200 }
1201 
1202 # endif
1203 
1204 } // namespace blender::meshintersect::tests
1205 #endif
#define BLI_assert(a)
Definition: BLI_assert.h:58
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define M_PI
Definition: BLI_math_base.h:38
void BLI_task_scheduler_init(void)
void BLI_task_scheduler_exit(void)
NSNotificationCenter * center
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble z
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint i1
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Platform independent time functions.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
ccl_device_inline float delta_phi(int p, float gamma_o, float gamma_t)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
static int verbose
Definition: cineonlib.c:44
static float verts[][3]
uint pos
#define sinf(x)
#define cosf(x)
static char faces[256]
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
std::string to_string(const T &n)
double PIL_check_seconds_timer(void)
Definition: time.c:80