Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorHoward Trickey <howard.trickey@gmail.com>2020-06-07 01:46:06 +0300
committerHoward Trickey <howard.trickey@gmail.com>2020-06-07 01:46:06 +0300
commite4c25b0ab7588c01166b208084a11996890841bf (patch)
tree415af2cb28aa01d2a6b168990e0ccf8adc7a3bd4 /source
parent61ae661103597f5877461eb1695ee86cfd0e4fce (diff)
Move to exact arithmetic for new boolean implementation.
This is a regression in functionality from the previous version, as I have not yet got anything beyond intersection working (and that only for triangulated meshes). But want to get this into the repository now while I continue to work on the functionality, and then, the performance.
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenlib/BLI_boolean.h60
-rw-r--r--source/blender/blenlib/BLI_delaunay_2d.h60
-rw-r--r--source/blender/blenlib/BLI_double2.hh140
-rw-r--r--source/blender/blenlib/BLI_double3.hh238
-rw-r--r--source/blender/blenlib/BLI_float2.hh46
-rw-r--r--source/blender/blenlib/BLI_float3.hh5
-rw-r--r--source/blender/blenlib/BLI_math_base.h1
-rw-r--r--source/blender/blenlib/BLI_math_geom.h38
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h4
-rw-r--r--source/blender/blenlib/BLI_math_mpq.hh26
-rw-r--r--source/blender/blenlib/BLI_math_vector.h35
-rw-r--r--source/blender/blenlib/BLI_mesh_intersect.hh122
-rw-r--r--source/blender/blenlib/BLI_mpq2.hh143
-rw-r--r--source/blender/blenlib/BLI_mpq3.hh257
-rw-r--r--source/blender/blenlib/CMakeLists.txt25
-rw-r--r--source/blender/blenlib/intern/boolean.cc93
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.c5171
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.cc2262
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c5
-rw-r--r--source/blender/blenlib/intern/math_geom.c156
-rw-r--r--source/blender/blenlib/intern/math_geom_inline.c5
-rw-r--r--source/blender/blenlib/intern/math_matrix.c64
-rw-r--r--source/blender/blenlib/intern/math_vec.cc2592
-rw-r--r--source/blender/blenlib/intern/math_vector.c46
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c110
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc1655
-rw-r--r--source/blender/bmesh/bmesh_tools.h1
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.c4669
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.h28
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_collapse.c2
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c102
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c57
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c1
-rw-r--r--source/creator/CMakeLists.txt21
34 files changed, 7956 insertions, 10284 deletions
diff --git a/source/blender/blenlib/BLI_boolean.h b/source/blender/blenlib/BLI_boolean.h
new file mode 100644
index 00000000000..f23e59c8c8d
--- /dev/null
+++ b/source/blender/blenlib/BLI_boolean.h
@@ -0,0 +1,60 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_BOOLEAN_H__
+#define __BLI_BOOLEAN_H__
+
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum bool_optype {
+ BOOLEAN_NONE = -1,
+ /* Aligned with BooleanModifierOp. */
+ BOOLEAN_ISECT = 0,
+ BOOLEAN_UNION = 1,
+ BOOLEAN_DIFFERENCE = 2,
+} bool_optype;
+
+typedef struct Boolean_trimesh_input {
+ int vert_len;
+ int tri_len;
+ float (*vert_coord)[3];
+ int (*tri)[3];
+} Boolean_trimesh_input;
+
+typedef struct Boolean_trimesh_output {
+ int vert_len;
+ int tri_len;
+ float (*vert_coord)[3];
+ int (*tri)[3];
+} Boolean_trimesh_output;
+
+Boolean_trimesh_output *BLI_boolean_trimesh(const Boolean_trimesh_input *input, int bool_optype);
+
+void BLI_boolean_trimesh_free(Boolean_trimesh_output *output);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __BLI_BOOLEAN_H__ */
diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h
index 95111dbbbf7..fc5c7123890 100644
--- a/source/blender/blenlib/BLI_delaunay_2d.h
+++ b/source/blender/blenlib/BLI_delaunay_2d.h
@@ -19,6 +19,9 @@
/** \file
* \ingroup bli
+ *
+ * This header file contains both a C interface and a C++ interface
+ * to the 2D Constrained Delaunay Triangulation library routine.
*/
#ifdef __cplusplus
@@ -108,11 +111,6 @@ extern "C" {
* If zero is supplied for epsilon, an internal value of 1e-8 used
* instead, since this code will not work correctly if it is not allowed
* to merge "too near" vertices.
- *
- * Normally, if epsilon is non-zero, there is an "input modify" pass which
- * checks to see if some vertices are within epsilon of other edges, and
- * snapping them to those edges if so. You can skip this pass by setting
- * skip_input_modify to true. (This is also useful in some unit tests.)
*/
typedef struct CDT_input {
int verts_len;
@@ -124,7 +122,6 @@ typedef struct CDT_input {
int *faces_start_table;
int *faces_len_table;
float epsilon;
- bool skip_input_modify;
} CDT_input;
/**
@@ -208,6 +205,55 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result);
#ifdef __cplusplus
}
-#endif
+
+/* C++ Interface. */
+
+# include "gmpxx.h"
+
+# include "BLI_array.hh"
+# include "BLI_double2.hh"
+# include "BLI_linklist.h"
+# include "BLI_mempool.h"
+# include "BLI_mpq2.hh"
+# include "BLI_vector.hh"
+
+namespace BLI {
+
+template<typename Arith_t> struct vec2_impl;
+template<> struct vec2_impl<double> {
+ typedef double2 type;
+};
+template<> struct vec2_impl<mpq_class> {
+ typedef mpq2 type;
+};
+template<typename Arith_t> using vec2 = typename vec2_impl<Arith_t>::type;
+
+template<typename Arith_t> class CDT_input {
+ public:
+ Array<vec2<Arith_t>> vert;
+ Array<std::pair<int, int>> edge;
+ Array<Vector<int>> face;
+ Arith_t epsilon{0};
+};
+
+template<typename Arith_t> class CDT_result {
+ public:
+ Array<vec2<Arith_t>> vert;
+ Array<std::pair<int, int>> edge;
+ Array<Vector<int>> face;
+ Array<Vector<int>> vert_orig;
+ Array<Vector<int>> edge_orig;
+ Array<Vector<int>> face_orig;
+ int face_edge_offset;
+};
+
+CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, CDT_output_type output_type);
+
+CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input,
+ CDT_output_type output_type);
+
+} /* namespace BLI */
+
+#endif /* __cplusplus */
#endif /* __BLI_DELAUNAY_2D_H__ */
diff --git a/source/blender/blenlib/BLI_double2.hh b/source/blender/blenlib/BLI_double2.hh
new file mode 100644
index 00000000000..994d6213502
--- /dev/null
+++ b/source/blender/blenlib/BLI_double2.hh
@@ -0,0 +1,140 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_DOUBLE2_HH__
+#define __BLI_DOUBLE2_HH__
+
+#include "BLI_double3.hh"
+
+namespace BLI {
+
+struct double2 {
+ double x, y;
+
+ double2() = default;
+
+ double2(const double *ptr) : x{ptr[0]}, y{ptr[1]}
+ {
+ }
+
+ double2(double x, double y) : x(x), y(y)
+ {
+ }
+
+ double2(const double3 &other) : x(other.x), y(other.y)
+ {
+ }
+
+ operator double *()
+ {
+ return &x;
+ }
+
+ operator const double *() const
+ {
+ return &x;
+ }
+
+ float length() const
+ {
+ return len_v2_db(*this);
+ }
+
+ friend double2 operator+(const double2 &a, const double2 &b)
+ {
+ return {a.x + b.x, a.y + b.y};
+ }
+
+ friend double2 operator-(const double2 &a, const double2 &b)
+ {
+ return {a.x - b.x, a.y - b.y};
+ }
+
+ friend double2 operator*(const double2 &a, double b)
+ {
+ return {a.x * b, a.y * b};
+ }
+
+ friend double2 operator/(const double2 &a, double b)
+ {
+ BLI_assert(b != 0.0);
+ return {a.x / b, a.y / b};
+ }
+
+ friend double2 operator*(double a, const double2 &b)
+ {
+ return b * a;
+ }
+
+ friend bool operator==(const double2 &a, const double2 &b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const double2 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ")";
+ return stream;
+ }
+
+ static double dot(const double2 &a, const double2 &b)
+ {
+ return a.x * b.x + a.y * b.y;
+ }
+
+ static double2 interpolate(const double2 &a, const double2 &b, double t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static double distance(const double2 &a, const double2 &b)
+ {
+ return (a - b).length();
+ }
+
+ static double distance_squared(const double2 &a, const double2 &b)
+ {
+ return double2::dot(a, b);
+ }
+
+ struct isect_result {
+ enum {
+ LINE_LINE_COLINEAR = -1,
+ LINE_LINE_NONE = 0,
+ LINE_LINE_EXACT = 1,
+ LINE_LINE_CROSS = 2,
+ } kind;
+ double lambda;
+ double mu;
+ };
+
+ static isect_result isect_seg_seg(const double2 &v1,
+ const double2 &v2,
+ const double2 &v3,
+ const double2 &v4);
+
+ static int orient2d(const double2 &a, const double2 &b, const double2 &c);
+
+ static int orient2d_fast(const double2 &a, const double2 &b, const double2 &c);
+
+ static int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d);
+
+ static int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d);
+};
+
+} // namespace BLI
+
+#endif /* __BLI_DOUBLE_HH__ */
diff --git a/source/blender/blenlib/BLI_double3.hh b/source/blender/blenlib/BLI_double3.hh
new file mode 100644
index 00000000000..d9926751e39
--- /dev/null
+++ b/source/blender/blenlib/BLI_double3.hh
@@ -0,0 +1,238 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_DOUBLE3_HH__
+#define __BLI_DOUBLE3_HH__
+
+#include <iostream>
+
+#include "BLI_math_vector.h"
+
+namespace BLI {
+
+struct double3 {
+ double x, y, z;
+
+ double3() = default;
+
+ double3(const double *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
+ {
+ }
+
+ double3(const double (*ptr)[3]) : double3((const double *)ptr)
+ {
+ }
+
+ explicit double3(double value) : x(value), y(value), z(value)
+ {
+ }
+
+ explicit double3(int value) : x(value), y(value), z(value)
+ {
+ }
+
+ double3(double x, double y, double z) : x{x}, y{y}, z{z}
+ {
+ }
+
+ operator const double *() const
+ {
+ return &x;
+ }
+
+ operator double *()
+ {
+ return &x;
+ }
+
+ double normalize_and_get_length()
+ {
+ return normalize_v3_db(*this);
+ }
+
+ double3 normalized() const
+ {
+ double3 result;
+ normalize_v3_v3_db(result, *this);
+ return result;
+ }
+
+ double length() const
+ {
+ return len_v3_db(*this);
+ }
+
+ double length_squared() const
+ {
+ return len_squared_v3_db(*this);
+ }
+
+ void reflect(const double3 &normal)
+ {
+ *this = this->reflected(normal);
+ }
+
+ double3 reflected(const double3 &normal) const
+ {
+ double3 result;
+ reflect_v3_v3v3_db(result, *this, normal);
+ return result;
+ }
+
+ static double3 safe_divide(const double3 &a, const double3 &b)
+ {
+ double3 result;
+ result.x = (b.x == 0.0) ? 0.0 : a.x / b.x;
+ result.y = (b.y == 0.0) ? 0.0 : a.y / b.y;
+ result.z = (b.z == 0.0) ? 0.0 : a.z / b.z;
+ return result;
+ }
+
+ void invert()
+ {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ friend double3 operator+(const double3 &a, const double3 &b)
+ {
+ return {a.x + b.x, a.y + b.y, a.z + b.z};
+ }
+
+ void operator+=(const double3 &b)
+ {
+ this->x += b.x;
+ this->y += b.y;
+ this->z += b.z;
+ }
+
+ friend double3 operator-(const double3 &a, const double3 &b)
+ {
+ return {a.x - b.x, a.y - b.y, a.z - b.z};
+ }
+
+ friend double3 operator-(const double3 &a)
+ {
+ return {-a.x, -a.y, -a.z};
+ }
+
+ void operator-=(const double3 &b)
+ {
+ this->x -= b.x;
+ this->y -= b.y;
+ this->z -= b.z;
+ }
+
+ void operator*=(const double &scalar)
+ {
+ this->x *= scalar;
+ this->y *= scalar;
+ this->z *= scalar;
+ }
+
+ void operator*=(const double3 &other)
+ {
+ this->x *= other.x;
+ this->y *= other.y;
+ this->z *= other.z;
+ }
+
+ friend double3 operator*(const double3 &a, const double3 &b)
+ {
+ return {a.x * b.x, a.y * b.y, a.z * b.z};
+ }
+
+ friend double3 operator*(const double3 &a, const double &b)
+ {
+ return {a.x * b, a.y * b, a.z * b};
+ }
+
+ friend double3 operator*(const double &a, const double3 &b)
+ {
+ return b * a;
+ }
+
+ friend double3 operator/(const double3 &a, const double &b)
+ {
+ BLI_assert(b != 0.0);
+ return {a.x / b, a.y / b, a.z / b};
+ }
+
+ friend bool operator==(const double3 &a, const double3 &b)
+ {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const double3 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
+ return stream;
+ }
+
+ static double dot(const double3 &a, const double3 &b)
+ {
+ return a.x * b.x + a.y * b.y + a.z * b.z;
+ }
+
+ static double3 cross_high_precision(const double3 &a, const double3 &b)
+ {
+ double3 result;
+ cross_v3_v3v3_db(result, a, b);
+ return result;
+ }
+
+ static double3 project(const double3 &a, const double3 &b)
+ {
+ double3 result;
+ project_v3_v3v3_db(result, a, b);
+ return result;
+ }
+
+ static double distance(const double3 &a, const double3 &b)
+ {
+ return (a - b).length();
+ }
+
+ static double distance_squared(const double3 &a, const double3 &b)
+ {
+ return double3::dot(a, b);
+ }
+
+ static double3 interpolate(const double3 &a, const double3 &b, double t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ /* orient3d gives the exact result, using multiprecision artihmetic when result
+ * is close to zero. orient3d_fast just uses double arithmetic, so may be
+ * wrong if the answer is very close to zero.
+ * Similarly, for insphere and insphere_fast.
+ */
+ static int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d);
+
+ static int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d);
+
+ static int insphere(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e);
+
+ static int insphere_fast(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e);
+};
+
+} // namespace BLI
+
+#endif /* __BLI_DOUBLE3_HH__ */
diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh
index da12dd7d206..2bb5cf82214 100644
--- a/source/blender/blenlib/BLI_float2.hh
+++ b/source/blender/blenlib/BLI_float2.hh
@@ -48,6 +48,11 @@ struct float2 {
return &x;
}
+ float length() const
+ {
+ return len_v2(*this);
+ }
+
friend float2 operator+(const float2 &a, const float2 &b)
{
return {a.x + b.x, a.y + b.y};
@@ -74,11 +79,52 @@ struct float2 {
return b * a;
}
+ friend bool operator==(const float2 &a, const float2 &b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
friend std::ostream &operator<<(std::ostream &stream, const float2 &v)
{
stream << "(" << v.x << ", " << v.y << ")";
return stream;
}
+
+ static float dot(const float2 &a, const float2 &b)
+ {
+ return a.x * b.x + a.y * b.y;
+ }
+
+ static float2 interpolate(const float2 &a, const float2 &b, float t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static float distance(const float2 &a, const float2 &b)
+ {
+ return (a - b).length();
+ }
+
+ static float distance_squared(const float2 &a, const float2 &b)
+ {
+ return float2::dot(a, b);
+ }
+
+ struct isect_result {
+ enum {
+ LINE_LINE_COLINEAR = -1,
+ LINE_LINE_NONE = 0,
+ LINE_LINE_EXACT = 1,
+ LINE_LINE_CROSS = 2,
+ } kind;
+ float lambda;
+ float mu;
+ };
+
+ static isect_result isect_seg_seg(const float2 &v1,
+ const float2 &v2,
+ const float2 &v3,
+ const float2 &v4);
};
} // namespace BLI
diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh
index 9678fa4b2d3..db174a2e0e4 100644
--- a/source/blender/blenlib/BLI_float3.hh
+++ b/source/blender/blenlib/BLI_float3.hh
@@ -172,6 +172,11 @@ struct float3 {
return {a.x / b, a.y / b, a.z / b};
}
+ friend bool operator==(const float3 &a, const float3 &b)
+ {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
friend std::ostream &operator<<(std::ostream &stream, const float3 &v)
{
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index d74b6295002..279f248e8c7 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -288,7 +288,6 @@ double double_round(double x, int ndigits);
#else
# define BLI_ASSERT_UNIT_V2(v) (void)(v)
# define BLI_ASSERT_UNIT_V3(v) (void)(v)
-# define BLI_ASSERT_UNIT_V3_DB(v) (void)(v)
# define BLI_ASSERT_UNIT_QUAT(v) (void)(v)
# define BLI_ASSERT_ZERO_M3(m) (void)(m)
# define BLI_ASSERT_ZERO_M4(m) (void)(m)
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index fb976dcf09b..563bcad5d14 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -42,7 +42,6 @@ extern "C" {
/********************************** Polygons *********************************/
float normal_tri_v3(float r[3], const float a[3], const float b[3], const float c[3]);
-double normal_tri_v3_db(double r[3], const double a[3], const double b[3], const double c[3]);
float normal_quad_v3(
float r[3], const float a[3], const float b[3], const float c[3], const float d[3]);
float normal_poly_v3(float r[3], const float verts[][3], unsigned int nr);
@@ -81,7 +80,6 @@ void plane_to_point_vector_v3_normalized(const float plane[4],
float r_plane_no[3]);
MINLINE float plane_point_side_v3(const float plane[4], const float co[3]);
-MINLINE double plane_point_side_v3_db(const double plane[4], const double co[3]);
/********************************* Volume **********************************/
@@ -116,7 +114,6 @@ float dist_to_line_segment_v2(const float p[2], const float l1[2], const float l
float dist_signed_squared_to_plane_v3(const float p[3], const float plane[4]);
float dist_squared_to_plane_v3(const float p[3], const float plane[4]);
-double dist_squared_to_plane_normalized_v3_db(const double pt[3], const double plane[4]);
float dist_signed_to_plane_v3(const float p[3], const float plane[4]);
float dist_to_plane_v3(const float p[3], const float plane[4]);
@@ -129,7 +126,6 @@ float dist_to_plane3_v3(const float p[3], const float plane[4]);
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
float dist_squared_to_line_v3(const float p[3], const float l1[3], const float l2[3]);
-double dist_squared_to_line_v3_db(const double p[3], const double l1[3], const double l2[3]);
float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3]);
float dist_signed_squared_to_corner_v3v3v3(const float p[3],
const float v1[3],
@@ -200,10 +196,6 @@ double closest_to_line_v2_db(double r_close[2],
const double l1[2],
const double l2[2]);
float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3]);
-double closest_to_line_v3_db(double r_close[3],
- const double p[3],
- const double l1[3],
- const double l2[3]);
void closest_to_line_segment_v2(float r_close[2],
const float p[2],
const float l1[2],
@@ -215,9 +207,6 @@ void closest_to_line_segment_v3(float r_close[3],
void closest_to_plane_normalized_v3(float r_close[3], const float plane[4], const float pt[3]);
void closest_to_plane_v3(float r_close[3], const float plane[4], const float pt[3]);
void closest_to_plane3_normalized_v3(float r_close[3], const float plane[3], const float pt[3]);
-void closest_to_plane3_normalized_v3_db(double r_close[3],
- const double plane[3],
- const double pt[3]);
void closest_to_plane3_v3(float r_close[3], const float plane[3], const float pt[3]);
/* Set 'r' to the point in triangle (t1, t2, t3) closest to point 'p' */
@@ -238,11 +227,6 @@ float line_point_factor_v3_ex(const float p[3],
const float l2[3],
const float epsilon,
const float fallback);
-double line_point_factor_v3_ex_db(const double p[3],
- const double l1[3],
- const double l2[3],
- const double epsilon,
- const double fallback);
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3]);
float line_point_factor_v2_ex(const float p[2],
@@ -318,25 +302,12 @@ int isect_line_line_epsilon_v3(const float v1[3],
float i1[3],
float i2[3],
const float epsilon);
-int isect_line_line_epsilon_v3_db(const double v1[3],
- const double v2[3],
- const double v3[3],
- const double v4[3],
- double i1[3],
- double i2[3],
- const double epsilon);
int isect_line_line_v3(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3],
float r_i1[3],
float r_i2[3]);
-int isect_line_line_v3_db(const double v1[3],
- const double v2[3],
- const double v3[3],
- const double v4[3],
- double r_i1[3],
- double r_i2[3]);
bool isect_line_line_strict_v3(const float v1[3],
const float v2[3],
const float v3[3],
@@ -380,10 +351,6 @@ bool isect_plane_plane_v3(const float plane_a[4],
const float plane_b[4],
float r_isect_co[3],
float r_isect_no[3]) ATTR_WARN_UNUSED_RESULT;
-bool isect_plane_plane_v3_db(const double plane_a[4],
- const double plane_b[4],
- double r_isect_co[3],
- double r_isect_no[3]) ATTR_WARN_UNUSED_RESULT;
/* line/ray triangle */
bool isect_line_segment_tri_v3(const float p1[3],
@@ -524,10 +491,6 @@ bool isect_aabb_aabb_v3(const float min1[3],
const float max1[3],
const float min2[3],
const float max2[3]);
-bool isect_aabb_aabb_v3_db(const double min1[3],
- const double max1[3],
- const double min2[3],
- const double max2[3]);
struct IsectRayAABB_Precalc {
float ray_origin[3];
@@ -815,7 +778,6 @@ float form_factor_hemi_poly(
void axis_dominant_v3_to_m3_negate(float r_mat[3][3], const float normal[3]);
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3]);
-void axis_dominant_v3_to_m3_db(double r_mat[3][3], const double normal[3]);
MINLINE void axis_dominant_v3(int *r_axis_a, int *r_axis_b, const float axis[3]);
MINLINE float axis_dominant_v3_max(int *r_axis_a,
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 879c1b720e4..2d11797bc34 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -226,7 +226,6 @@ bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], const float epsilon);
bool invert_m3(float R[3][3]);
bool invert_m3_m3(float R[3][3], const float A[3][3]);
-bool invert_m3_m3_db(double R[3][3], const double A[3][3]);
bool invert_m4(float R[4][4]);
bool invert_m4_m4(float R[4][4], const float A[4][4]);
bool invert_m4_m4_fallback(float R[4][4], const float A[4][4]);
@@ -242,7 +241,6 @@ void mul_m3_v3_db(const double M[3][3], double r[3]);
/****************************** Linear Algebra *******************************/
void transpose_m3(float R[3][3]);
-void transpose_m3_db(double R[3][3]);
void transpose_m3_m3(float R[3][3], const float A[3][3]);
void transpose_m3_m4(float R[3][3], const float A[4][4]);
void transpose_m4(float R[4][4]);
@@ -283,14 +281,12 @@ bool is_uniform_scaled_m4(const float m[4][4]);
*/
void adjoint_m2_m2(float R[2][2], const float A[2][2]);
void adjoint_m3_m3(float R[3][3], const float A[3][3]);
-void adjoint_m3_m3_db(double R[3][3], const double A[3][3]);
void adjoint_m4_m4(float R[4][4], const float A[4][4]);
float determinant_m2(float a, float b, float c, float d);
float determinant_m3(
float a, float b, float c, float d, float e, float f, float g, float h, float i);
float determinant_m3_array(const float m[3][3]);
-double determinant_m3_array_db(const double m[3][3]);
float determinant_m4_mat3_array(const float m[4][4]);
float determinant_m4(const float A[4][4]);
diff --git a/source/blender/blenlib/BLI_math_mpq.hh b/source/blender/blenlib/BLI_math_mpq.hh
new file mode 100644
index 00000000000..ede30da29b8
--- /dev/null
+++ b/source/blender/blenlib/BLI_math_mpq.hh
@@ -0,0 +1,26 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_MATH_MPQ_HH__
+#define __BLI_MATH_MPQ_HH__
+
+#include "gmpxx.h"
+
+namespace BLI {
+
+} // namespace BLI
+
+#endif /* __BLI_MATH_NPQ_HH__ */
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index 46f83604e5a..b4b0265ca6f 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -82,8 +82,6 @@ MINLINE void copy_v3_v3_int(int r[3], const int a[3]);
MINLINE void copy_v4_v4_int(int r[4], const int a[4]);
/* double */
MINLINE void zero_v3_db(double r[3]);
-MINLINE void zero_v4_db(double r[4]);
-MINLINE void copy_v3_db(double r[3], double d);
MINLINE void copy_v2_v2_db(double r[2], const double a[2]);
MINLINE void copy_v3_v3_db(double r[3], const double a[3]);
MINLINE void copy_v4_v4_db(double r[4], const double a[4]);
@@ -118,7 +116,6 @@ MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2]);
MINLINE void add_v3_v3(float r[3], const float a[3]);
MINLINE void add_v3_v3_db(double r[3], const double a[3]);
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3]);
-MINLINE void add_v3_v3v3_db(double r[3], const double a[3], const double b[3]);
MINLINE void add_v4_v4(float r[4], const float a[4]);
MINLINE void add_v4_v4v4(float r[4], const float a[4], const float b[4]);
@@ -142,9 +139,8 @@ MINLINE void mul_v2_fl(float r[2], float f);
MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f);
MINLINE void mul_v3_fl(float r[3], float f);
MINLINE void mul_v3db_db(double r[3], double f);
-MINLINE void mul_v3db_db(double r[3], double f);
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f);
-MINLINE void mul_v3db_v3dbdb(double r[3], const double a[3], double f);
+MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f);
MINLINE void mul_v2_v2(float r[2], const float a[2]);
MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2]);
MINLINE void mul_v3_v3(float r[3], const float a[3]);
@@ -165,12 +161,9 @@ MINLINE float dot_m4_v3_row_z(const float M[4][4], const float a[3]) ATTR_WARN_U
MINLINE void madd_v2_v2fl(float r[2], const float a[2], float f);
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f);
-MINLINE void madd_v3db_v3dbdb(double r[3], const double a[3], double f);
MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3]);
-MINLINE void madd_v3_v3v3_db(double r[3], const double a[3], const double b[3]);
MINLINE void madd_v2_v2v2fl(float r[2], const float a[2], const float b[2], float f);
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f);
-MINLINE void madd_v3_v3v3db_db(double r[3], const double a[3], const double b[3], double f);
MINLINE void madd_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3]);
MINLINE void madd_v4_v4fl(float r[4], const float a[4], float f);
MINLINE void madd_v4_v4v4(float r[4], const float a[4], const float b[4]);
@@ -224,40 +217,40 @@ MINLINE void star_m3_v3(float rmat[3][3], float a[3]);
/*********************************** Length **********************************/
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double len_squared_v2_db(const double v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double len_squared_v3_db(const double v[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE int len_manhattan_v2_int(const int v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE double len_v2_db(const double a[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE double len_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2v2_int(const int v1[2], const int v2[2]);
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE double len_squared_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double len_squared_v3v3_db(const double a[3], const double b[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_squared_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE int len_manhattan_v2v2_int(const int a[2], const int b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double len_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double len_v3v3_db(const double a[3], const double b[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE double len_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE double len_squared_v3_db(const double v[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float normalize_v2_length(float r[2], const float unit_scale);
MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_scale);
MINLINE float normalize_v3_length(float r[3], const float unit_scale);
MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_scale);
-MINLINE double normalize_v3_length_d(double n[3], const double unit_scale);
+MINLINE double normalize_v3_length_db(double n[3], const double unit_scale);
+MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], const double unit_scale);
MINLINE float normalize_v2(float r[2]);
MINLINE float normalize_v2_v2(float r[2], const float a[2]);
MINLINE float normalize_v3(float r[3]);
MINLINE float normalize_v3_v3(float r[3], const float a[3]);
-MINLINE double normalize_v3_d(double n[3]);
+MINLINE double normalize_v3_v3_db(double r[3], const double a[3]);
+MINLINE double normalize_v3_db(double n[3]);
/******************************* Interpolation *******************************/
@@ -351,9 +344,6 @@ MINLINE bool compare_v2v2(const float a[2],
MINLINE bool compare_v3v3(const float a[3],
const float b[3],
const float limit) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_v3v3_db(const double a[3],
- const double b[3],
- const double limit) ATTR_WARN_UNUSED_RESULT;
MINLINE bool compare_v4v4(const float a[4],
const float b[4],
const float limit) ATTR_WARN_UNUSED_RESULT;
@@ -379,10 +369,6 @@ MINLINE float line_point_side_v2(const float l1[2],
const float l2[2],
const float pt[2]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double line_point_side_v2_db(const double l1[2],
- const double l2[2],
- const double pt[2]) ATTR_WARN_UNUSED_RESULT;
-
/********************************** Angles ***********************************/
/* - angle with 2 arguments is angle between vector */
/* - angle with 3 arguments is angle between 3 points at the middle point */
@@ -426,14 +412,11 @@ void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_pr
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3]);
void project_plane_v2_v2v2(float out[2], const float p[2], const float v_plane[2]);
void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[3]);
-void project_plane_normalized_v3_v3v3_db(double out[3],
- const double p[3],
- const double v_plane[3]);
void project_plane_normalized_v2_v2v2(float out[2], const float p[2], const float v_plane[2]);
void project_v3_plane(float out[3], const float plane_no[3], const float plane_co[3]);
void reflect_v3_v3v3(float out[3], const float vec[3], const float normal[3]);
+void reflect_v3_v3v3_db(double out[3], const double vec[3], const double normal[3]);
void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]);
-void ortho_basis_v3v3_v3_db(double r_n1[3], double r_n2[3], const double n[3]);
void ortho_v3_v3(float out[3], const float v[3]);
void ortho_v2_v2(float out[2], const float v[2]);
void bisect_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3]);
diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh
new file mode 100644
index 00000000000..77addf51f0e
--- /dev/null
+++ b/source/blender/blenlib/BLI_mesh_intersect.hh
@@ -0,0 +1,122 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_MESH_INTERSECT_HH__
+#define __BLI_MESH_INTERSECT_HH__
+
+/** \file
+ * \ingroup bli
+ *
+ * Work in progress on mesh intersection library function.
+ */
+
+#include <iostream>
+
+#include "gmpxx.h"
+
+#include "BLI_array.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_vector.hh"
+
+namespace BLI {
+
+namespace MeshIntersect {
+
+/* The indices are for vertices in some external space of coordinates.
+ * The "orig" component is used to track how a triangle originally came
+ * from some other space of triangle indices. Which we usually need,
+ * and it packs nicely into this structure, so keeping it here will save
+ * memory.
+ */
+struct IndexedTriangle {
+ IndexedTriangle() : m_v{-1, -1, -1}, m_orig{-1}
+ {
+ }
+ IndexedTriangle(int v0, int v1, int v2, int orig) : m_v{v0, v1, v2}, m_orig{orig}
+ {
+ }
+ IndexedTriangle(const IndexedTriangle &other) : m_orig{other.m_orig}
+ {
+ for (int i = 0; i < 3; ++i) {
+ this->m_v[i] = other.m_v[i];
+ }
+ }
+
+ int v0() const
+ {
+ return m_v[0];
+ }
+ int v1() const
+ {
+ return m_v[1];
+ }
+ int v2() const
+ {
+ return m_v[2];
+ }
+ int orig() const
+ {
+ return m_orig;
+ }
+ int operator[](int i) const
+ {
+ return m_v[i];
+ }
+ int &operator[](int i)
+ {
+ return m_v[i];
+ }
+
+ private:
+ int m_v[3];
+ int m_orig;
+};
+
+struct TriMesh {
+ Array<mpq3> vert;
+ Array<IndexedTriangle> tri;
+
+ TriMesh() = default;
+};
+
+/* The output will have dup vertices merged and degenerate triangles ignored.
+ * If the input has overlapping coplanar triangles, then there will be
+ * as many duplicates as there are overlaps in each overlapping triangular region.
+ * The orig field of each IndexedTriangle will give the orig index in the input TriMesh
+ * that the output triangle was a part of (input can have -1 for that field and then
+ * the index in tri[] will be used as the original index).
+ */
+TriMesh trimesh_self_intersect(const TriMesh &tm_in);
+
+/* For debugging output. */
+std::ostream &operator<<(std::ostream &os, const IndexedTriangle &tri);
+
+std::ostream &operator<<(std::ostream &os, const TriMesh &tm);
+
+void write_html_trimesh(const Array<mpq3> &vert,
+ const Array<IndexedTriangle> &tri,
+ const std::string &fname,
+ const std::string &label);
+
+void write_obj_trimesh(const Array<mpq3> &vert,
+ const Array<IndexedTriangle> &tri,
+ const std::string &objname);
+
+} /* namespace MeshIntersect */
+
+} /* namespace BLI */
+
+#endif /* __BLI_MESH_INTERSECT_HH__ */
diff --git a/source/blender/blenlib/BLI_mpq2.hh b/source/blender/blenlib/BLI_mpq2.hh
new file mode 100644
index 00000000000..48c9aa9580d
--- /dev/null
+++ b/source/blender/blenlib/BLI_mpq2.hh
@@ -0,0 +1,143 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_MPQ2_HH__
+#define __BLI_MPQ2_HH__
+
+#include "gmpxx.h"
+
+#include "BLI_math_mpq.hh"
+#include "BLI_mpq3.hh"
+
+namespace BLI {
+
+struct mpq2 {
+ mpq_class x, y;
+
+ mpq2() = default;
+
+ mpq2(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]}
+ {
+ }
+
+ mpq2(mpq_class x, mpq_class y) : x(x), y(y)
+ {
+ }
+
+ mpq2(const mpq3 &other) : x(other.x), y(other.y)
+ {
+ }
+
+ operator mpq_class *()
+ {
+ return &x;
+ }
+
+ operator const mpq_class *() const
+ {
+ return &x;
+ }
+
+ /* Cannot do this exactly in rational arithmetic!
+ * Approximate by going in and out of doubles.
+ */
+ mpq_class length() const
+ {
+ mpq_class lsquared = dot(*this, *this);
+ return mpq_class(sqrt(lsquared.get_d()));
+ }
+
+ friend mpq2 operator+(const mpq2 &a, const mpq2 &b)
+ {
+ return {a.x + b.x, a.y + b.y};
+ }
+
+ friend mpq2 operator-(const mpq2 &a, const mpq2 &b)
+ {
+ return {a.x - b.x, a.y - b.y};
+ }
+
+ friend mpq2 operator*(const mpq2 &a, mpq_class b)
+ {
+ return {a.x * b, a.y * b};
+ }
+
+ friend mpq2 operator/(const mpq2 &a, mpq_class b)
+ {
+ BLI_assert(b != 0);
+ return {a.x / b, a.y / b};
+ }
+
+ friend mpq2 operator*(mpq_class a, const mpq2 &b)
+ {
+ return b * a;
+ }
+
+ friend bool operator==(const mpq2 &a, const mpq2 &b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const mpq2 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ")";
+ return stream;
+ }
+
+ static mpq_class dot(const mpq2 &a, const mpq2 &b)
+ {
+ return a.x * b.x + a.y * b.y;
+ }
+
+ static mpq2 interpolate(const mpq2 &a, const mpq2 &b, mpq_class t)
+ {
+ return a * (1 - t) + b * t;
+ }
+
+ static mpq_class distance(const mpq2 &a, const mpq2 &b)
+ {
+ return (a - b).length();
+ }
+
+ static mpq_class distance_squared(const mpq2 &a, const mpq2 &b)
+ {
+ return dot(a, b);
+ }
+
+ struct isect_result {
+ enum {
+ LINE_LINE_COLINEAR = -1,
+ LINE_LINE_NONE = 0,
+ LINE_LINE_EXACT = 1,
+ LINE_LINE_CROSS = 2,
+ } kind;
+ mpq_class lambda;
+ mpq_class mu;
+ };
+
+ static isect_result isect_seg_seg(const mpq2 &v1,
+ const mpq2 &v2,
+ const mpq2 &v3,
+ const mpq2 &v4);
+
+ static int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c);
+
+ static int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d);
+};
+
+} // namespace BLI
+
+#endif /* __BLI_MPQ_HH__ */
diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh
new file mode 100644
index 00000000000..ca556e46e83
--- /dev/null
+++ b/source/blender/blenlib/BLI_mpq3.hh
@@ -0,0 +1,257 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BLI_MPQ3_HH__
+#define __BLI_MPQ3_HH__
+
+#include <iostream>
+
+#include "BLI_math.h"
+#include "BLI_math_mpq.hh"
+#include "gmpxx.h"
+
+namespace BLI {
+
+struct mpq3 {
+ mpq_class x, y, z;
+
+ mpq3() = default;
+
+ mpq3(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
+ {
+ }
+
+ mpq3(const mpq_class (*ptr)[3]) : mpq3((const mpq_class *)ptr)
+ {
+ }
+
+ explicit mpq3(mpq_class value) : x(value), y(value), z(value)
+ {
+ }
+
+ explicit mpq3(int value) : x(value), y(value), z(value)
+ {
+ }
+
+ mpq3(mpq_class x, mpq_class y, mpq_class z) : x{x}, y{y}, z{z}
+ {
+ }
+
+ operator const mpq_class *() const
+ {
+ return &x;
+ }
+
+ operator mpq_class *()
+ {
+ return &x;
+ }
+
+ /* Cannot do this exactly in rational arithmetic!
+ * Approximate by going in and out of doubles.
+ */
+ mpq_class normalize_and_get_length()
+ {
+ double dv[3] = {x.get_d(), y.get_d(), z.get_d()};
+ double len = normalize_v3_db(dv);
+ this->x = mpq_class(dv[0]);
+ this->y = mpq_class(dv[1]);
+ this->z = mpq_class(dv[2]);
+ return len;
+ }
+
+ mpq3 normalized() const
+ {
+ double dv[3] = {x.get_d(), y.get_d(), z.get_d()};
+ double dr[3];
+ normalize_v3_v3_db(dr, dv);
+ return mpq3(mpq_class(dr[0]), mpq_class(dr[1]), mpq_class(dr[2]));
+ }
+
+ /* Cannot do this exactly in rational arithmetic!
+ * Approximate by going in and out of double.
+ */
+ mpq_class length() const
+ {
+ mpq_class lsquared = this->length_squared();
+ double dsquared = lsquared.get_d();
+ double d = sqrt(dsquared);
+ return mpq_class(d);
+ }
+
+ mpq_class length_squared() const
+ {
+ return x * x + y * y + z * z;
+ }
+
+ void reflect(const mpq3 &normal)
+ {
+ *this = this->reflected(normal);
+ }
+
+ mpq3 reflected(const mpq3 &normal) const
+ {
+ mpq3 result;
+ const mpq_class dot2 = 2 * dot(*this, normal);
+ result.x = this->x - (dot2 * normal.x);
+ result.y = this->y - (dot2 * normal.y);
+ result.z = this->z - (dot2 * normal.z);
+ return result;
+ }
+
+ static mpq3 safe_divide(const mpq3 &a, const mpq3 &b)
+ {
+ mpq3 result;
+ result.x = (b.x == 0) ? mpq_class(0) : a.x / b.x;
+ result.y = (b.y == 0) ? mpq_class(0) : a.y / b.y;
+ result.z = (b.z == 0) ? mpq_class(0) : a.z / b.z;
+ return result;
+ }
+
+ void invert()
+ {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ friend mpq3 operator+(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ void operator+=(const mpq3 &b)
+ {
+ this->x += b.x;
+ this->y += b.y;
+ this->z += b.z;
+ }
+
+ friend mpq3 operator-(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3(a.x - b.x, a.y - b.y, a.z - b.z);
+ }
+
+ friend mpq3 operator-(const mpq3 &a)
+ {
+ return mpq3(-a.x, -a.y, -a.z);
+ }
+
+ void operator-=(const mpq3 &b)
+ {
+ this->x -= b.x;
+ this->y -= b.y;
+ this->z -= b.z;
+ }
+
+ void operator*=(mpq_class scalar)
+ {
+ this->x *= scalar;
+ this->y *= scalar;
+ this->z *= scalar;
+ }
+
+ void operator*=(const mpq3 &other)
+ {
+ this->x *= other.x;
+ this->y *= other.y;
+ this->z *= other.z;
+ }
+
+ friend mpq3 operator*(const mpq3 &a, const mpq3 &b)
+ {
+ return {a.x * b.x, a.y * b.y, a.z * b.z};
+ }
+
+ friend mpq3 operator*(const mpq3 &a, const mpq_class &b)
+ {
+ return mpq3(a.x * b, a.y * b, a.z * b);
+ }
+
+ friend mpq3 operator*(const mpq_class &a, const mpq3 &b)
+ {
+ return mpq3(a * b.x, a * b.y, a * b.z);
+ }
+
+ friend mpq3 operator/(const mpq3 &a, const mpq_class &b)
+ {
+ BLI_assert(b != 0);
+ return mpq3(a.x / b, a.y / b, a.z / b);
+ }
+
+ friend bool operator==(const mpq3 &a, const mpq3 &b)
+ {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const mpq3 &v)
+ {
+ stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
+ return stream;
+ }
+
+ static mpq_class dot(const mpq3 &a, const mpq3 &b)
+ {
+ return a.x * b.x + a.y * b.y + a.z * b.z;
+ }
+
+ static mpq3 cross(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
+ }
+
+ static mpq3 cross_high_precision(const mpq3 &a, const mpq3 &b)
+ {
+ return cross(a, b);
+ }
+
+ static mpq3 project(const mpq3 &a, const mpq3 &b)
+ {
+ const mpq_class mul = mpq3::dot(a, b) / mpq3::dot(b, b);
+ return mpq3(mul * b[0], mul * b[1], mul * b[2]);
+ }
+
+ static mpq_class distance(const mpq3 &a, const mpq3 &b)
+ {
+ mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z);
+ return diff.length();
+ }
+
+ static mpq_class distance_squared(const mpq3 &a, const mpq3 &b)
+ {
+ return mpq3::dot(a, b);
+ }
+
+ static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t)
+ {
+ mpq_class s = 1 - t;
+ return mpq3(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t);
+ }
+
+ static int dominant_axis(const mpq3 &a)
+ {
+ mpq_class x = abs(a[0]);
+ mpq_class y = abs(a[1]);
+ mpq_class z = abs(a[2]);
+ return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2));
+ }
+
+ static int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d);
+};
+
+} // namespace BLI
+
+#endif /* __BLI_MPQ3_HH__ */
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 7757b838afe..f09291ecf03 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -32,6 +32,7 @@ set(INC
set(INC_SYS
${ZLIB_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
+ ${GMP_INCLUDE_DIRS}
)
set(SRC
@@ -60,10 +61,11 @@ set(SRC
intern/astar.c
intern/bitmap.c
intern/bitmap_draw_2d.c
+ intern/boolean.cc
intern/boxpack_2d.c
intern/buffer.c
intern/convexhull_2d.c
- intern/delaunay_2d.c
+ intern/delaunay_2d.cc
intern/dot_export.cc
intern/dynlib.c
intern/easing.c
@@ -97,9 +99,11 @@ set(SRC
intern/math_rotation.c
intern/math_solvers.c
intern/math_statistics.c
+ intern/math_vec.cc
intern/math_vector.c
intern/math_vector_inline.c
intern/memory_utils.c
+ intern/mesh_intersect.cc
intern/noise.c
intern/path_util.c
intern/polyfill_2d.c
@@ -154,6 +158,7 @@ set(SRC
BLI_bitmap.h
BLI_bitmap_draw_2d.h
BLI_blenlib.h
+ BLI_boolean.h
BLI_boxpack_2d.h
BLI_buffer.h
BLI_color.hh
@@ -167,6 +172,8 @@ set(SRC
BLI_dlrbTree.h
BLI_dot_export.hh
BLI_dot_export_attribute_enums.hh
+ BLI_double2.hh
+ BLI_double3.hh
BLI_dynlib.h
BLI_dynstr.h
BLI_easing.h
@@ -211,6 +218,7 @@ set(SRC
BLI_math_geom.h
BLI_math_inline.h
BLI_math_interp.h
+ BLI_math_mpq.hh
BLI_math_matrix.h
BLI_math_rotation.h
BLI_math_solvers.h
@@ -222,6 +230,9 @@ set(SRC
BLI_memory_utils.h
BLI_memory_utils.hh
BLI_mempool.h
+ BLI_mesh_intersect.hh
+ BLI_mpq2.hh
+ BLI_mpq3.hh
BLI_noise.h
BLI_open_addressing.hh
BLI_optional.hh
@@ -293,6 +304,18 @@ if(WITH_TBB)
)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
if(WIN32)
list(APPEND INC
../../../intern/utfconv
diff --git a/source/blender/blenlib/intern/boolean.cc b/source/blender/blenlib/intern/boolean.cc
new file mode 100644
index 00000000000..ae4422dabec
--- /dev/null
+++ b/source/blender/blenlib/intern/boolean.cc
@@ -0,0 +1,93 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <iostream>
+
+#include "gmpxx.h"
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_mesh_intersect.hh"
+#include "BLI_mpq3.hh"
+
+#include "BLI_boolean.h"
+
+namespace BLI {
+namespace MeshIntersect {
+
+static TriMesh self_boolean(const TriMesh &tm_in, int bool_optype)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nSELF_BOOLEAN op=" << bool_optype << "\n";
+ }
+ TriMesh tm_si = trimesh_self_intersect(tm_in);
+ return tm_si;
+}
+
+} /* namespace MeshIntersect */
+} /* namespace BLI */
+
+extern "C" Boolean_trimesh_output *BLI_boolean_trimesh(const Boolean_trimesh_input *input,
+ int bool_optype)
+{
+ constexpr int dbg_level = 1;
+ BLI::MeshIntersect::TriMesh tm_in;
+ tm_in.vert = BLI::Array<BLI::mpq3>(input->vert_len);
+ for (int v = 0; v < input->vert_len; ++v) {
+ tm_in.vert[v] = BLI::mpq3(
+ input->vert_coord[v][0], input->vert_coord[v][1], input->vert_coord[v][2]);
+ }
+ tm_in.tri = BLI::Array<BLI::MeshIntersect::IndexedTriangle>(input->tri_len);
+ for (int t = 0; t < input->tri_len; ++t) {
+ tm_in.tri[t] = BLI::MeshIntersect::IndexedTriangle(
+ input->tri[t][0], input->tri[t][1], input->tri[t][2], t);
+ }
+ BLI::MeshIntersect::TriMesh tm_out = self_boolean(tm_in, bool_optype);
+ if (dbg_level > 0) {
+ BLI::MeshIntersect::write_html_trimesh(
+ tm_out.vert, tm_out.tri, "mesh_boolean_test.html", "after self_boolean");
+ BLI::MeshIntersect::write_obj_trimesh(tm_out.vert, tm_out.tri, "test_tettet");
+ }
+ int nv = tm_out.vert.size();
+ int nt = tm_out.tri.size();
+ Boolean_trimesh_output *output = static_cast<Boolean_trimesh_output *>(
+ MEM_mallocN(sizeof(*output), __func__));
+ output->vert_len = nv;
+ output->tri_len = nt;
+ output->vert_coord = static_cast<decltype(output->vert_coord)>(
+ MEM_malloc_arrayN(nv, sizeof(output->vert_coord[0]), __func__));
+ output->tri = static_cast<decltype(output->tri)>(
+ MEM_malloc_arrayN(nt, sizeof(output->tri[0]), __func__));
+ for (int v = 0; v < nv; ++v) {
+ output->vert_coord[v][0] = static_cast<float>(tm_out.vert[v][0].get_d());
+ output->vert_coord[v][1] = static_cast<float>(tm_out.vert[v][1].get_d());
+ output->vert_coord[v][2] = static_cast<float>(tm_out.vert[v][2].get_d());
+ }
+ for (int t = 0; t < nt; ++t) {
+ output->tri[t][0] = tm_out.tri[t].v0();
+ output->tri[t][1] = tm_out.tri[t].v1();
+ output->tri[t][2] = tm_out.tri[t].v2();
+ }
+ return output;
+}
+
+extern "C" void BLI_boolean_trimesh_free(Boolean_trimesh_output *output)
+{
+ MEM_freeN(output->vert_coord);
+ MEM_freeN(output->tri);
+ MEM_freeN(output);
+}
diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c
deleted file mode 100644
index 4e0cd3a78dc..00000000000
--- a/source/blender/blenlib/intern/delaunay_2d.c
+++ /dev/null
@@ -1,5171 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup bli
- *
- * Constrained 2d Delaunay Triangulation.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_array.h"
-#include "BLI_bitmap.h"
-#include "BLI_linklist.h"
-#include "BLI_math.h"
-#include "BLI_memarena.h"
-#include "BLI_mempool.h"
-
-#include "BLI_delaunay_2d.h"
-
-/* Uncomment this define to get helpful debugging functions etc. defined. */
-// #define DEBUG_CDT
-
-struct CDTEdge;
-struct CDTFace;
-struct CDTVert;
-
-typedef struct SymEdge {
- struct SymEdge *next; /* In face, doing CCW traversal of face. */
- struct SymEdge *rot; /* CCW around vert. */
- struct CDTVert *vert; /* Vert at origin. */
- struct CDTEdge *edge; /* Undirected edge this is for. */
- struct CDTFace *face; /* Face on left side. */
-} SymEdge;
-
-typedef struct CDTVert {
- double co[2]; /* Coordinate. */
- SymEdge *symedge; /* Some edge attached to it. */
- LinkNode *input_ids; /* List of corresponding vertex input ids. */
- int index; /* Index into array that cdt keeps. */
- int merge_to_index; /* Index of a CDTVert that this has merged to. -1 if no merge. */
- int visit_index; /* Which visit epoch has this been seen. */
-} CDTVert;
-
-typedef struct CDTEdge {
- LinkNode *input_ids; /* List of input edge ids that this is part of. */
- SymEdge symedges[2]; /* The directed edges for this edge. */
- bool in_queue; /* Used in flipping algorithm. */
-} CDTEdge;
-
-typedef struct CDTFace {
- SymEdge *symedge; /* A symedge in face; only used during output, so only valid then. */
- LinkNode *input_ids; /* List of input face ids that this is part of. */
- int visit_index; /* Which visit epoch has this been seen. */
- bool deleted; /* Marks this face no longer used. */
- bool in_queue; /* Used in remove_small_features algorithm. */
-} CDTFace;
-
-typedef struct CDT_state {
- LinkNode *edges; /* List of CDTEdge pointer. */
- LinkNode *faces; /* List of CDTFace pointer. */
- CDTFace *outer_face; /* Which CDTFace is the outer face. */
- CDTVert **vert_array; /* Array of CDTVert pointer, grows. */
- int vert_array_len; /* Current length of vert_array. */
- int vert_array_len_alloc; /* Allocated length of vert_array. */
- int input_vert_tot; /* How many verts were in input (will be first in vert_array). */
- double minx; /* Used for debug drawing. */
- double miny; /* Used for debug drawing. */
- double maxx; /* Used for debug drawing. */
- double maxy; /* Used for debug drawing. */
- double margin; /* Used for debug drawing. */
- int visit_count; /* Used for visiting things without having to initialized their visit fields. */
- int face_edge_offset; /* Input edge id where we start numbering the face edges. */
- MemArena *arena; /* Most allocations are done from here, so can free all at once at end. */
- BLI_mempool *listpool; /* Allocations of ListNodes done from this pool. */
- double epsilon; /* The user-specified nearness limit. */
- double epsilon_squared; /* Square of epsilon. */
- bool output_prepared; /* Set after the mesh has been modified for output (may not be all
- triangles now). */
-} CDT_state;
-
-#define DLNY_ARENASIZE 1 << 14
-
-#ifdef DEBUG_CDT
-# ifdef __GNUC__
-# define ATTU __attribute__((unused))
-# else
-# define ATTU
-# endif
-# define F2(p) p[0], p[1]
-# define F3(p) p[0], p[1], p[2]
-struct CrossData;
-ATTU static void dump_se(const SymEdge *se, const char *lab);
-ATTU static void dump_se_short(const SymEdge *se, const char *lab);
-ATTU static void dump_v(const CDTVert *v, const char *lab);
-ATTU static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit);
-ATTU static void dump_id_list(const LinkNode *id_list, const char *lab);
-ATTU static void dump_cross_data(struct CrossData *cd, const char *lab);
-ATTU static void dump_cdt(const CDT_state *cdt, const char *lab);
-ATTU static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab);
-ATTU static void cdt_draw(CDT_state *cdt, const char *lab);
-ATTU static void cdt_draw_region(
- CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy);
-
-ATTU static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab);
-ATTU static void cdt_draw_edge_region(
- CDT_state *cdt, int v1, int v2, double dist, const char *lab);
-ATTU static void write_cdt_input_to_file(const CDT_input *inp);
-ATTU static void validate_cdt(CDT_state *cdt,
- bool check_all_tris,
- bool check_delaunay,
- bool check_visibility);
-#endif
-
-static void exactinit(void);
-static double orient2d(const double *pa, const double *pb, const double *pc);
-static double incircle(const double *pa, const double *pb, const double *pc, const double *pd);
-
-/** Return other #SymEdge for same #CDTEdge as se. */
-BLI_INLINE SymEdge *sym(const SymEdge *se)
-{
- return se->next->rot;
-}
-
-/** Return SymEdge whose next is se. */
-BLI_INLINE SymEdge *prev(const SymEdge *se)
-{
- return se->rot->next->rot;
-}
-
-/**
- * Return true if a -- b -- c are in that order, assuming they are on a straight line according to
- * orient2d and we know the order is either `abc` or `bac`.
- * This means `ab . ac` and `bc . ac` must both be non-negative. */
-static bool in_line(const double a[2], const double b[2], const double c[2])
-{
- double ab[2], bc[2], ac[2];
- sub_v2_v2v2_db(ab, b, a);
- sub_v2_v2v2_db(bc, c, b);
- sub_v2_v2v2_db(ac, c, a);
- if (dot_v2v2_db(ab, ac) < 0.0) {
- return false;
- }
- return dot_v2v2_db(bc, ac) >= 0.0;
-}
-
-#ifndef NDEBUG
-/** Is s2 reachable from s1 by next pointers with < limit hops? */
-static bool reachable(SymEdge *s1, SymEdge *s2, int limit)
-{
- int count = 0;
- for (SymEdge *s = s1; s && count < limit; s = s->next) {
- if (s == s2) {
- return true;
- }
- count++;
- }
- return false;
-}
-#endif
-
-/** Using array to store these instead of linked list so can make a random selection from them. */
-static CDTVert *add_cdtvert(CDT_state *cdt, double x, double y)
-{
- CDTVert *v = BLI_memarena_alloc(cdt->arena, sizeof(*v));
- v->co[0] = x;
- v->co[1] = y;
- v->input_ids = NULL;
- v->symedge = NULL;
- if (cdt->vert_array_len == cdt->vert_array_len_alloc) {
- CDTVert **old_array = cdt->vert_array;
- cdt->vert_array_len_alloc *= 4;
- cdt->vert_array = BLI_memarena_alloc(cdt->arena,
- cdt->vert_array_len_alloc * sizeof(cdt->vert_array[0]));
- memmove(cdt->vert_array, old_array, cdt->vert_array_len * sizeof(cdt->vert_array[0]));
- }
- BLI_assert(cdt->vert_array_len < cdt->vert_array_len_alloc);
- v->index = cdt->vert_array_len;
- v->merge_to_index = -1;
- v->visit_index = 0;
- cdt->vert_array[cdt->vert_array_len++] = v;
- return v;
-}
-
-static CDTEdge *add_cdtedge(
- CDT_state *cdt, CDTVert *v1, CDTVert *v2, CDTFace *fleft, CDTFace *fright)
-{
- CDTEdge *e = BLI_memarena_alloc(cdt->arena, sizeof(*e));
- SymEdge *se = &e->symedges[0];
- SymEdge *sesym = &e->symedges[1];
- e->input_ids = NULL;
- e->in_queue = false;
- BLI_linklist_prepend_arena(&cdt->edges, (void *)e, cdt->arena);
- se->edge = sesym->edge = e;
- se->face = fleft;
- sesym->face = fright;
- se->vert = v1;
- if (v1->symedge == NULL) {
- v1->symedge = se;
- }
- sesym->vert = v2;
- if (v2->symedge == NULL) {
- v2->symedge = sesym;
- }
- se->next = sesym->next = se->rot = sesym->rot = NULL;
- return e;
-}
-
-static CDTFace *add_cdtface(CDT_state *cdt)
-{
- CDTFace *f = BLI_memarena_alloc(cdt->arena, sizeof(*f));
- f->visit_index = 0;
- f->deleted = false;
- f->symedge = NULL;
- f->input_ids = NULL;
- f->in_queue = false;
- BLI_linklist_prepend_arena(&cdt->faces, (void *)f, cdt->arena);
- return f;
-}
-
-static bool id_in_list(const LinkNode *id_list, int id)
-{
- const LinkNode *ln;
-
- for (ln = id_list; ln; ln = ln->next) {
- if (POINTER_AS_INT(ln->link) == id) {
- return true;
- }
- }
- return false;
-}
-
-/** is any id in (range_start, range_start+1, ... , range_end) in id_list? */
-static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end)
-{
- const LinkNode *ln;
- int id;
-
- for (ln = id_list; ln; ln = ln->next) {
- id = POINTER_AS_INT(ln->link);
- if (id >= range_start && id <= range_end) {
- return true;
- }
- }
- return false;
-}
-
-static void add_to_input_ids(LinkNode **dst, int input_id, CDT_state *cdt)
-{
- if (!id_in_list(*dst, input_id)) {
- BLI_linklist_prepend_arena(dst, POINTER_FROM_INT(input_id), cdt->arena);
- }
-}
-
-static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state *cdt)
-{
- const LinkNode *ln;
-
- for (ln = src; ln; ln = ln->next) {
- add_to_input_ids(dst, POINTER_AS_INT(ln->link), cdt);
- }
-}
-
-BLI_INLINE bool is_border_edge(const CDTEdge *e, const CDT_state *cdt)
-{
- return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face;
-}
-
-BLI_INLINE bool is_constrained_edge(const CDTEdge *e)
-{
- return e->input_ids != NULL;
-}
-
-BLI_INLINE bool is_deleted_edge(const CDTEdge *e)
-{
- return e->symedges[0].next == NULL;
-}
-
-BLI_INLINE bool is_original_vert(const CDTVert *v, CDT_state *cdt)
-{
- return (v->index < cdt->input_vert_tot);
-}
-
-/** Return the Symedge that goes from v1 to v2, if it exists, else return NULL. */
-static SymEdge *find_symedge_between_verts(const CDTVert *v1, const CDTVert *v2)
-{
- SymEdge *tstart, *t;
-
- t = tstart = v1->symedge;
- do {
- if (t->next->vert == v2) {
- return t;
- }
- } while ((t = t->rot) != tstart);
- return NULL;
-}
-
-/** Return the SymEdge attached to v that has face f, if it exists, else return NULL. */
-static SymEdge *find_symedge_with_face(const CDTVert *v, const CDTFace *f)
-{
- SymEdge *tstart, *t;
-
- t = tstart = v->symedge;
- do {
- if (t->face == f) {
- return t;
- }
- } while ((t = t->rot) != tstart);
- return NULL;
-}
-
-/** Is there already an edge between a and b? */
-static inline bool exists_edge(const CDTVert *v1, const CDTVert *v2)
-{
- return find_symedge_between_verts(v1, v2) != NULL;
-}
-
-/** Is the vertex v incident on face f? */
-static bool vert_touches_face(const CDTVert *v, const CDTFace *f)
-{
- SymEdge *se = v->symedge;
- do {
- if (se->face == f) {
- return true;
- }
- } while ((se = se->rot) != v->symedge);
- return false;
-}
-
-/**
- * Assume s1 and s2 are both SymEdges in a face with > 3 sides,
- * and one is not the next of the other.
- * Add an edge from s1->v to s2->v, splitting the face in two.
- * The original face will continue to be associated with the subface
- * that has s1, and a new face will be made for s2's new face.
- * Return the new diagonal's CDTEdge *.
- */
-static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2)
-{
- CDTEdge *ediag;
- CDTFace *fold, *fnew;
- SymEdge *sdiag, *sdiagsym;
- SymEdge *s1prev, *s1prevsym, *s2prev, *s2prevsym, *se;
- BLI_assert(reachable(s1, s2, 20000));
- BLI_assert(reachable(s2, s1, 20000));
- fold = s1->face;
- fnew = add_cdtface(cdt);
- s1prev = prev(s1);
- s1prevsym = sym(s1prev);
- s2prev = prev(s2);
- s2prevsym = sym(s2prev);
- ediag = add_cdtedge(cdt, s1->vert, s2->vert, fnew, fold);
- sdiag = &ediag->symedges[0];
- sdiagsym = &ediag->symedges[1];
- sdiag->next = s2;
- sdiagsym->next = s1;
- s2prev->next = sdiagsym;
- s1prev->next = sdiag;
- s1->rot = sdiag;
- sdiag->rot = s1prevsym;
- s2->rot = sdiagsym;
- sdiagsym->rot = s2prevsym;
-#ifdef DEBUG_CDT
- BLI_assert(reachable(s2, sdiag, 2000));
-#endif
- for (se = s2; se != sdiag; se = se->next) {
- se->face = fnew;
- }
- add_list_to_input_ids(&fnew->input_ids, fold->input_ids, cdt);
- return ediag;
-}
-
-/**
- * Add a dangling edge from an isolated v to the vert at se in the same face as se->face.
- */
-static CDTEdge *add_vert_to_symedge_edge(CDT_state *cdt, CDTVert *v, SymEdge *se)
-{
- CDTEdge *e;
- SymEdge *se_rot, *se_rotsym, *new_se, *new_se_sym;
-
- se_rot = se->rot;
- se_rotsym = sym(se_rot);
- e = add_cdtedge(cdt, v, se->vert, se->face, se->face);
- new_se = &e->symedges[0];
- new_se_sym = &e->symedges[1];
- new_se->next = se;
- new_se_sym->next = new_se;
- new_se->rot = new_se;
- new_se_sym->rot = se_rot;
- se->rot = new_se_sym;
- se_rotsym->next = new_se_sym;
- return e;
-}
-
-/* Connect the verts of se1 and se2, assuming that currently those two SymEdges are on
- * the outer boundary (have face == outer_face) of two components that are isolated from
- * each other.
- */
-static CDTEdge *connect_separate_parts(CDT_state *cdt, SymEdge *se1, SymEdge *se2)
-{
- CDTEdge *e;
- SymEdge *se1_rot, *se1_rotsym, *se2_rot, *se2_rotsym, *new_se, *new_se_sym;
-
- BLI_assert(se1->face == cdt->outer_face && se2->face == cdt->outer_face);
- se1_rot = se1->rot;
- se1_rotsym = sym(se1_rot);
- se2_rot = se2->rot;
- se2_rotsym = sym(se2_rot);
- e = add_cdtedge(cdt, se1->vert, se2->vert, cdt->outer_face, cdt->outer_face);
- new_se = &e->symedges[0];
- new_se_sym = &e->symedges[1];
- new_se->next = se2;
- new_se_sym->next = se1;
- new_se->rot = se1_rot;
- new_se_sym->rot = se2_rot;
- se1->rot = new_se;
- se2->rot = new_se_sym;
- se1_rotsym->next = new_se;
- se2_rotsym->next = new_se_sym;
- return e;
-}
-
-/**
- * Split \a se at fraction \a lambda,
- * and return the new #CDTEdge that is the new second half.
- * Copy the edge input_ids into the new one.
- */
-static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda)
-{
- const double *a, *b;
- double p[2];
- CDTVert *v;
- CDTEdge *e;
- SymEdge *sesym, *newse, *newsesym, *senext, *sesymprev, *sesymprevsym;
- /* Split e at lambda. */
- a = se->vert->co;
- b = se->next->vert->co;
- sesym = sym(se);
- sesymprev = prev(sesym);
- sesymprevsym = sym(sesymprev);
- senext = se->next;
- p[0] = (1.0 - lambda) * a[0] + lambda * b[0];
- p[1] = (1.0 - lambda) * a[1] + lambda * b[1];
- v = add_cdtvert(cdt, p[0], p[1]);
- e = add_cdtedge(cdt, v, se->next->vert, se->face, sesym->face);
- sesym->vert = v;
- newse = &e->symedges[0];
- newsesym = &e->symedges[1];
- se->next = newse;
- newsesym->next = sesym;
- newse->next = senext;
- newse->rot = sesym;
- sesym->rot = newse;
- senext->rot = newsesym;
- newsesym->rot = sesymprevsym;
- sesymprev->next = newsesym;
- if (newsesym->vert->symedge == sesym) {
- newsesym->vert->symedge = newsesym;
- }
- add_list_to_input_ids(&e->input_ids, se->edge->input_ids, cdt);
- return e;
-}
-
-/**
- * Delete an edge from the structure. The new combined face on either side of
- * the deleted edge will be the one that was e's face.
- * There will be now an unused face, marked by setting its deleted flag,
- * and an unused #CDTEdge, marked by setting the next and rot pointers of
- * its SymEdges to NULL.
- * <pre>
- * . v2 .
- * / \ / \
- * /f|j\ / \
- * / | \ / \
- * |
- * A | B A
- * \ e| / \ /
- * \ | / \ /
- * \h|i/ \ /
- * . v1 .
- * </pre>
- * Also handle variant cases where one or both ends
- * are attached only to e.
- */
-static void delete_edge(CDT_state *cdt, SymEdge *e)
-{
- SymEdge *esym, *f, *h, *i, *j, *k, *jsym, *hsym;
- CDTFace *aface, *bface;
- CDTVert *v1, *v2;
- bool v1_isolated, v2_isolated;
-
- esym = sym(e);
- v1 = e->vert;
- v2 = esym->vert;
- aface = e->face;
- bface = esym->face;
- f = e->next;
- h = prev(e);
- i = esym->next;
- j = prev(esym);
- jsym = sym(j);
- hsym = sym(h);
- v1_isolated = (i == e);
- v2_isolated = (f == esym);
-
- if (!v1_isolated) {
- h->next = i;
- i->rot = hsym;
- }
- if (!v2_isolated) {
- j->next = f;
- f->rot = jsym;
- }
- if (!v1_isolated && !v2_isolated && aface != bface) {
- for (k = i; k != f; k = k->next) {
- k->face = aface;
- }
- }
-
- /* If e was representative symedge for v1 or v2, fix that. */
- if (v1_isolated) {
- v1->symedge = NULL;
- }
- else if (v1->symedge == e) {
- v1->symedge = i;
- }
- if (v2_isolated) {
- v2->symedge = NULL;
- }
- else if (v2->symedge == esym) {
- v2->symedge = f;
- }
-
- /* Mark SymEdge as deleted by setting all its pointers to NULL. */
- e->next = e->rot = NULL;
- esym->next = esym->rot = NULL;
- if (!v1_isolated && !v2_isolated && aface != bface) {
- bface->deleted = true;
- if (cdt->outer_face == bface) {
- cdt->outer_face = aface;
- }
- }
-}
-
-static CDT_state *cdt_init(const CDT_input *in)
-{
- int i;
- MemArena *arena = BLI_memarena_new(DLNY_ARENASIZE, __func__);
- CDT_state *cdt = BLI_memarena_calloc(arena, sizeof(CDT_state));
-
- cdt->epsilon = (double)in->epsilon;
- cdt->epsilon_squared = cdt->epsilon * cdt->epsilon;
- cdt->arena = arena;
- cdt->input_vert_tot = in->verts_len;
- cdt->vert_array_len_alloc = 2 * in->verts_len;
- cdt->vert_array = BLI_memarena_alloc(arena,
- cdt->vert_array_len_alloc * sizeof(*cdt->vert_array));
- cdt->listpool = BLI_mempool_create(
- sizeof(LinkNode), 128 + 4 * in->verts_len, 128 + in->verts_len, 0);
-
- for (i = 0; i < in->verts_len; i++) {
- add_cdtvert(cdt, (double)(in->vert_coords[i][0]), (double)(in->vert_coords[i][1]));
- }
- cdt->outer_face = add_cdtface(cdt);
- return cdt;
-}
-
-static void new_cdt_free(CDT_state *cdt)
-{
- BLI_mempool_destroy(cdt->listpool);
- BLI_memarena_free(cdt->arena);
-}
-
-typedef struct SiteInfo {
- CDTVert *v;
- int orig_index;
-} SiteInfo;
-
-static int site_lexicographic_cmp(const void *a, const void *b)
-{
- const SiteInfo *s1 = a;
- const SiteInfo *s2 = b;
- const double *co1 = s1->v->co;
- const double *co2 = s2->v->co;
-
- if (co1[0] < co2[0]) {
- return -1;
- }
- else if (co1[0] > co2[0]) {
- return 1;
- }
- else if (co1[1] < co2[1]) {
- return -1;
- }
- else if (co1[1] > co2[1]) {
- return 1;
- }
- else if (s1->orig_index < s2->orig_index) {
- return -1;
- }
- else if (s1->orig_index > s2->orig_index) {
- return 1;
- }
- return 0;
-}
-
-BLI_INLINE bool vert_left_of_symedge(CDTVert *v, SymEdge *se)
-{
- return orient2d(v->co, se->vert->co, se->next->vert->co) > 0.0;
-}
-
-BLI_INLINE bool vert_right_of_symedge(CDTVert *v, SymEdge *se)
-{
- return orient2d(v->co, se->next->vert->co, se->vert->co) > 0.0;
-}
-
-/* Is se above basel? */
-BLI_INLINE bool dc_tri_valid(SymEdge *se, SymEdge *basel, SymEdge *basel_sym)
-{
- return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0.0;
-}
-
-/* Delaunay triangulate sites[start} to sites[end-1].
- * Assume sites are lexicographically sorted by coordinate.
- * Return SymEdge of ccw convex hull at left-most point in *r_le
- * and that of right-most point of cw convex null in *r_re.
- */
-static void dc_tri(
- CDT_state *cdt, SiteInfo *sites, int start, int end, SymEdge **r_le, SymEdge **r_re)
-{
- int n = end - start;
- int n2;
- CDTVert *v1, *v2, *v3;
- CDTEdge *ea, *eb, *ebasel;
- SymEdge *ldo, *ldi, *rdi, *rdo, *basel, *basel_sym, *lcand, *rcand, *t;
- double orient;
- bool valid_lcand, valid_rcand;
-#ifdef DEBUG_CDT
- char label_buf[100];
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "DC_TRI start=%d end=%d\n", start, end);
- }
-#endif
-
- BLI_assert(r_le != NULL && r_re != NULL);
- if (n <= 1) {
- *r_le = NULL;
- *r_re = NULL;
- return;
- }
- if (n <= 3) {
- v1 = sites[start].v;
- v2 = sites[start + 1].v;
- ea = add_cdtedge(cdt, v1, v2, cdt->outer_face, cdt->outer_face);
- ea->symedges[0].next = &ea->symedges[1];
- ea->symedges[1].next = &ea->symedges[0];
- ea->symedges[0].rot = &ea->symedges[0];
- ea->symedges[1].rot = &ea->symedges[1];
- if (n == 2) {
- *r_le = &ea->symedges[0];
- *r_re = &ea->symedges[1];
- return;
- }
- v3 = sites[start + 2].v;
- eb = add_vert_to_symedge_edge(cdt, v3, &ea->symedges[1]);
- orient = orient2d(v1->co, v2->co, v3->co);
- if (orient > 0.0) {
- add_diagonal(cdt, &eb->symedges[0], &ea->symedges[0]);
- *r_le = &ea->symedges[0];
- *r_re = &eb->symedges[0];
- }
- else if (orient < 0.0) {
- add_diagonal(cdt, &ea->symedges[0], &eb->symedges[0]);
- *r_le = ea->symedges[0].rot;
- *r_re = eb->symedges[0].rot;
- }
- else {
- /* Collinear points. Just return a line. */
- *r_le = &ea->symedges[0];
- *r_re = &eb->symedges[0];
- }
- return;
- }
- /* Here: n >= 4. Divide and conquer. */
- n2 = n / 2;
- BLI_assert(n2 >= 2 && end - (start + n2) >= 2);
-
- /* Delaunay triangulate two halves, L and R. */
- dc_tri(cdt, sites, start, start + n2, &ldo, &ldi);
- dc_tri(cdt, sites, start + n2, end, &rdi, &rdo);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nDC_TRI merge step for start=%d, end=%d\n", start, end);
- dump_se(ldo, "ldo");
- dump_se(ldi, "ldi");
- dump_se(rdi, "rdi");
- dump_se(rdo, "rdo");
- if (dbg_level > 1) {
- sprintf(label_buf, "dc_tri(%d,%d)(%d,%d)", start, start + n2, start + n2, end);
- /* dump_cdt(cdt, label_buf); */
- cdt_draw(cdt, label_buf);
- }
- }
-#endif
-
- /* Find lower common tangent of L and R. */
- for (;;) {
- if (vert_left_of_symedge(rdi->vert, ldi)) {
- ldi = ldi->next;
- }
- else if (vert_right_of_symedge(ldi->vert, rdi)) {
- rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */
- }
- else {
- break;
- }
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "common lower tangent is between\n");
- dump_se(rdi, "rdi");
- dump_se(ldi, "ldi");
- }
-#endif
- ebasel = connect_separate_parts(cdt, sym(rdi)->next, ldi);
- basel = &ebasel->symedges[0];
- basel_sym = &ebasel->symedges[1];
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_se(basel, "basel");
- cdt_draw(cdt, "after basel made");
- }
-#endif
- if (ldi->vert == ldo->vert) {
- ldo = basel_sym;
- }
- if (rdi->vert == rdo->vert) {
- rdo = basel;
- }
-
- /* Merge loop. */
- for (;;) {
- /* Locate the first point lcand->next->vert encountered by rising bubble,
- * and delete L edges out of basel->next->vert that fail the circle test. */
- lcand = basel_sym->rot;
- rcand = basel_sym->next;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "\ntop of merge loop\n");
- dump_se(lcand, "lcand");
- dump_se(rcand, "rcand");
- dump_se(basel, "basel");
- }
-#endif
- if (dc_tri_valid(lcand, basel, basel_sym)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "found valid lcand\n");
- dump_se(lcand, " lcand");
- }
-#endif
- while (incircle(basel_sym->vert->co,
- basel->vert->co,
- lcand->next->vert->co,
- lcand->rot->next->vert->co) > 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "incircle says to remove lcand\n");
- dump_se(lcand, " lcand");
- }
-#endif
- t = lcand->rot;
- delete_edge(cdt, sym(lcand));
- lcand = t;
- }
- }
- /* Symmetrically, locate first R point to be hit and delete R edges. */
- if (dc_tri_valid(rcand, basel, basel_sym)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "found valid rcand\n");
- dump_se(rcand, " rcand");
- }
-#endif
- while (incircle(basel_sym->vert->co,
- basel->vert->co,
- rcand->next->vert->co,
- sym(rcand)->next->next->vert->co) > 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "incircle says to remove rcand\n");
- dump_se(lcand, " rcand");
- }
-#endif
- t = sym(rcand)->next;
- delete_edge(cdt, rcand);
- rcand = t;
- }
- }
- /* If both lcand and rcand are invalid, then basel is the common upper tangent. */
- valid_lcand = dc_tri_valid(lcand, basel, basel_sym);
- valid_rcand = dc_tri_valid(rcand, basel, basel_sym);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(
- stderr, "after bubbling up, valid_lcand=%d, valid_rcand=%d\n", valid_lcand, valid_rcand);
- dump_se(lcand, "lcand");
- dump_se(rcand, "rcand");
- }
-#endif
- if (!valid_lcand && !valid_rcand) {
- break;
- }
- /* The next cross edge to be connected is to either lcand->next->vert or rcand->next->vert;
- * if both are valid, choose the appropriate one using the incircle test.
- */
- if (!valid_lcand ||
- (valid_rcand &&
- incircle(lcand->next->vert->co, lcand->vert->co, rcand->vert->co, rcand->next->vert->co) >
- 0.0)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "connecting rcand\n");
- dump_se(basel_sym, " se1=basel_sym");
- dump_se(rcand->next, " se2=rcand->next");
- }
-#endif
- ebasel = add_diagonal(cdt, rcand->next, basel_sym);
- }
- else {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "connecting lcand\n");
- dump_se(sym(lcand), " se1=sym(lcand)");
- dump_se(basel_sym->next, " se2=basel_sym->next");
- }
-#endif
- ebasel = add_diagonal(cdt, basel_sym->next, sym(lcand));
- }
- basel = &ebasel->symedges[0];
- basel_sym = &ebasel->symedges[1];
- BLI_assert(basel_sym->face == cdt->outer_face);
-#ifdef DEBUG_CDT
- if (dbg_level > 2) {
- cdt_draw(cdt, "after adding new crossedge");
- // dump_cdt(cdt, "after adding new crossedge");
- }
-#endif
- }
- *r_le = ldo;
- *r_re = rdo;
- BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face);
-}
-
-/* Guibas-Stolfi Divide-and_Conquer algorithm. */
-static void dc_triangulate(CDT_state *cdt, SiteInfo *sites, int nsites)
-{
- int i, j, n;
- SymEdge *le, *re;
-
- /* Compress sites in place to eliminated verts that merge to others. */
- i = 0;
- j = 0;
- while (j < nsites) {
- /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */
- sites[i] = sites[j++];
- if (sites[i].v->merge_to_index < 0) {
- i++;
- }
- }
- n = i;
- if (n == 0) {
- return;
- }
- dc_tri(cdt, sites, 0, n, &le, &re);
-}
-
-/**
- * Do a Delaunay Triangulation of the points in cdt->vert_array.
- * This is only a first step in the Constrained Delaunay triangulation,
- * because it doesn't yet deal with the segment constraints.
- * The algorithm used is the Divide & Conquer algorithm from the
- * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision
- * and the Computation of Voronoi Diagrams" paper.
- * The data structure here is similar to but not exactly the same as
- * the quad-edge structure described in that paper.
- * The incircle and ccw tests are done using Shewchuk's exact
- * primitives (see below), so that this routine is robust.
- *
- * As a preprocessing step, we want to merge all vertices that are
- * within cdt->epsilon of each other. This is accomplished by lexicographically
- * sorting the coordinates first (which is needed anyway for the D&C algorithm).
- * The CDTVerts with merge_to_index not equal to -1 are after this regarded
- * as having been merged into the vertex with the corresponding index.
- */
-static void initial_triangulation(CDT_state *cdt)
-{
- int i, j, n;
- SiteInfo *sites;
- double *ico, *jco;
- double xend, yend, xcur;
- double epsilon = cdt->epsilon;
- double epsilon_squared = cdt->epsilon_squared;
-#ifdef SJF_WAY
- CDTEdge *e;
- CDTVert *va, *vb;
-#endif
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nINITIAL TRIANGULATION\n\n");
- }
-#endif
-
- /* First sort the vertices by lexicographic order of their
- * coordinates, breaking ties by putting earlier original-index
- * vertices first.
- */
- n = cdt->vert_array_len;
- if (n <= 1) {
- return;
- }
- sites = MEM_malloc_arrayN(n, sizeof(SiteInfo), __func__);
- for (i = 0; i < n; i++) {
- sites[i].v = cdt->vert_array[i];
- sites[i].orig_index = i;
- }
- qsort(sites, n, sizeof(SiteInfo), site_lexicographic_cmp);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "after sorting\n");
- for (i = 0; i < n; i++) {
- fprintf(stderr, "%d: orig index: %d, (%f,%f)\n", i, sites[i].orig_index, F2(sites[i].v->co));
- }
- }
-#endif
-
- /* Now dedup according to user-defined epsilon.
- * We will merge a vertex into an earlier-indexed vertex
- * that is within epsilon (Euclidean distance).
- * Merges may cascade. So we may end up merging two things
- * that are farther than epsilon by transitive merging. Oh well.
- * Assume that merges are rare, so use simple searches in the
- * lexicographic ordering - likely we will soon hit y's with
- * the same x that are farther away than epsilon, and then
- * skipping ahead to the next biggest x, are likely to soon
- * find one of those farther away than epsilon.
- */
- for (i = 0; i < n - 1; i++) {
- ico = sites[i].v->co;
- /* Start j at next place that has both x and y coords within epsilon. */
- xend = ico[0] + epsilon;
- yend = ico[1] + epsilon;
- j = i + 1;
- while (j < n) {
- jco = sites[j].v->co;
- if (jco[0] > xend) {
- break; /* No more j's to process. */
- }
- else if (jco[1] > yend) {
- /* Get past any string of v's with the same x and too-big y. */
- xcur = jco[0];
- while (++j < n) {
- if (sites[j].v->co[0] > xcur) {
- break;
- }
- }
- BLI_assert(j == n || sites[j].v->co[0] > xcur);
- if (j == n) {
- break;
- }
- jco = sites[j].v->co;
- if (jco[0] > xend || jco[1] > yend) {
- break;
- }
- }
- /* When here, vertex i and j are within epsilon by box test.
- * The Euclidean distance test is stricter, so need to do it too, now.
- */
- BLI_assert(j < n && jco[0] <= xend && jco[1] <= yend);
- if (len_squared_v2v2_db(ico, jco) <= epsilon_squared) {
- sites[j].v->merge_to_index = (sites[i].v->merge_to_index == -1) ?
- sites[i].orig_index :
- sites[i].v->merge_to_index;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "merged orig vert %d to %d\n",
- sites[j].orig_index,
- sites[j].v->merge_to_index);
- }
-#endif
- }
- j++;
- }
- }
-
- /* Now add non-dup vertices into triangulation in lexicographic order. */
-
- dc_triangulate(cdt, sites, n);
- MEM_freeN(sites);
-}
-
-/** Use LinkNode linked list as stack of SymEdges, allocating from cdt->listpool. */
-typedef LinkNode *Stack;
-
-BLI_INLINE void push(Stack *stack, SymEdge *se, CDT_state *cdt)
-{
- BLI_linklist_prepend_pool(stack, se, cdt->listpool);
-}
-
-BLI_INLINE SymEdge *pop(Stack *stack, CDT_state *cdt)
-{
- return (SymEdge *)BLI_linklist_pop_pool(stack, cdt->listpool);
-}
-
-BLI_INLINE bool is_empty(Stack *stack)
-{
- return *stack == NULL;
-}
-
-/**
- * Re-triangulates, assuring constrained delaunay condition,
- * the pseudo-polygon that cycles from se.
- * "pseudo" because a vertex may be repeated.
- * See Anglada paper, "An Improved incremental algorithm
- * for constructing restricted Delaunay triangulations".
- */
-static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se)
-{
- SymEdge *ss, *first, *cse;
- CDTVert *a, *b, *c, *v;
- CDTEdge *ebc, *eca;
- int count;
-#ifdef DEBUG_CDT
- SymEdge *last;
- const int dbg_level = 0;
-#endif
-
- if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) {
- return;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "retriangulate");
- dump_se_cycle(se, "poly ", 1000);
- }
-#endif
- /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */
- count = 1;
- for (ss = se->next; ss != se; ss = ss->next) {
- count++;
- }
- if (count <= 3) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "nothing to do\n");
- }
-#endif
- return;
- }
- /* First and last are the SymEdges whose verts are first and last off of base,
- * continuing from 'se'. */
- first = se->next->next;
- /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */
- a = se->vert;
- b = se->next->vert;
- c = first->vert;
- cse = first;
-#ifdef DEBUG_CDT
- last = prev(se);
- if (dbg_level > 1) {
- dump_se(first, "first");
- dump_se(last, "last");
- dump_v(a, "a");
- dump_v(b, "b");
- dump_v(c, "c");
- }
-#endif
- for (ss = first->next; ss != se; ss = ss->next) {
- v = ss->vert;
- if (incircle(a->co, b->co, c->co, v->co) > 0.0) {
- c = v;
- cse = ss;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_v(c, "new c ");
- }
-#endif
- }
- }
- /* Add diagonals necessary to make abc a triangle. */
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "make triangle abc exist where\n");
- dump_v(a, " a");
- dump_v(b, " b");
- dump_v(c, " c");
- }
-#endif
- ebc = NULL;
- eca = NULL;
- if (!exists_edge(b, c)) {
- ebc = add_diagonal(cdt, se->next, cse);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "added edge ebc\n");
- dump_se(&ebc->symedges[0], " ebc");
- }
-#endif
- }
- if (!exists_edge(c, a)) {
- eca = add_diagonal(cdt, cse, se);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "added edge eca\n");
- dump_se(&eca->symedges[0], " eca");
- }
-#endif
- }
- /* Now recurse. */
- if (ebc) {
- re_delaunay_triangulate(cdt, &ebc->symedges[1]);
- }
- if (eca) {
- re_delaunay_triangulate(cdt, &eca->symedges[1]);
- }
-}
-
-static double tri_orient(const SymEdge *t)
-{
- return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co);
-}
-
-/**
- * The CrossData struct gives defines either an endpoint or an intermediate point
- * in the path we will take to insert an edge constraint.
- * Each such point will either be
- * (a) a vertex or
- * (b) a fraction lambda (0 < lambda < 1) along some SymEdge.]
- *
- * In general, lambda=0 indicates case a and lambda != 0 indicates case be.
- * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing,
- * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing.
- * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL.
- *
- * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the SymEdge from
- * 'vert' that has as face the one that you go through to get to this vertex. If you go exactly
- * along an edge then we set 'in' to NULL, since it won't be needed. The first crossing will have
- * 'in' = NULL. We set 'out' to the SymEdge that has the face we go though to get to the next
- * crossing, or, if the next crossing is a case (a), then it is the edge that goes to that next
- * vertex. 'out' wlll be NULL for the last one.
- *
- * For case (b), vert will be NULL at first, and later filled in with the created split vertex,
- * and 'in' will be the SymEdge that we go through, and lambda will be between 0 and 1,
- * the fraction from in's vert to in->next's vert to put the split vertex.
- * 'out' is not needed in this case, since the attachment point will be the sym of the first
- * half of the split edge.
- */
-typedef struct CrossData {
- double lambda;
- CDTVert *vert;
- SymEdge *in;
- SymEdge *out;
-} CrossData;
-
-static bool get_next_crossing_from_vert(CDT_state *cdt,
- CrossData *cd,
- CrossData *cd_next,
- const CDTVert *v2);
-
-/**
- * As part of finding crossings, we found a case where the next crossing goes through vert v.
- * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v.
- * Else cd_out can be NULL, because it won't be used.
- * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the
- * current cd.
- */
-static void fill_crossdata_for_through_vert(CDTVert *v,
- SymEdge *cd_out,
- CrossData *cd,
- CrossData *cd_next)
-{
- SymEdge *se;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- cd_next->lambda = 0.0;
- cd_next->vert = v;
- cd_next->in = NULL;
- cd_next->out = NULL;
- if (cd->lambda == 0.0) {
- cd->out = cd_out;
- }
- else {
- /* One of the edges in the triangle with edge sym(cd->in) contains v. */
- se = sym(cd->in);
- if (se->vert != v) {
- se = se->next;
- if (se->vert != v) {
- se = se->next;
- }
- }
- BLI_assert(se->vert == v);
- cd_next->in = se;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- dump_cross_data(cd, "cd through vert, cd");
- dump_cross_data(cd_next, "cd_next through vert, cd");
- }
-#endif
-}
-
-/**
- * As part of finding crossings, we found a case where orient tests say that the next crossing
- * is on the SymEdge t, while intersecting with the ray from curco to v2.
- * Find the intersection point and fill in the CrossData for that point.
- * It may turn out that when doing the intersection, we get an answer that says that
- * this case is better handled as through-vertex case instead, so we may do that.
- * In the latter case, we want to avoid a situation where the current crossing is on an edge
- * and the next will be an endpoint of the same edge. When that happens, we "rewrite history"
- * and turn the current crossing into a vert one, and then extend from there.
- *
- * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert
- * case. We need to fill in cd's 'out' edge if it was a vert case.
- */
-static void fill_crossdata_for_intersect(CDT_state *cdt,
- const double *curco,
- const CDTVert *v2,
- SymEdge *t,
- CrossData *cd,
- CrossData *cd_next)
-{
- CDTVert *va, *vb, *vc;
- double lambda, mu, len_ab;
- SymEdge *se_vcva, *se_vcvb;
- int isect;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- va = t->vert;
- vb = t->next->vert;
- vc = t->next->next->vert;
- se_vcvb = sym(t->next);
- se_vcva = t->next->next;
- BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va);
- BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb);
- UNUSED_VARS_NDEBUG(vc);
- isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, curco, v2->co, &lambda, &mu);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- double co[2];
- fprintf(stderr, "crossdata for intersect gets lambda=%.17g, mu=%.17g\n", lambda, mu);
- fprintf(stderr,
- "isect=%s\n",
- isect == 2 ? "cross" : (isect == 1 ? "exact" : (isect == 0 ? "none" : "colinear")));
- fprintf(stderr,
- "va=v%d=(%g,%g), vb=v%d=(%g,%g), vc=v%d, curco=(%g,%g), v2=(%g,%g)\n",
- va->index,
- F2(va->co),
- vb->index,
- F2(vb->co),
- vc->index,
- F2(curco),
- F2(v2->co));
- dump_se_short(se_vcva, "vcva=");
- dump_se_short(se_vcvb, " vcvb=");
- interp_v2_v2v2_db(co, va->co, vb->co, lambda);
- fprintf(stderr, "\nco=(%.17g,%.17g)\n", F2(co));
- }
-#endif
- switch (isect) {
- case ISECT_LINE_LINE_CROSS:
- len_ab = len_v2v2_db(va->co, vb->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "len_ab=%g, near a=%g, near b=%g\n",
- len_ab,
- lambda * len_ab,
- (1.0 - lambda) * len_ab);
- }
-#endif
- if (lambda * len_ab <= cdt->epsilon) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else if ((1.0 - lambda) * len_ab <= cdt->epsilon) {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- else {
- *cd_next = (CrossData){lambda, NULL, t, NULL};
- if (cd->lambda == 0.0) {
- cd->out = se_vcva;
- }
- }
- break;
- case ISECT_LINE_LINE_EXACT:
- if (lambda == 0.0) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else if (lambda == 1.0) {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- else {
- *cd_next = (CrossData){lambda, NULL, t, NULL};
- if (cd->lambda == 0.0) {
- cd->out = se_vcva;
- }
- }
- break;
- case ISECT_LINE_LINE_NONE:
- /* It should be very near one end or other of segment. */
- if (lambda <= 0.5) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- break;
- case ISECT_LINE_LINE_COLINEAR:
- if (len_squared_v2v2_db(va->co, v2->co) <= len_squared_v2v2_db(vb->co, v2->co)) {
- fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
- }
- else {
- fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
- }
- break;
- }
-}
-
-/**
- * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
- * 'cd' represents a crossing that goes through a vertex.
- *
- * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert
- * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges.
- */
-static bool get_next_crossing_from_vert(CDT_state *cdt,
- CrossData *cd,
- CrossData *cd_next,
- const CDTVert *v2)
-{
- SymEdge *t, *tstart;
- CDTVert *vcur, *va, *vb;
- double orient1, orient2;
- bool ok;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nget_next_crossing_from_vert\n");
- dump_v(cd->vert, " cd->vert");
- }
-#endif
-
- t = tstart = cd->vert->symedge;
- vcur = cd->vert;
- ok = false;
- do {
- /*
- * The ray from vcur to v2 has to go either between two successive
- * edges around vcur or exactly along them. This time through the
- * loop, check to see if the ray goes along vcur-va
- * or between vcur-va and vcur-vb, where va is the end of t
- * and vb is the next vertex (on the next rot edge around vcur, but
- * should also be the next vert of triangle starting with vcur-va.
- */
- if (t->face != cdt->outer_face && tri_orient(t) < 0.0) {
- fprintf(stderr, "BAD TRI orientation %g\n", tri_orient(t)); /* TODO: handle this */
-#ifdef DEBUG_CDT
- dump_se_cycle(t, "top of ccw scan loop", 4);
-#endif
- }
- va = t->next->vert;
- vb = t->next->next->vert;
- orient1 = orient2d(t->vert->co, va->co, v2->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "non-final through vert case\n");
- dump_v(va, " va");
- dump_v(vb, " vb");
- fprintf(stderr, "orient1=%g\n", orient1);
- }
-#endif
- if (orient1 == 0.0 && in_line(vcur->co, va->co, v2->co)) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "ray goes through va\n");
- }
-#endif
- fill_crossdata_for_through_vert(va, t, cd, cd_next);
- ok = true;
- break;
- }
- else if (t->face != cdt->outer_face) {
- orient2 = orient2d(vcur->co, vb->co, v2->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "orient2=%g\n", orient2);
- }
-#endif
- /* Don't handle orient2 == 0.0 case here: next rotation will get it. */
- if (orient1 > 0.0 && orient2 < 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "segment intersects\n");
- }
-#endif
- t = t->next;
- fill_crossdata_for_intersect(cdt, vcur->co, v2, t, cd, cd_next);
- ok = true;
- break;
- }
- }
- } while ((t = t->rot) != tstart);
-#ifdef DEBUG_CDT
- if (!ok) {
- /* Didn't find the exit! Shouldn't happen. */
- fprintf(stderr, "shouldn't happen: can't find next crossing from vert\n");
- }
-#endif
- return ok;
-}
-
-/**
- * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
- * 'cd' represents a crossing that goes through a an edge, not at either end of that edge.
- *
- * We have the triangle vb-va-vc, where va and vb are the split edge and vc is the third vertex on
- * that new side of the edge (should be closer to v2). The next crossing should be through vc or
- * intersecting vb-vc or va-vc.
- */
-static void get_next_crossing_from_edge(CDT_state *cdt,
- CrossData *cd,
- CrossData *cd_next,
- const CDTVert *v2)
-{
- double curco[2];
- double orient;
- CDTVert *va, *vb, *vc;
- SymEdge *se_ac;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nget_next_crossing_from_edge\n");
- fprintf(stderr, " lambda=%.17g\n", cd->lambda);
- dump_se_short(cd->in, " cd->in");
- }
-#endif
-
- va = cd->in->vert;
- vb = cd->in->next->vert;
- interp_v2_v2v2_db(curco, va->co, vb->co, cd->lambda);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, " curco=(%.17g,%.17g)\n", F2(curco));
- }
-#endif
- se_ac = sym(cd->in)->next;
- vc = se_ac->next->vert;
- orient = orient2d(curco, v2->co, vc->co);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "now searching with third vertex ");
- dump_v(vc, "vc");
- fprintf(stderr, "orient2d(cur, v2, vc) = %g\n", orient);
- }
-#endif
- if (orient < 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "curco--v2 intersects vb--vc\n");
- }
-#endif
- fill_crossdata_for_intersect(cdt, curco, v2, se_ac->next, cd, cd_next);
- }
- else if (orient > 0.0) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "curco--v2 intersects va--vc\n");
- }
-#endif
- fill_crossdata_for_intersect(cdt, curco, v2, se_ac, cd, cd_next);
- }
- else {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "orient==0 case, so going through or to vc\n");
- }
-#endif
- *cd_next = (CrossData){0.0, vc, se_ac->next, NULL};
- }
-}
-
-/**
- * Add a constrained edge between v1 and v2 to cdt structure.
- * This may result in a number of #CDTEdges created, due to intersections
- * and partial overlaps with existing cdt vertices and edges.
- * Each created #CDTEdge will have input_id added to its input_ids list.
- *
- * If \a r_edges is not NULL, the #CDTEdges generated or found that go from
- * v1 to v2 are put into that linked list, in order.
- *
- * Assumes that #BLI_constrained_delaunay_get_output has not been called yet.
- */
-static void add_edge_constraint(
- CDT_state *cdt, CDTVert *v1, CDTVert *v2, int input_id, LinkNode **r_edges)
-{
- SymEdge *t, *se, *tstart, *tnext;
- int i, j, n, visit;
- bool ok;
- CrossData *crossings = NULL;
- CrossData *cd, *cd_prev, *cd_next;
- CDTVert *v;
- CDTEdge *edge;
- LinkNodePair edge_list = {NULL, NULL};
- BLI_array_staticdeclare(crossings, 128);
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nADD_EDGE_CONSTRAINT\n");
- dump_v(v1, " 1");
- dump_v(v2, " 2");
- }
-#endif
-
- if (r_edges) {
- *r_edges = NULL;
- }
-
- /*
- * Handle two special cases first:
- * 1) The two end vertices are the same (can happen because of merging).
- * 2) There is already an edge between v1 and v2.
- */
- if (v1 == v2) {
- return;
- }
- if ((t = find_symedge_between_verts(v1, v2)) != NULL) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "segment already there\n");
- }
-#endif
- add_to_input_ids(&t->edge->input_ids, input_id, cdt);
- if (r_edges != NULL) {
- BLI_linklist_append_pool(&edge_list, t->edge, cdt->listpool);
- *r_edges = edge_list.list;
- }
- return;
- }
-
- /*
- * Fill crossings array with CrossData points for intersection path from v1 to v2.
- *
- * At every point, the crossings array has the path so far, except that
- * the .out field of the last element of it may not be known yet -- if that
- * last element is a vertex, then we won't know the output edge until we
- * find the next crossing.
- *
- * To protect against infinite loops, we keep track of which vertices
- * we have visited by setting their visit_index to a new visit epoch.
- *
- * We check a special case first: where the segment is already there in
- * one hop. Saves a bunch of orient2d tests in that common case.
- */
- visit = ++cdt->visit_count;
- BLI_array_grow_one(crossings);
- crossings[0] = (CrossData){0.0, v1, NULL, NULL};
- while (!((n = BLI_array_len(crossings)) > 0 && crossings[n - 1].vert == v2)) {
- BLI_array_grow_one(crossings);
- cd = &crossings[n - 1];
- cd_next = &crossings[n];
- if (crossings[n - 1].lambda == 0.0) {
- ok = get_next_crossing_from_vert(cdt, cd, cd_next, v2);
- }
- else {
- get_next_crossing_from_edge(cdt, cd, cd_next, v2);
- }
- if (!ok || BLI_array_len(crossings) == 100000) {
- /* Shouldn't happen but if does, just bail out. */
-#ifdef DEBUG_CDT
- fprintf(stderr, "FAILURE adding segment, bailing out\n");
-#endif
- BLI_array_free(crossings);
- return;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "crossings[%d]: ", n);
- dump_cross_data(&crossings[n], "");
- }
-#endif
- if (crossings[n].lambda == 0.0) {
- if (crossings[n].vert->visit_index == visit) {
- fprintf(stderr, "WHOOPS, REVISIT. Bailing out.\n"); /*TODO: desperation search here. */
- BLI_array_free(crossings);
- return;
- }
- crossings[n].vert->visit_index = visit;
- }
- }
-
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\ncrossings found\n");
- for (i = 0; i < BLI_array_len(crossings); i++) {
- fprintf(stderr, "%d: ", i);
- dump_cross_data(&crossings[i], "");
- if (crossings[i].lambda == 0.0) {
- if (i == 0 || crossings[i - 1].lambda == 0.0) {
- BLI_assert(crossings[i].in == NULL);
- }
- else {
- BLI_assert(crossings[i].in != NULL && crossings[i].in->vert == crossings[i].vert);
- BLI_assert(crossings[i].in->face == sym(crossings[i - 1].in)->face);
- }
- if (i == BLI_array_len(crossings) - 1) {
- BLI_assert(crossings[i].vert == v2);
- BLI_assert(crossings[i].out == NULL);
- }
- else {
- BLI_assert(crossings[i].out->vert == crossings[i].vert);
- if (crossings[i + 1].lambda == 0.0) {
- BLI_assert(crossings[i].out->next->vert == crossings[i + 1].vert);
- }
- else {
- BLI_assert(crossings[i].out->face == crossings[i + 1].in->face);
- }
- }
- }
- else {
- if (i > 0 && crossings[i - 1].lambda == 0.0) {
- BLI_assert(crossings[i].in->face == crossings[i - 1].out->face);
- }
- BLI_assert(crossings[i].out == NULL);
- }
- }
- }
-#endif
-
- /*
- * Postprocess crossings.
- * Some crossings may have an intersection crossing followed
- * by a vertex crossing that is on the same edge that was just
- * intersected. We prefer to go directly from the previous
- * crossing directly to the vertex. This may chain backwards.
- *
- * This loop marks certain crossings as "deleted", by setting
- * their lambdas to -1.0.
- */
- for (i = 2; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda == 0.0) {
- v = cd->vert;
- for (j = i - 1; j > 0; j--) {
- cd_prev = &crossings[j];
- if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) ||
- (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) {
- break;
- }
- else {
- cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "deleted crossing %d\n", j);
- }
-#endif
- }
- }
- if (j < i - 1) {
- /* Some crossings were deleted. Fix the in and out edges across gap. */
- cd_prev = &crossings[j];
- if (cd_prev->lambda == 0.0) {
- se = find_symedge_between_verts(cd_prev->vert, v);
- if (se == NULL) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "FAILURE(a) in delete crossings, bailing out.\n");
-#endif
- BLI_array_free(crossings);
- return;
- }
- cd_prev->out = se;
- cd->in = NULL;
- }
- else {
- se = find_symedge_with_face(v, sym(cd_prev->in)->face);
- if (se == NULL) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "FAILURE(b) in delete crossings, bailing out.\n");
-#endif
- BLI_array_free(crossings);
- return;
- }
- cd->in = se;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "after deleting crossings:\n");
- fprintf(stderr, "cross[%d]: ", j);
- dump_cross_data(cd_prev, "");
- fprintf(stderr, "cross[%d]: ", i);
- dump_cross_data(cd, "");
- }
-#endif
- }
- }
- }
-
- /*
- * Insert all intersection points on constrained edges.
- */
- for (i = 0; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) {
- edge = split_edge(cdt, cd->in, cd->lambda);
- cd->vert = edge->symedges[0].vert;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "insert vert for crossing %d: ", i);
- dump_v(cd->vert, "inserted");
- }
-#endif
- }
- }
-
- /*
- * Remove any crossed, non-intersected edges.
- */
- for (i = 0; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) {
- delete_edge(cdt, cd->in);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "delete edge for crossing %d\n", i);
- }
-#endif
- }
- }
-
- /*
- * Insert segments for v1->v2.
- */
- tstart = crossings[0].out;
- for (i = 1; i < BLI_array_len(crossings); i++) {
- cd = &crossings[i];
- if (cd->lambda == -1.0) {
- continue; /* This crossing was deleted. */
- }
- t = tnext = NULL;
- if (cd->lambda != 0.0) {
- if (is_constrained_edge(cd->in->edge)) {
- t = cd->vert->symedge;
- tnext = sym(t)->next;
- }
- }
- else if (cd->lambda == 0.0) {
- t = cd->in;
- tnext = cd->out;
- if (t == NULL) {
- /* Previous non-deleted crossing must also have been a vert, and segment should exist. */
- for (j = i - 1; j >= 0; j--) {
- cd_prev = &crossings[j];
- if (cd_prev->lambda != -1.0) {
- break;
- }
- }
- BLI_assert(cd_prev->lambda == 0.0);
- BLI_assert(cd_prev->out->next->vert == cd->vert);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "edge to crossing %d already there\n", i);
- }
-#endif
- edge = cd_prev->out->edge;
- add_to_input_ids(&edge->input_ids, input_id, cdt);
- }
- }
- if (t != NULL) {
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "edge to crossing %d: insert diagonal between\n", i);
- dump_se(tstart, " ");
- dump_se(t, " ");
- dump_se_cycle(tstart, "tstart", 100);
- dump_se_cycle(t, "t", 100);
- }
-#endif
- if (tstart->next->vert == t->vert) {
- edge = tstart->edge;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "already there (b)\n");
- }
-#endif
- }
- else {
- edge = add_diagonal(cdt, tstart, t);
- }
- add_to_input_ids(&edge->input_ids, input_id, cdt);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "added\n");
- }
-#endif
- if (r_edges != NULL) {
- BLI_linklist_append_pool(&edge_list, edge, cdt->listpool);
- }
- /* Now retriangulate upper and lower gaps. */
- re_delaunay_triangulate(cdt, &edge->symedges[0]);
- re_delaunay_triangulate(cdt, &edge->symedges[1]);
- }
- if (i < BLI_array_len(crossings) - 1) {
- if (tnext != NULL) {
- tstart = tnext;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- fprintf(stderr, "now tstart = ");
- dump_se(tstart, "");
- }
-#endif
- }
- }
- }
-
- if (r_edges) {
- *r_edges = edge_list.list;
- }
- BLI_array_free(crossings);
-}
-
-/**
- * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that
- * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is
- * on the left of that SymEdge.
- *
- * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then
- * process all adjacent faces where the adjacency isn't across an edge that was a constraint added
- * for the boundary of the input face.
- * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face.
- *
- * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside.
- * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the
- * contiguous set of faces enclosed by parts of the boundary, leaving the other such untagged. This
- * may be a feature instead of a bug if the first contiguous section is most of the face and the
- * others are tiny self-crossing triangles at some parts of the boundary. On the other hand, if
- * decide we want to handle these in full generality, then will need a more complicated algorithm
- * (using "inside" tests and a parity rule) to decide on the interior.
- */
-static void add_face_ids(
- CDT_state *cdt, SymEdge *face_symedge, int face_id, int fedge_start, int fedge_end)
-{
- Stack stack;
- SymEdge *se, *se_start, *se_sym;
- CDTFace *face, *face_other;
- int visit;
-
- /* Can't loop forever since eventually would visit every face. */
- cdt->visit_count++;
- visit = cdt->visit_count;
- stack = NULL;
- push(&stack, face_symedge, cdt);
- while (!is_empty(&stack)) {
- se = pop(&stack, cdt);
- face = se->face;
- if (face->visit_index == visit) {
- continue;
- }
- face->visit_index = visit;
- add_to_input_ids(&face->input_ids, face_id, cdt);
- se_start = se;
- for (se = se->next; se != se_start; se = se->next) {
- if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) {
- se_sym = sym(se);
- face_other = se_sym->face;
- if (face_other->visit_index != visit) {
- push(&stack, se_sym, cdt);
- }
- }
- }
- }
-}
-
-/* Delete_edge but try not to mess up outer face.
- * Also faces have symedges now, so make sure not
- * to mess those up either. */
-static void dissolve_symedge(CDT_state *cdt, SymEdge *se)
-{
- SymEdge *symse = sym(se);
- if (symse->face == cdt->outer_face) {
- se = sym(se);
- symse = sym(se);
- }
- if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) {
- /* Advancing by 2 to get past possible 'sym(se)'. */
- if (se->next->next == se) {
- cdt->outer_face->symedge = NULL;
- }
- else {
- cdt->outer_face->symedge = se->next->next;
- }
- }
- else {
- if (se->face->symedge == se) {
- se->face->symedge = se->next;
- }
- if (symse->face->symedge == symse) {
- symse->face->symedge = symse->next;
- }
- }
- delete_edge(cdt, se);
-}
-
-/* Slow way to get face and start vertex index within face for edge id e. */
-static bool get_face_edge_id_indices(const CDT_input *in, int e, int *r_f, int *r_fi)
-{
- int f;
- int id;
-
- id = in->edges_len;
- if (e < id) {
- return false;
- }
- for (f = 0; f < in->faces_len; f++) {
- if (e < id + in->faces_len_table[f]) {
- *r_f = f;
- *r_fi = e - id;
- return true;
- }
- id += in->faces_len_table[f];
- }
- return false;
-}
-
-/* Is pt_co when snapped to segment seg1 seg2 all of:
- * a) strictly within that segment
- * b) within epsilon from original pt_co
- * c) pt_co is not within epsilon of either seg1 or seg2.
- * Return true if so, and return in *r_lambda the fraction of the way from seg1 to seg2 of the
- * snapped point.
- */
-static bool check_vert_near_segment(const double *pt_co,
- const double *seg1,
- const double *seg2,
- double epsilon_squared,
- double *r_lambda)
-{
- double lambda, snap_co[2];
-
- lambda = closest_to_line_v2_db(snap_co, pt_co, seg1, seg2);
- *r_lambda = lambda;
- if (lambda <= 0.0 || lambda >= 1.0) {
- return false;
- }
- if (len_squared_v2v2_db(pt_co, snap_co) > epsilon_squared) {
- return false;
- }
- if (len_squared_v2v2_db(pt_co, seg1) <= epsilon_squared ||
- len_squared_v2v2_db(pt_co, seg2) <= epsilon_squared) {
- return false;
- }
- return true;
-}
-
-typedef struct EdgeVertLambda {
- int e_id;
- int v_id;
- double lambda;
-} EdgeVertLambda;
-
-/* For sorting first by edge id, then by lambda, then by vert id. */
-static int evl_cmp(const void *a, const void *b)
-{
- const EdgeVertLambda *area = a;
- const EdgeVertLambda *sb = b;
-
- if (area->e_id < sb->e_id) {
- return -1;
- }
- else if (area->e_id > sb->e_id) {
- return 1;
- }
- else if (area->lambda < sb->lambda) {
- return -1;
- }
- else if (area->lambda > sb->lambda) {
- return 1;
- }
- else if (area->v_id < sb->v_id) {
- return -1;
- }
- else if (area->v_id > sb->v_id) {
- return 1;
- }
- return 0;
-}
-
-/**
- * If epsilon > 0, and input doesn't have skip_modify_input == true,
- * check input to see if any constraint edge ends (including face edges) come
- * within epsilon of another edge.
- * For all such cases, we want to split the constraint edge at the point nearest to near vertex
- * and move the vertex coordinates to be on that edge.
- * But exclude cases where they come within epsilon of either end because those will be handled
- * by vertex merging in the main triangulation algorithm.
- *
- * If any such splits are found, make a new CDT_input reflecting this change, and provide an
- * edge map to map from edge ids in the new input space to edge ids in the old input space.
- *
- * TODO: replace naive O(n^2) algorithm with kdopbvh-based one.
- */
-static const CDT_input *modify_input_for_near_edge_ends(const CDT_input *input, int **r_edge_map)
-{
- CDT_input *new_input = NULL;
- int e, eprev, e1, e2, f, fi, flen, start, i, j;
- int i_new, i_old, i_evl;
- int v11, v12, v21, v22;
- double co11[2], co12[2], co21[2], co22[2];
- double lambda;
- double eps = (double)input->epsilon;
- double eps_sq = eps * eps;
- int tot_edge_constraints, edges_len, tot_face_edges;
- int new_tot_face_edges, new_tot_con_edges;
- int delta_con_edges, delta_face_edges, cur_e_cnt;
- int *edge_map;
- int evl_len;
- EdgeVertLambda *edge_vert_lambda = NULL;
- BLI_array_staticdeclare(edge_vert_lambda, 128);
-#ifdef DEBUG_CDT
- EdgeVertLambda *evl;
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nMODIFY INPUT\n\n");
- }
-#endif
-
- *r_edge_map = NULL;
- if (input->epsilon == 0.0 || input->skip_input_modify ||
- (input->edges_len == 0 && input->faces_len == 0)) {
- return input;
- }
-
- /* Edge constraints are union of the explicitly provided edges and the implicit edges
- * that are part of the provided faces. We index constraints by have the first input->edges_len
- * ints standing for the explicit edge with the same index, and the rest being face edges in
- * the order that the faces appear and then edges within those faces, with indices offset by
- * input->edges_len.
- * Calculate tot_edge_constraints to be the sum of the two kinds of edges.
- * We first have to count the number of face edges.
- * That is the same as the number of vertices in the faces table, which
- * we can find by adding the last length to the last start.
- */
- edges_len = input->edges_len;
- tot_edge_constraints = edges_len;
- if (input->faces_len > 0) {
- tot_face_edges = input->faces_start_table[input->faces_len - 1] +
- input->faces_len_table[input->faces_len - 1];
- }
- else {
- tot_face_edges = 0;
- }
- tot_edge_constraints = edges_len + tot_face_edges;
-
- for (e1 = 0; e1 < tot_edge_constraints - 1; e1++) {
- if (e1 < edges_len) {
- v11 = input->edges[e1][0];
- v12 = input->edges[e1][1];
- }
- else {
- if (!get_face_edge_id_indices(input, e1, &f, &fi)) {
- /* Must be bad input. Will be caught later so don't need to signal here. */
- continue;
- }
- start = input->faces_start_table[f];
- flen = input->faces_len_table[f];
- v11 = input->faces[start + fi];
- v12 = input->faces[(fi == flen - 1) ? start : start + fi + 1];
- }
- for (e2 = e1 + 1; e2 < tot_edge_constraints; e2++) {
- if (e2 < edges_len) {
- v21 = input->edges[e2][0];
- v22 = input->edges[e2][1];
- }
- else {
- if (!get_face_edge_id_indices(input, e2, &f, &fi)) {
- continue;
- }
- start = input->faces_start_table[f];
- flen = input->faces_len_table[f];
- v21 = input->faces[start + fi];
- v22 = input->faces[(fi == flen - 1) ? start : start + fi + 1];
- }
- copy_v2db_v2fl(co11, input->vert_coords[v11]);
- copy_v2db_v2fl(co12, input->vert_coords[v12]);
- copy_v2db_v2fl(co21, input->vert_coords[v21]);
- copy_v2db_v2fl(co22, input->vert_coords[v22]);
- if (check_vert_near_segment(co11, co21, co22, eps_sq, &lambda)) {
-
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v11, lambda}));
- }
- if (check_vert_near_segment(co12, co21, co22, eps_sq, &lambda)) {
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v12, lambda}));
- }
- if (check_vert_near_segment(co21, co11, co12, eps_sq, &lambda)) {
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v21, lambda}));
- }
- if (check_vert_near_segment(co22, co11, co12, eps_sq, &lambda)) {
- BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v22, lambda}));
- }
- }
- }
-
- evl_len = BLI_array_len(edge_vert_lambda);
- if (evl_len > 0) {
- /* Sort to bring splits for each edge together,
- * and for each edge, to be in order of lambda. */
- qsort(edge_vert_lambda, evl_len, sizeof(EdgeVertLambda), evl_cmp);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nafter sorting\n");
- for (i = 0; i < evl_len; i++) {
- evl = &edge_vert_lambda[i];
- fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda);
- }
- }
-#endif
-
- /* Remove dups in edge_vert_lambda, where dup means that the edge is the
- * same, and the verts are either the same or will be merged by epsilon-nearness.
- */
- i = 0;
- j = 0;
- /* In loop, copy from position j to position i. */
- for (j = 0; j < evl_len;) {
- int k;
- if (i != j) {
- memmove(&edge_vert_lambda[i], &edge_vert_lambda[j], sizeof(EdgeVertLambda));
- }
- for (k = j + 1; k < evl_len; k++) {
- int vj = edge_vert_lambda[j].v_id;
- int vk = edge_vert_lambda[k].v_id;
- if (vj != vk) {
- if (len_squared_v2v2(input->vert_coords[vj], input->vert_coords[vk]) > (float)eps_sq) {
- break;
- }
- }
- }
- j = k;
- i++;
- }
-
- if (i != evl_len) {
- evl_len = i;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nduplicates eliminated\n");
- for (i = 0; i < evl_len; i++) {
- evl = &edge_vert_lambda[i];
- fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda);
- }
- }
-#endif
- }
- /* Find delta in number of constraint edges and face edges.
- * This may be overestimates of true number, due to duplicates. */
- delta_con_edges = 0;
- delta_face_edges = 0;
- cur_e_cnt = 0;
- eprev = -1;
- for (i = 0; i < evl_len; i++) {
- e = edge_vert_lambda[i].e_id;
- if (i > 0 && e > eprev) {
- /* New edge group. Previous group had cur_e_cnt split vertices.
- * That is the delta in the number of edges needed in input since
- * there will be cur_e_cnt + 1 edges replacing one edge.
- */
- if (eprev < edges_len) {
- delta_con_edges += cur_e_cnt;
- }
- else {
- delta_face_edges += cur_e_cnt;
- }
- cur_e_cnt = 1;
- ;
- }
- else {
- cur_e_cnt++;
- }
- eprev = e;
- }
- if (eprev < edges_len) {
- delta_con_edges += cur_e_cnt;
- }
- else {
- delta_face_edges += cur_e_cnt;
- }
- new_tot_con_edges = input->edges_len + delta_con_edges;
- if (input->faces_len > 0) {
- new_tot_face_edges = input->faces_start_table[input->faces_len - 1] +
- input->faces_len_table[input->faces_len - 1] + delta_face_edges;
- }
- else {
- new_tot_face_edges = 0;
- }
-
- /* Allocate new CDT_input, now we know sizes needed (perhaps overestimated a bit).
- * Caller will be responsible for freeing it and its arrays.
- */
- new_input = MEM_callocN(sizeof(CDT_input), __func__);
- new_input->epsilon = input->epsilon;
- new_input->verts_len = input->verts_len;
- new_input->vert_coords = (float(*)[2])MEM_malloc_arrayN(
- new_input->verts_len, 2 * sizeof(float), __func__);
- /* We don't do it now, but may decide to change coords of snapped verts. */
- memmove(new_input->vert_coords,
- input->vert_coords,
- (size_t)new_input->verts_len * sizeof(float) * 2);
-
- if (edges_len > 0) {
- new_input->edges_len = new_tot_con_edges;
- new_input->edges = (int(*)[2])MEM_malloc_arrayN(
- new_tot_con_edges, 2 * sizeof(int), __func__);
- }
-
- if (input->faces_len > 0) {
- new_input->faces_len = input->faces_len;
- new_input->faces_start_table = (int *)MEM_malloc_arrayN(
- new_input->faces_len, sizeof(int), __func__);
- new_input->faces_len_table = (int *)MEM_malloc_arrayN(
- new_input->faces_len, sizeof(int), __func__);
- new_input->faces = (int *)MEM_malloc_arrayN(new_tot_face_edges, sizeof(int), __func__);
- }
-
- edge_map = (int *)MEM_malloc_arrayN(
- new_tot_con_edges + new_tot_face_edges, sizeof(int), __func__);
- *r_edge_map = edge_map;
-
- i_new = i_old = i_evl = 0;
- e = edge_vert_lambda[0].e_id;
- /* First do new constraint edges. */
- for (i_old = 0; i_old < edges_len; i_old++) {
- if (i_old < e) {
- /* Edge for i_old not split; copy it into new_input. */
- new_input->edges[i_new][0] = input->edges[i_old][0];
- new_input->edges[i_new][1] = input->edges[i_old][1];
- edge_map[i_new] = i_old;
- i_new++;
- }
- else {
- /* Edge for i_old is split. */
- BLI_assert(i_old == e);
- new_input->edges[i_new][0] = input->edges[i_old][0];
- new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id;
- edge_map[i_new] = i_old;
- i_new++;
- i_evl++;
- while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) {
- new_input->edges[i_new][0] = new_input->edges[i_new - 1][1];
- new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id;
- edge_map[i_new] = i_old;
- i_new++;
- i_evl++;
- }
- new_input->edges[i_new][0] = new_input->edges[i_new - 1][1];
- new_input->edges[i_new][1] = input->edges[i_old][1];
- edge_map[i_new] = i_old;
- i_new++;
- if (i_evl < evl_len) {
- e = edge_vert_lambda[i_evl].e_id;
- }
- else {
- e = INT_MAX;
- }
- }
- }
- BLI_assert(i_new <= new_tot_con_edges);
- new_input->edges_len = i_new;
-
- /* Now do face constraints. */
- if (input->faces_len > 0) {
- f = 0;
- i_new = 0; /* Now will index cur place in new_input->faces. */
- while (i_old < tot_edge_constraints) {
- flen = input->faces_len_table[f];
- BLI_assert(i_old - edges_len == input->faces_start_table[f]);
- new_input->faces_start_table[f] = i_new;
- if (i_old + flen - 1 < e) {
- /* Face f is not split. */
- for (j = 0; j < flen; j++) {
- new_input->faces[i_new] = input->faces[i_old - edges_len + j];
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- }
- i_old += flen;
- new_input->faces_len_table[f] = flen;
- f++;
- }
- else {
- /* Face f has at least one split edge. */
- int i_new_start = i_new;
- for (j = 0; j < flen; j++) {
- if (i_old + j < e) {
- /* jth edge of f is not split. */
- new_input->faces[i_new] = input->faces[i_old - edges_len + j];
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- }
- else {
- /* jth edge of f is split. */
- BLI_assert(i_old + j == e);
- new_input->faces[i_new] = input->faces[i_old - edges_len + j];
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) {
- new_input->faces[i_new] = edge_vert_lambda[i_evl].v_id;
- edge_map[i_new + new_input->edges_len] = i_old + j;
- i_new++;
- i_evl++;
- }
- if (i_evl < evl_len) {
- e = edge_vert_lambda[i_evl].e_id;
- }
- else {
- e = INT_MAX;
- }
- }
- }
- new_input->faces_len_table[f] = i_new - i_new_start;
- i_old += flen;
- f++;
- }
- }
- }
-
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\nnew constraint edges\n");
- for (i = 0; i < new_input->edges_len; i++) {
- fprintf(stderr, " e%d: (%d,%d)\n", i, new_input->edges[i][0], new_input->edges[i][1]);
- }
- fprintf(stderr, "\nnew faces\n");
- for (f = 0; f < new_input->faces_len; f++) {
- flen = new_input->faces_len_table[f];
- start = new_input->faces_start_table[f];
- fprintf(stderr, " f%d: start=%d, len=%d\n ", f, start, flen);
- for (i = start; i < start + flen; i++) {
- fprintf(stderr, "%d ", new_input->faces[i]);
- }
- fprintf(stderr, "\n");
- }
- fprintf(stderr, "\nedge map (new->old)\n");
- for (i = 0; i < new_tot_con_edges + new_tot_face_edges; i++) {
- fprintf(stderr, " %d->%d\n", i, edge_map[i]);
- }
- }
-#endif
- }
-
- BLI_array_free(edge_vert_lambda);
- if (new_input != NULL) {
- return (const CDT_input *)new_input;
- }
- else {
- return input;
- }
-}
-
-static void free_modified_input(CDT_input *input)
-{
- MEM_freeN(input->vert_coords);
- if (input->edges != NULL) {
- MEM_freeN(input->edges);
- }
- if (input->faces != NULL) {
- MEM_freeN(input->faces);
- MEM_freeN(input->faces_len_table);
- MEM_freeN(input->faces_start_table);
- }
- MEM_freeN(input);
-}
-
-/* Return true if we can merge se's vert into se->next's vert
- * without making the area of any new triangle formed by doing
- * that into a zero or negative area triangle.*/
-static bool can_collapse(const SymEdge *se)
-{
- SymEdge *loop_se;
- const double *co = se->next->vert->co;
-
- for (loop_se = se->rot; loop_se != se && loop_se->rot != se; loop_se = loop_se->rot) {
- if (orient2d(co, loop_se->next->vert->co, loop_se->rot->next->vert->co) <= 0.0) {
- return false;
- }
- }
- return true;
-}
-
-/*
- * Merge one end of e onto the other, fixing up surrounding faces.
- *
- * General situation looks something like:
- *
- * c-----e
- * / \ / \
- * / \ / \
- * a------b-----f
- * \ / \ /
- * \ / \ /
- * d-----g
- *
- * where ab is the tiny edge. We want to merge a and b and delete edge ab.
- * We don't want to change the coordinates of input vertices [We could revisit this
- * in the future, as API def doesn't prohibit this, but callers will appreciate if they
- * don't change.]
- * Sometimes the collapse shouldn't happen because the triangles formed by the changed
- * edges may end up with zero or negative area (see can_collapse, above).
- * So don't choose a collapse direction that is not allowed or one that has an original vertex
- * as origin and a non-original vertex as destination.
- * If both collapse directions are allowed by that rule, pick the one with the lower original
- * index.
- *
- * After merging, the faces abc and adb disappear (if they are not the outer face).
- * Suppose we merge b onto a.
- * Then edges cb and db are deleted. Face cbe becomes cae and face bdg becomes adg.
- * Any other faces attached to b now have a in their place.
- * We can do this by rotating edges round b, replacing their vert references with a.
- * Similar statements can be made about what happens when a merges into b;
- * in code below we'll swap a and b to make above lettering work for a b->a merge.
- * Return the vert at the collapsed edge, if a collapse happens.
- */
-static CDTVert *collapse_tiny_edge(CDT_state *cdt, CDTEdge *e)
-{
- CDTVert *va, *vb;
- SymEdge *ab_se, *ba_se, *bd_se, *bc_se, *ad_se, *ac_se;
- SymEdge *bg_se, *be_se, *se, *gb_se, *ca_se;
- bool can_collapse_a_to_b, can_collapse_b_to_a;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- ab_se = &e->symedges[0];
- ba_se = &e->symedges[1];
- va = ab_se->vert;
- vb = ba_se->vert;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "\ncollapse_tiny_edge\n");
- dump_se(&e->symedges[0], "tiny edge");
- fprintf(stderr, "a = [%d], b = [%d]\n", va->index, vb->index);
- validate_cdt(cdt, true, false, true);
- }
-#endif
- can_collapse_a_to_b = can_collapse(ab_se);
- can_collapse_b_to_a = can_collapse(ba_se);
- /* Now swap a and b if necessary and possible, so that from this point on we are collapsing b to
- * a. */
- if (va->index > vb->index || !can_collapse_b_to_a) {
- if (can_collapse_a_to_b && !(is_original_vert(va, cdt) && !is_original_vert(vb, cdt))) {
- SWAP(CDTVert *, va, vb);
- ab_se = &e->symedges[1];
- ba_se = &e->symedges[0];
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "swapped a and b\n");
- }
-#endif
- }
- else {
- /* Neither collapse direction is OK. */
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "neither collapse direction ok\n");
- }
-#endif
- return NULL;
- }
- }
- bc_se = ab_se->next;
- bd_se = ba_se->rot;
- if (bd_se == ba_se) {
- /* Shouldn't happen. Wire edge in outer face. */
- fprintf(stderr, "unexpected wire edge\n");
- return NULL;
- }
- vb->merge_to_index = va->merge_to_index == -1 ? va->index : va->merge_to_index;
- vb->symedge = NULL;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "vb = v[%d] merges to va = v[%d], vb->merge_to_index=%d\n",
- vb->index,
- va->index,
- vb->merge_to_index);
- }
-#endif
- /* First fix the vertex of intermediate triangles, like bgf. */
- for (se = bd_se->rot; se != bc_se; se = se->rot) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- dump_se(se, "intermediate tri edge, setting vert to va");
- }
-#endif
- se->vert = va;
- }
- ad_se = sym(sym(bd_se)->rot);
- ca_se = bc_se->next;
- ac_se = sym(ca_se);
- if (bd_se->rot != bc_se) {
- bg_se = bd_se->rot;
- be_se = sym(bc_se)->next;
- gb_se = sym(bg_se);
- }
- else {
- bg_se = NULL;
- be_se = NULL;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "delete bd, inputs to ad\n");
- dump_se(bd_se, " bd");
- dump_se(ad_se, " ad");
- fprintf(stderr, "delete bc, inputs to ac\n");
- dump_se(bc_se, " bc");
- dump_se(ac_se, " ac");
- fprintf(stderr, "delete ab\n");
- dump_se(ab_se, " ab");
- if (bg_se != NULL) {
- fprintf(stderr, "fix up bg, be\n");
- dump_se(bg_se, " bg");
- dump_se(be_se, " be");
- }
- }
-#endif
- add_list_to_input_ids(&ad_se->edge->input_ids, bd_se->edge->input_ids, cdt);
- delete_edge(cdt, bd_se);
- add_list_to_input_ids(&ac_se->edge->input_ids, bc_se->edge->input_ids, cdt);
- delete_edge(cdt, sym(bc_se));
- /* At this point we have this:
- *
- * c-----e
- * / / \
- * / / \
- * a------b-----f
- * \ \ /
- * \ \ /
- * d-----g
- *
- * Or, if there is not bg_se and be_se, like this:
- *
- * c
- * /
- * /
- * a------b
- * \
- * \
- * d
- *
- * (But we've already changed the vert field for bg, bf, ..., be to be va.)
- */
- if (bg_se != NULL) {
- gb_se->next = ad_se;
- ad_se->rot = bg_se;
- ca_se->next = be_se;
- be_se->rot = ac_se;
- bg_se->vert = va;
- be_se->vert = va;
- }
- else {
- ca_se->next = ad_se;
- ad_se->rot = ac_se;
- }
- /* Don't use delete_edge as it changes too much. */
- ab_se->next = ab_se->rot = NULL;
- ba_se->next = ba_se->rot = NULL;
- if (va->symedge == ab_se) {
- va->symedge = ac_se;
- }
- return va;
-}
-
-/*
- * Check to see if e is tiny (length <= epsilon) and queue it if so.
- */
-static void maybe_enqueue_small_feature(CDT_state *cdt, CDTEdge *e, LinkNodePair *tiny_edge_queue)
-{
- SymEdge *se, *sesym;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nmaybe_enqueue_small_features\n");
- dump_se(&e->symedges[0], " se0");
- }
-#endif
-
- if (is_deleted_edge(e) || e->in_queue) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "returning because of e conditions\n");
- }
-#endif
- return;
- }
- se = &e->symedges[0];
- sesym = &e->symedges[1];
- if (len_squared_v2v2_db(se->vert->co, sesym->vert->co) <= cdt->epsilon_squared) {
- BLI_linklist_append_pool(tiny_edge_queue, e, cdt->listpool);
- e->in_queue = true;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "Queue tiny edge\n");
- }
-#endif
- }
-}
-
-/* Consider all edges in rot ring around v for possible enqueing as small features .*/
-static void maybe_enqueue_small_features(CDT_state *cdt, CDTVert *v, LinkNodePair *tiny_edge_queue)
-{
- SymEdge *se, *se_start;
-
- se = se_start = v->symedge;
- if (!se_start) {
- return;
- }
- do {
- maybe_enqueue_small_feature(cdt, se->edge, tiny_edge_queue);
- } while ((se = se->rot) != se_start);
-}
-
-/* Collapse small edges (length <= epsilon) until no more exist.
- */
-static void remove_small_features(CDT_state *cdt)
-{
- double epsilon = cdt->epsilon;
- LinkNodePair tiny_edge_queue = {NULL, NULL};
- BLI_mempool *pool = cdt->listpool;
- LinkNode *ln;
- CDTEdge *e;
- CDTVert *v_change;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nREMOVE_SMALL_FEATURES, epsilon=%g\n", epsilon);
- }
-#endif
-
- if (epsilon == 0.0) {
- return;
- }
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- maybe_enqueue_small_feature(cdt, e, &tiny_edge_queue);
- }
-
- while (tiny_edge_queue.list != NULL) {
- e = (CDTEdge *)BLI_linklist_pop_pool(&tiny_edge_queue.list, pool);
- if (tiny_edge_queue.list == NULL) {
- tiny_edge_queue.last_node = NULL;
- }
- e->in_queue = false;
- if (is_deleted_edge(e)) {
- continue;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "collapse tiny edge\n");
- dump_se(&e->symedges[0], "");
- }
-#endif
- v_change = collapse_tiny_edge(cdt, e);
- if (v_change) {
- maybe_enqueue_small_features(cdt, v_change, &tiny_edge_queue);
- }
- }
-}
-
-/* Remove all non-constraint edges. */
-static void remove_non_constraint_edges(CDT_state *cdt)
-{
- LinkNode *ln;
- CDTEdge *e;
- SymEdge *se;
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- se = &e->symedges[0];
- if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
- dissolve_symedge(cdt, se);
- }
- }
-}
-
-/*
- * Remove the non-constraint edges, but leave enough of them so that all of the
- * faces that would be bmesh faces (that is, the faces that have some input representative)
- * are valid: they can't have holes, they can't have repeated vertices, and they can't have
- * repeated edges.
- *
- * Not essential, but to make the result look more aesthetically nice,
- * remove the edges in order of decreasing length, so that it is more likely that the
- * final remaining support edges are short, and therefore likely to make a fairly
- * direct path from an outer face to an inner hole face.
- */
-
-/* For sorting edges by decreasing length (squared). */
-struct EdgeToSort {
- double len_squared;
- CDTEdge *e;
-};
-
-static int edge_to_sort_cmp(const void *a, const void *b)
-{
- const struct EdgeToSort *e1 = a;
- const struct EdgeToSort *e2 = b;
-
- if (e1->len_squared > e2->len_squared) {
- return -1;
- }
- else if (e1->len_squared < e2->len_squared) {
- return 1;
- }
- return 0;
-}
-
-static void remove_non_constraint_edges_leave_valid_bmesh(CDT_state *cdt)
-{
- LinkNode *ln;
- CDTEdge *e;
- SymEdge *se, *se2;
- CDTFace *fleft, *fright;
- bool dissolve;
- size_t nedges;
- int i, ndissolvable;
- const double *co1, *co2;
- struct EdgeToSort *sorted_edges;
-
- nedges = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- nedges++;
- }
- if (nedges == 0) {
- return;
- }
- sorted_edges = BLI_memarena_alloc(cdt->arena, nedges * sizeof(*sorted_edges));
- i = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
- sorted_edges[i].e = e;
- co1 = e->symedges[0].vert->co;
- co2 = e->symedges[1].vert->co;
- sorted_edges[i].len_squared = len_squared_v2v2_db(co1, co2);
- i++;
- }
- }
- ndissolvable = i;
- qsort(sorted_edges, ndissolvable, sizeof(*sorted_edges), edge_to_sort_cmp);
- for (i = 0; i < ndissolvable; i++) {
- e = sorted_edges[i].e;
- se = &e->symedges[0];
- dissolve = true;
- if (true /*!edge_touches_frame(e)*/) {
- fleft = se->face;
- fright = sym(se)->face;
- if (fleft != cdt->outer_face && fright != cdt->outer_face &&
- (fleft->input_ids != NULL || fright->input_ids != NULL)) {
- /* Is there another symedge with same left and right faces?
- * Or is there a vertex not part of e touching the same left and right faces? */
- for (se2 = se->next; dissolve && se2 != se; se2 = se2->next) {
- if (sym(se2)->face == fright ||
- (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) {
- dissolve = false;
- }
- }
- }
- }
- if (dissolve) {
- dissolve_symedge(cdt, se);
- }
- }
-}
-
-static void remove_outer_edges_until_constraints(CDT_state *cdt)
-{
- LinkNode *fstack = NULL;
- SymEdge *se, *se_start;
- CDTFace *f, *fsym;
- int visit = ++cdt->visit_count;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "remove_outer_edges_until_constraints\n");
- }
-#endif
-
- cdt->outer_face->visit_index = visit;
- /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */
- se_start = se = cdt->outer_face->symedge;
- do {
- if (!is_constrained_edge(se->edge)) {
- fsym = sym(se)->face;
- if (fsym->visit_index != visit) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "pushing f=%p from symedge ", fsym);
- dump_se(se, "an outer edge");
- }
-#endif
- BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool);
- }
- }
- } while ((se = se->next) != se_start);
-
- while (fstack != NULL) {
- LinkNode *to_dissolve = NULL;
- bool dissolvable;
- f = (CDTFace *)BLI_linklist_pop_pool(&fstack, cdt->listpool);
- if (f->visit_index == visit) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "skipping f=%p, already visited\n", f);
- }
-#endif
- continue;
- }
- BLI_assert(f != cdt->outer_face);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "top of loop, f=%p\n", f);
- dump_se_cycle(f->symedge, "visit", 10000);
- if (dbg_level > 1) {
- dump_cdt(cdt, "cdt at top of loop");
- cdt_draw(cdt, "top of dissolve loop");
- }
- }
-#endif
- f->visit_index = visit;
- se_start = se = f->symedge;
- do {
- dissolvable = !is_constrained_edge(se->edge);
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_se(se, "edge in f");
- fprintf(stderr, " dissolvable=%d\n", dissolvable);
- }
-#endif
- if (dissolvable) {
- fsym = sym(se)->face;
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_se_cycle(fsym->symedge, "fsym", 10000);
- fprintf(stderr, " visited=%d\n", fsym->visit_index == visit);
- }
-#endif
- if (fsym->visit_index != visit) {
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "pushing face %p\n", fsym);
- dump_se_cycle(fsym->symedge, "pushed", 10000);
- }
-#endif
- BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool);
- }
- else {
- BLI_linklist_prepend_pool(&to_dissolve, se, cdt->listpool);
- }
- }
- se = se->next;
- } while (se != se_start);
- while (to_dissolve != NULL) {
- se = (SymEdge *)BLI_linklist_pop_pool(&to_dissolve, cdt->listpool);
- if (se->next != NULL) {
- dissolve_symedge(cdt, se);
- }
- }
- }
-}
-
-/**
- * Remove edges and merge faces to get desired output, as per options.
- * \note the cdt cannot be further changed after this.
- */
-static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_type)
-{
- CDTFace *f;
- CDTEdge *e;
- LinkNode *ln;
-
- cdt->output_prepared = true;
- if (cdt->edges == NULL) {
- return;
- }
-
- /* Make sure all non-deleted faces have a symedge. */
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (!is_deleted_edge(e)) {
- if (e->symedges[0].face->symedge == NULL) {
- e->symedges[0].face->symedge = &e->symedges[0];
- }
- if (e->symedges[1].face->symedge == NULL) {
- e->symedges[1].face->symedge = &e->symedges[1];
- }
- }
- }
-#ifdef DEBUG_CDT
- /* All non-deleted faces should have a symedge now. */
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- if (!f->deleted) {
- BLI_assert(f->symedge != NULL);
- }
- }
-#else
- UNUSED_VARS(f);
-#endif
-
- if (output_type == CDT_CONSTRAINTS) {
- remove_non_constraint_edges(cdt);
- }
- else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) {
- remove_non_constraint_edges_leave_valid_bmesh(cdt);
- }
- else if (output_type == CDT_INSIDE) {
- remove_outer_edges_until_constraints(cdt);
- }
-}
-
-static CDT_result *cdt_get_output(CDT_state *cdt,
- const CDT_input *input,
- const CDT_output_type output_type)
-{
- int i, j, nv, ne, nf, faces_len_total;
- int orig_map_size, orig_map_index;
- int *vert_to_output_map;
- CDT_result *result;
- CDTVert *v;
- LinkNode *lne, *lnf, *ln;
- SymEdge *se, *se_start;
- CDTEdge *e;
- CDTFace *f;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- fprintf(stderr, "\nCDT_GET_OUTPUT\n\n");
- }
-#endif
-
- prepare_cdt_for_output(cdt, output_type);
-
- result = (CDT_result *)MEM_callocN(sizeof(*result), __func__);
- if (cdt->vert_array_len == 0) {
- return result;
- }
-
-#ifdef DEBUG_CDT
- if (dbg_level > 1) {
- dump_cdt(cdt, "cdt to output");
- }
-#endif
-
- /* All verts without a merge_to_index will be output.
- * vert_to_output_map[i] will hold the output vertex index
- * corresponding to the vert in position i in cdt->vert_array.
- * Since merging picked the leftmost-lowermost representative,
- * that is not necessarily the same as the vertex with the lowest original
- * index (i.e., index in cdt->vert_array), so we need two passes:
- * one to get the non-merged-to vertices in vert_to_output_map,
- * and a second to put in the merge targets for merged-to vertices.
- */
- vert_to_output_map = BLI_memarena_alloc(cdt->arena, (size_t)cdt->vert_array_len * sizeof(int *));
- nv = 0;
- for (i = 0; i < cdt->vert_array_len; i++) {
- v = cdt->vert_array[i];
- if (v->merge_to_index == -1) {
- vert_to_output_map[i] = nv;
- nv++;
- }
- }
- if (nv <= 0) {
- return result;
- }
- if (nv < cdt->vert_array_len) {
- for (i = 0; i < input->verts_len; i++) {
- v = cdt->vert_array[i];
- if (v->merge_to_index != -1) {
- add_to_input_ids(&cdt->vert_array[v->merge_to_index]->input_ids, i, cdt);
- vert_to_output_map[i] = vert_to_output_map[v->merge_to_index];
- }
- }
- }
-
- result->verts_len = nv;
- result->vert_coords = MEM_malloc_arrayN(nv, sizeof(result->vert_coords[0]), __func__);
-
- /* Make the vertex "orig" map arrays, mapping output verts to lists of input ones. */
- orig_map_size = 0;
- for (i = 0; i < cdt->vert_array_len; i++) {
- if (cdt->vert_array[i]->merge_to_index == -1) {
- orig_map_size += 1 + BLI_linklist_count(cdt->vert_array[i]->input_ids);
- }
- }
- result->verts_orig_len_table = MEM_malloc_arrayN(nv, sizeof(int), __func__);
- result->verts_orig_start_table = MEM_malloc_arrayN(nv, sizeof(int), __func__);
- result->verts_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__);
-
- orig_map_index = 0;
- i = 0;
- for (j = 0; j < cdt->vert_array_len; j++) {
- v = cdt->vert_array[j];
- if (v->merge_to_index == -1) {
- result->vert_coords[i][0] = (float)v->co[0];
- result->vert_coords[i][1] = (float)v->co[1];
- result->verts_orig_start_table[i] = orig_map_index;
- if (j < input->verts_len) {
- result->verts_orig[orig_map_index++] = j;
- }
- for (ln = v->input_ids; ln; ln = ln->next) {
- result->verts_orig[orig_map_index++] = POINTER_AS_INT(ln->link);
- }
- result->verts_orig_len_table[i] = orig_map_index - result->verts_orig_start_table[i];
- i++;
- }
- }
-
- ne = 0;
- orig_map_size = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (!is_deleted_edge(e)) {
- ne++;
- if (e->input_ids) {
- orig_map_size += BLI_linklist_count(e->input_ids);
- }
- }
- }
- if (ne != 0) {
- result->edges_len = ne;
- result->face_edge_offset = cdt->face_edge_offset;
- result->edges = MEM_malloc_arrayN(ne, sizeof(result->edges[0]), __func__);
- result->edges_orig_len_table = MEM_malloc_arrayN(ne, sizeof(int), __func__);
- result->edges_orig_start_table = MEM_malloc_arrayN(ne, sizeof(int), __func__);
- if (orig_map_size > 0) {
- result->edges_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__);
- }
- orig_map_index = 0;
- i = 0;
- for (lne = cdt->edges; lne; lne = lne->next) {
- e = (CDTEdge *)lne->link;
- if (!is_deleted_edge(e)) {
- result->edges[i][0] = vert_to_output_map[e->symedges[0].vert->index];
- result->edges[i][1] = vert_to_output_map[e->symedges[1].vert->index];
- result->edges_orig_start_table[i] = orig_map_index;
- for (ln = e->input_ids; ln; ln = ln->next) {
- result->edges_orig[orig_map_index++] = POINTER_AS_INT(ln->link);
- }
- result->edges_orig_len_table[i] = orig_map_index - result->edges_orig_start_table[i];
- i++;
- }
- }
- }
-
- nf = 0;
- faces_len_total = 0;
- orig_map_size = 0;
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- if (!f->deleted && f != cdt->outer_face) {
- nf++;
- se = se_start = f->symedge;
- BLI_assert(se != NULL);
- do {
- faces_len_total++;
- se = se->next;
- } while (se != se_start);
- if (f->input_ids) {
- orig_map_size += BLI_linklist_count(f->input_ids);
- }
- }
- }
-
- if (nf != 0) {
- result->faces_len = nf;
- result->faces_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- result->faces_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- result->faces = MEM_malloc_arrayN(faces_len_total, sizeof(int), __func__);
- result->faces_orig_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- result->faces_orig_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__);
- if (orig_map_size > 0) {
- result->faces_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__);
- }
- orig_map_index = 0;
- i = 0;
- j = 0;
- for (lnf = cdt->faces; lnf; lnf = lnf->next) {
- f = (CDTFace *)lnf->link;
- if (!f->deleted && f != cdt->outer_face) {
- result->faces_start_table[i] = j;
- se = se_start = f->symedge;
- do {
- result->faces[j++] = vert_to_output_map[se->vert->index];
- se = se->next;
- } while (se != se_start);
- result->faces_len_table[i] = j - result->faces_start_table[i];
- result->faces_orig_start_table[i] = orig_map_index;
- for (ln = f->input_ids; ln; ln = ln->next) {
- result->faces_orig[orig_map_index++] = POINTER_AS_INT(ln->link);
- }
- result->faces_orig_len_table[i] = orig_map_index - result->faces_orig_start_table[i];
- i++;
- }
- }
- }
- return result;
-}
-
-/**
- * Calculate the Constrained Delaunay Triangulation of the 2d elements given in \a input.
- *
- * A Delaunay triangulation of a set of vertices is a triangulation where no triangle in the
- * triangulation has a circumcircle that strictly contains another vertex. Delaunay triangulations
- * are avoid long skinny triangles: they maximize the minimum angle of all triangles in the
- * triangulation.
- *
- * A Constrained Delaunay Triangulation adds the requirement that user-provided line segments must
- * appear as edges in the output (perhaps divided into several sub-segments). It is not required
- * that the input edges be non-intersecting: this routine will calculate the intersections. This
- * means that besides triangulating, this routine is also useful for general and robust 2d edge and
- * face intersection.
- *
- * This routine also takes an epsilon parameter in the \a input. Input vertices closer than epsilon
- * will be merged, and we collapse tiny edges (less than epsilon length).
- *
- * The current initial Deluanay triangulation algorithm is the Guibas-Stolfi Divide and Conquer
- * algorithm (see "Primitives for the Manipulation of General Subdivisions and the Computation of
- * Voronoi Diagrams"). and uses Shewchuk's exact predicates to issues where numeric errors cause
- * inconsistent geometric judgments. This is followed by inserting edge constraints (including the
- * edges implied by faces) using the algorithms discussed in "Fully Dynamic Constrained Delaunay
- * Triangulations" by Kallmann, Bieri, and Thalmann.
- *
- * \param input: points to a CDT_input struct which contains the vertices, edges, and faces to be
- * triangulated. \param output_type: specifies which edges to remove after doing the triangulation.
- * \return A pointer to an allocated CDT_result struct, which describes the triangulation in terms
- * of vertices, edges, and faces, and also has tables to map output elements back to input
- * elements. The caller must use BLI_delaunay_2d_cdt_free() on the result when done with it.
- *
- * See the header file BLI_delaunay_2d.h for details of the CDT_input and CDT_result structs and
- * the CDT_output_type enum.
- */
-CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type)
-{
- int nv = input->verts_len;
- int ne = input->edges_len;
- int nf = input->faces_len;
- int i, iv1, iv2, f, fedge_start, fedge_end, ei;
- CDT_state *cdt;
- CDTVert *v1, *v2;
- CDTEdge *face_edge;
- SymEdge *face_symedge;
- LinkNode *edge_list;
- CDT_result *result;
- const CDT_input *input_orig;
- int *new_edge_map;
- static bool called_exactinit = false;
-#ifdef DEBUG_CDT
- int dbg_level = 0;
-#endif
-
- /* The exact orientation and incircle primitives need a one-time initialization of certain
- * constants. */
- if (!called_exactinit) {
- exactinit();
- called_exactinit = true;
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr,
- "\n\nCDT CALC, nv=%d, ne=%d, nf=%d, eps=%g\n",
- input->verts_len,
- input->edges_len,
- input->faces_len,
- input->epsilon);
- }
- if (dbg_level == -1) {
- write_cdt_input_to_file(input);
- }
-#endif
-
- if ((nv > 0 && input->vert_coords == NULL) || (ne > 0 && input->edges == NULL) ||
- (nf > 0 && (input->faces == NULL || input->faces_start_table == NULL ||
- input->faces_len_table == NULL))) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "invalid input: unexpected NULL array(s)\n");
-#endif
- return NULL;
- }
-
- input_orig = input;
- input = modify_input_for_near_edge_ends(input, &new_edge_map);
- if (input != input_orig) {
- nv = input->verts_len;
- ne = input->edges_len;
- nf = input->faces_len;
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- fprintf(stderr, "input modified for near ends; now ne=%d\n", ne);
- }
-#endif
- }
- cdt = cdt_init(input);
- initial_triangulation(cdt);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- validate_cdt(cdt, true, false, false);
- if (dbg_level > 1) {
- cdt_draw(cdt, "after initial triangulation");
- }
- }
-#endif
-
- for (i = 0; i < ne; i++) {
- iv1 = input->edges[i][0];
- iv2 = input->edges[i][1];
- if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "edge indices for e%d not valid: v1=%d, v2=%d, nv=%d\n", i, iv1, iv2, nv);
-#endif
- continue;
- }
- v1 = cdt->vert_array[iv1];
- v2 = cdt->vert_array[iv2];
- if (v1->merge_to_index != -1) {
- v1 = cdt->vert_array[v1->merge_to_index];
- }
- if (v2->merge_to_index != -1) {
- v2 = cdt->vert_array[v2->merge_to_index];
- }
- if (new_edge_map) {
- ei = new_edge_map[i];
- }
- else {
- ei = i;
- }
- add_edge_constraint(cdt, v1, v2, ei, NULL);
-#ifdef DEBUG_CDT
- if (dbg_level > 3) {
- char namebuf[60];
- sprintf(namebuf, "after edge constraint %d = (%d,%d)\n", i, iv1, iv2);
- cdt_draw(cdt, namebuf);
- // dump_cdt(cdt, namebuf);
- validate_cdt(cdt, true, true, false);
- }
-#endif
- }
-
- cdt->face_edge_offset = ne;
- for (f = 0; f < nf; f++) {
- int flen = input->faces_len_table[f];
- int fstart = input->faces_start_table[f];
- if (flen <= 2) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "face %d has length %d; ignored\n", f, flen);
-#endif
- continue;
- }
- for (i = 0; i < flen; i++) {
- int face_edge_id = cdt->face_edge_offset + fstart + i;
- if (new_edge_map) {
- face_edge_id = new_edge_map[face_edge_id];
- }
- iv1 = input->faces[fstart + i];
- iv2 = input->faces[fstart + ((i + 1) % flen)];
- if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
-#ifdef DEBUG_CDT
- fprintf(stderr, "face indices not valid: f=%d, iv1=%d, iv2=%d, nv=%d\n", f, iv1, iv2, nv);
-#endif
- continue;
- }
- v1 = cdt->vert_array[iv1];
- v2 = cdt->vert_array[iv2];
- if (v1->merge_to_index != -1) {
- v1 = cdt->vert_array[v1->merge_to_index];
- }
- if (v2->merge_to_index != -1) {
- v2 = cdt->vert_array[v2->merge_to_index];
- }
- add_edge_constraint(cdt, v1, v2, face_edge_id, &edge_list);
-#ifdef DEBUG_CDT
- if (dbg_level > 2) {
- fprintf(stderr, "edges for edge %d:\n", i);
- for (LinkNode *ln = edge_list; ln; ln = ln->next) {
- CDTEdge *cdt_e = (CDTEdge *)ln->link;
- fprintf(stderr,
- " (%.2f,%.2f)->(%.2f,%.2f)\n",
- F2(cdt_e->symedges[0].vert->co),
- F2(cdt_e->symedges[1].vert->co));
- }
- }
- if (dbg_level > 2) {
- cdt_draw(cdt, "after a face edge");
- if (dbg_level > 3) {
- dump_cdt(cdt, "after a face edge");
- }
- validate_cdt(cdt, true, true, false);
- }
-#endif
- if (i == 0) {
- face_edge = (CDTEdge *)edge_list->link;
- face_symedge = &face_edge->symedges[0];
- if (face_symedge->vert != v1) {
- face_symedge = &face_edge->symedges[1];
- BLI_assert(face_symedge->vert == v1);
- }
- }
- BLI_linklist_free_pool(edge_list, NULL, cdt->listpool);
- }
- fedge_start = cdt->face_edge_offset + fstart;
- fedge_end = fedge_start + flen - 1;
- add_face_ids(cdt, face_symedge, f, fedge_start, fedge_end);
- }
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- validate_cdt(cdt, true, true, false);
- }
- if (dbg_level > 1) {
- cdt_draw(cdt, "after adding edges and faces");
- if (dbg_level > 2) {
- dump_cdt(cdt, "after adding edges and faces");
- }
- }
-#endif
-
- if (cdt->epsilon > 0.0) {
- remove_small_features(cdt);
-#ifdef DEBUG_CDT
- if (dbg_level > 2) {
- cdt_draw(cdt, "after remove small features\n");
- if (dbg_level > 3) {
- dump_cdt(cdt, "after remove small features\n");
- }
- }
-#endif
- }
-
- result = cdt_get_output(cdt, input, output_type);
-#ifdef DEBUG_CDT
- if (dbg_level > 0) {
- cdt_draw(cdt, "final");
- }
-#endif
-
- if (input != input_orig) {
- free_modified_input((CDT_input *)input);
- }
- new_cdt_free(cdt);
- return result;
-}
-
-void BLI_delaunay_2d_cdt_free(CDT_result *result)
-{
- if (result == NULL) {
- return;
- }
- if (result->vert_coords) {
- MEM_freeN(result->vert_coords);
- }
- if (result->edges) {
- MEM_freeN(result->edges);
- }
- if (result->faces) {
- MEM_freeN(result->faces);
- }
- if (result->faces_start_table) {
- MEM_freeN(result->faces_start_table);
- }
- if (result->faces_len_table) {
- MEM_freeN(result->faces_len_table);
- }
- if (result->verts_orig) {
- MEM_freeN(result->verts_orig);
- }
- if (result->verts_orig_start_table) {
- MEM_freeN(result->verts_orig_start_table);
- }
- if (result->verts_orig_len_table) {
- MEM_freeN(result->verts_orig_len_table);
- }
- if (result->edges_orig) {
- MEM_freeN(result->edges_orig);
- }
- if (result->edges_orig_start_table) {
- MEM_freeN(result->edges_orig_start_table);
- }
- if (result->edges_orig_len_table) {
- MEM_freeN(result->edges_orig_len_table);
- }
- if (result->faces_orig) {
- MEM_freeN(result->faces_orig);
- }
- if (result->faces_orig_start_table) {
- MEM_freeN(result->faces_orig_start_table);
- }
- if (result->faces_orig_len_table) {
- MEM_freeN(result->faces_orig_len_table);
- }
- MEM_freeN(result);
-}
-
-#ifdef DEBUG_CDT
-
-ATTU static const char *vertname(const CDTVert *v)
-{
- static char vertnamebuf[20];
-
- sprintf(vertnamebuf, "[%d]", v->index);
- return vertnamebuf;
-}
-
-ATTU static const char *sename(const SymEdge *se)
-{
- static char senamebuf[20];
-
- sprintf(senamebuf, "{%x}", (POINTER_AS_UINT(se)) & 0xFFFF);
- return senamebuf;
-}
-
-static void dump_v(const CDTVert *v, const char *lab)
-{
- fprintf(stderr, "%s%s(%.10f,%.10f)\n", lab, vertname(v), F2(v->co));
-}
-
-static void dump_se(const SymEdge *se, const char *lab)
-{
- if (se->next) {
- fprintf(stderr,
- "%s%s((%.10f,%.10f)->(%.10f,%.10f))",
- lab,
- vertname(se->vert),
- F2(se->vert->co),
- F2(se->next->vert->co));
- fprintf(stderr, "%s\n", vertname(se->next->vert));
- }
- else {
- fprintf(stderr, "%s%s((%.10f,%.10f)->NULL)\n", lab, vertname(se->vert), F2(se->vert->co));
- }
-}
-
-static void dump_se_short(const SymEdge *se, const char *lab)
-{
- if (se == NULL) {
- fprintf(stderr, "%sNULL", lab);
- }
- else {
- fprintf(stderr, "%s%s", lab, vertname(se->vert));
- fprintf(stderr, "%s", se->next == NULL ? "[NULL]" : vertname(se->next->vert));
- }
-}
-
-static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit)
-{
- int count = 0;
- const SymEdge *s = se;
- fprintf(stderr, "%s:\n", lab);
- do {
- dump_se(s, " ");
- s = s->next;
- count++;
- } while (s != se && count < limit);
- if (count == limit) {
- fprintf(stderr, " limit hit without cycle!\n");
- }
-}
-
-static void dump_id_list(const LinkNode *id_list, const char *lab)
-{
- const LinkNode *ln;
- if (!id_list) {
- return;
- }
- fprintf(stderr, "%s", lab);
- for (ln = id_list; ln; ln = ln->next) {
- fprintf(stderr, "%d%c", POINTER_AS_INT(ln->link), ln->next ? ' ' : '\n');
- }
-}
-
-static void dump_cross_data(struct CrossData *cd, const char *lab)
-{
- fprintf(stderr, "%s", lab);
- if (cd->lambda == 0.0) {
- fprintf(stderr, "v%d", cd->vert->index);
- }
- else {
- fprintf(stderr, "lambda=%.17g", cd->lambda);
- }
- dump_se_short(cd->in, " in=");
- dump_se_short(cd->out, " out=");
- fprintf(stderr, "\n");
-}
-
-/* If filter_fn != NULL, only dump vert v its edges when filter_fn(cdt, v, filter_data) is true. */
-# define PL(p) (POINTER_AS_UINT(p) & 0xFFFF)
-static void dump_cdt_filtered(const CDT_state *cdt,
- bool (*filter_fn)(const CDT_state *, int, void *),
- void *filter_data,
- const char *lab)
-{
- LinkNode *ln;
- CDTVert *v, *vother;
- CDTEdge *e;
- CDTFace *f;
- SymEdge *se;
- int i, cnt;
-
- fprintf(stderr, "\nCDT %s\n", lab);
- fprintf(stderr, "\nVERTS\n");
- for (i = 0; i < cdt->vert_array_len; i++) {
- if (filter_fn && !filter_fn(cdt, i, filter_data)) {
- continue;
- }
- v = cdt->vert_array[i];
- fprintf(stderr, "%s %x: (%f,%f) symedge=%x", vertname(v), PL(v), F2(v->co), PL(v->symedge));
- if (v->merge_to_index == -1) {
- fprintf(stderr, "\n");
- }
- else {
- fprintf(stderr, " merge to %s\n", vertname(cdt->vert_array[v->merge_to_index]));
- continue;
- }
- dump_id_list(v->input_ids, " ");
- se = v->symedge;
- cnt = 0;
- if (se) {
- fprintf(stderr, " edges out:\n");
- do {
- if (se->next == NULL) {
- fprintf(stderr, " [NULL next/rot symedge, se=%x\n", PL(se));
- break;
- }
- if (se->next->next == NULL) {
- fprintf(stderr, " [NULL next-next/rot symedge, se=%x\n", PL(se));
- break;
- }
- vother = sym(se)->vert;
- fprintf(stderr, " %s (e=%x, se=%x)\n", vertname(vother), PL(se->edge), PL(se));
- se = se->rot;
- cnt++;
- } while (se != v->symedge && cnt < 25);
- fprintf(stderr, "\n");
- }
- }
- if (filter_fn) {
- return;
- }
- fprintf(stderr, "\nEDGES\n");
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (e->symedges[0].next == NULL) {
- continue;
- }
- fprintf(stderr, "%x:\n", PL(e));
- for (i = 0; i < 2; i++) {
- se = &e->symedges[i];
- fprintf(stderr,
- " se[%d] @%x: next=%x, rot=%x, vert=%x [%s] (%.2f,%.2f), edge=%x, face=%x\n",
- i,
- PL(se),
- PL(se->next),
- PL(se->rot),
- PL(se->vert),
- vertname(se->vert),
- F2(se->vert->co),
- PL(se->edge),
- PL(se->face));
- }
- dump_id_list(e->input_ids, " ");
- }
- fprintf(stderr, "\nFACES\n");
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- if (f->deleted) {
- continue;
- }
- if (f == cdt->outer_face) {
- fprintf(stderr, "%x: outer", PL(f));
- }
- fprintf(stderr, " symedge=%x\n", PL(f->symedge));
- dump_id_list(f->input_ids, " ");
- }
- fprintf(stderr, "\nOTHER\n");
- fprintf(stderr, "outer_face=%x\n", PL(cdt->outer_face));
- fprintf(
- stderr, "minx=%f, maxx=%f, miny=%f, maxy=%f\n", cdt->minx, cdt->maxx, cdt->miny, cdt->maxy);
- fprintf(stderr, "margin=%f\n", cdt->margin);
-}
-# undef PL
-
-static void dump_cdt(const CDT_state *cdt, const char *lab)
-{
- dump_cdt_filtered(cdt, NULL, NULL, lab);
-}
-
-typedef struct ReachableFilterData {
- int vstart_index;
- int maxdist;
-} ReachableFilterData;
-
-/* Stupid algorithm will repeat itself. Don't use for large n. */
-static bool reachable_filter(const CDT_state *cdt, int v_index, void *filter_data)
-{
- CDTVert *v;
- SymEdge *se;
- ReachableFilterData *rfd_in = (ReachableFilterData *)filter_data;
- ReachableFilterData rfd_next;
-
- if (v_index == rfd_in->vstart_index) {
- return true;
- }
- if (rfd_in->maxdist <= 0 || v_index < 0 || v_index >= cdt->vert_array_len) {
- return false;
- }
- else {
- v = cdt->vert_array[v_index];
- se = v->symedge;
- if (se != NULL) {
- rfd_next.vstart_index = rfd_in->vstart_index;
- rfd_next.maxdist = rfd_in->maxdist - 1;
- do {
- if (reachable_filter(cdt, se->next->vert->index, &rfd_next)) {
- return true;
- }
- se = se->rot;
- } while (se != v->symedge);
- }
- }
- return false;
-}
-
-static void set_min_max(CDT_state *cdt)
-{
- int i;
- double minx, maxx, miny, maxy;
- double *co;
-
- minx = miny = DBL_MAX;
- maxx = maxy = -DBL_MAX;
- for (i = 0; i < cdt->vert_array_len; i++) {
- co = cdt->vert_array[i]->co;
- if (co[0] < minx) {
- minx = co[0];
- }
- if (co[0] > maxx) {
- maxx = co[0];
- }
- if (co[1] < miny) {
- miny = co[1];
- }
- if (co[1] > maxy) {
- maxy = co[1];
- }
- }
- if (minx != DBL_MAX) {
- cdt->minx = minx;
- cdt->miny = miny;
- cdt->maxx = maxx;
- cdt->maxy = maxy;
- }
-}
-
-static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab)
-{
- ReachableFilterData rfd;
- rfd.vstart_index = v;
- rfd.maxdist = maxdist;
- dump_cdt_filtered(cdt, reachable_filter, &rfd, lab);
-}
-
-/*
- * Make an html file with svg in it to display the argument cdt.
- * Mouse-overs will reveal the coordinates of vertices and edges.
- * Constraint edges are drawn thicker than non-constraint edges.
- * The first call creates DRAWFILE; subsequent calls append to it.
- */
-# define DRAWFILE "/tmp/debug_draw.html"
-# define MAX_DRAW_WIDTH 2000
-# define MAX_DRAW_HEIGHT 1400
-# define THIN_LINE 1
-# define THICK_LINE 4
-# define VERT_RADIUS 3
-# define DRAW_VERT_LABELS 1
-# define DRAW_EDGE_LABELS 0
-
-static void cdt_draw_region(
- CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy)
-{
- static bool append = false;
- FILE *f = fopen(DRAWFILE, append ? "a" : "w");
- int view_width, view_height;
- double width, height, aspect, scale;
- LinkNode *ln;
- CDTVert *v, *u;
- CDTEdge *e;
- int i, strokew;
-
- width = maxx - minx;
- height = maxy - miny;
- aspect = height / width;
- view_width = MAX_DRAW_WIDTH;
- view_height = (int)(view_width * aspect);
- if (view_height > MAX_DRAW_HEIGHT) {
- view_height = MAX_DRAW_HEIGHT;
- view_width = (int)(view_height / aspect);
- }
- scale = view_width / width;
-
-# define SX(x) ((x - minx) * scale)
-# define SY(y) ((maxy - y) * scale)
-
- if (!f) {
- printf("couldn't open file %s\n", DRAWFILE);
- return;
- }
- fprintf(f, "<div>%s</div>\n<div>\n", lab);
- fprintf(f,
- "<svg version=\"1.1\" "
- "xmlns=\"http://www.w3.org/2000/svg\" "
- "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
- "xml:space=\"preserve\"\n");
- fprintf(f, "width=\"%d\" height=\"%d\">/n", view_width, view_height);
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- if (is_deleted_edge(e)) {
- continue;
- }
- u = e->symedges[0].vert;
- v = e->symedges[1].vert;
- strokew = is_constrained_edge(e) ? THICK_LINE : THIN_LINE;
- fprintf(f,
- "<line fill=\"none\" stroke=\"black\" stroke-width=\"%d\" "
- "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\">\n",
- strokew,
- SX(u->co[0]),
- SY(u->co[1]),
- SX(v->co[0]),
- SY(v->co[1]));
- fprintf(f, " <title>%s", vertname(u));
- fprintf(f, "%s</title>\n", vertname(v));
- fprintf(f, "</line>\n");
-# if DRAW_EDGE_LABELS
- fprintf(f,
- "<text x=\"%f\" y=\"%f\" font-size=\"small\">",
- SX(0.5 * (u->co[0] + v->co[0])),
- SY(0.5 * (u->co[1] + v->co[1])));
- fprintf(f, "%s", vertname(u));
- fprintf(f, "%s", vertname(v));
- fprintf(f, "%s", sename(&e->symedges[0]));
- fprintf(f, "%s</text>\n", sename(&e->symedges[1]));
-# endif
- }
- i = 0;
- for (; i < cdt->vert_array_len; i++) {
- v = cdt->vert_array[i];
- if (v->merge_to_index != -1) {
- continue;
- }
- fprintf(f,
- "<circle fill=\"black\" cx=\"%f\" cy=\"%f\" r=\"%d\">\n",
- SX(v->co[0]),
- SY(v->co[1]),
- VERT_RADIUS);
- fprintf(f, " <title>%s(%.10f,%.10f)</title>\n", vertname(v), v->co[0], v->co[1]);
- fprintf(f, "</circle>\n");
-# if DRAW_VERT_LABELS
- fprintf(f,
- "<text x=\"%f\" y=\"%f\" font-size=\"small\">%s</text>\n",
- SX(v->co[0]) + VERT_RADIUS,
- SY(v->co[1]) - VERT_RADIUS,
- vertname(v));
-# endif
- }
-
- fprintf(f, "</svg>\n</div>\n");
- fclose(f);
- append = true;
-# undef SX
-# undef SY
-}
-
-static void cdt_draw(CDT_state *cdt, const char *lab)
-{
- double draw_margin, minx, maxx, miny, maxy;
-
- set_min_max(cdt);
- draw_margin = (cdt->maxx - cdt->minx + cdt->maxy - cdt->miny + 1) * 0.05;
- minx = cdt->minx - draw_margin;
- maxx = cdt->maxx + draw_margin;
- miny = cdt->miny - draw_margin;
- maxy = cdt->maxy + draw_margin;
- cdt_draw_region(cdt, lab, minx, miny, maxx, maxy);
-}
-
-static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab)
-{
- const double *co = cdt->vert_array[v]->co;
- cdt_draw_region(cdt, lab, co[0] - dist, co[1] - dist, co[0] + dist, co[1] + dist);
-}
-
-static void cdt_draw_edge_region(CDT_state *cdt, int v1, int v2, double dist, const char *lab)
-{
- const double *co1 = cdt->vert_array[v1]->co;
- const double *co2 = cdt->vert_array[v2]->co;
- double minx, miny, maxx, maxy;
-
- minx = min_dd(co1[0], co2[0]);
- miny = min_dd(co1[1], co2[1]);
- maxx = max_dd(co1[0], co2[0]);
- maxy = max_dd(co1[1], co2[1]);
- cdt_draw_region(cdt, lab, minx - dist, miny - dist, maxx + dist, maxy + dist);
-}
-
-# define CDTFILE "/tmp/cdtinput.txt"
-static void write_cdt_input_to_file(const CDT_input *inp)
-{
- int i, j;
- FILE *f = fopen(CDTFILE, "w");
-
- fprintf(f, "%d %d %d\n", inp->verts_len, inp->edges_len, inp->faces_len);
- for (i = 0; i < inp->verts_len; i++) {
- fprintf(f, "%.17f %.17f\n", inp->vert_coords[i][0], inp->vert_coords[i][1]);
- }
- for (i = 0; i < inp->edges_len; i++) {
- fprintf(f, "%d %d\n", inp->edges[i][0], inp->edges[i][1]);
- }
- for (i = 0; i < inp->faces_len; i++) {
- for (j = 0; j < inp->faces_len_table[i]; j++) {
- fprintf(f, "%d ", inp->faces[j + inp->faces_start_table[i]]);
- }
- fprintf(f, "\n");
- }
- fclose(f);
-}
-
-# ifndef NDEBUG /* Only used in assert. */
-/*
- * Is a visible from b: i.e., ab crosses no edge of cdt?
- * If constrained is true, consider only constrained edges as possible crossed edges.
- * In any case, don't count an edge ab itself.
- * Note: this is an expensive test if there are a lot of edges.
- */
-static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, const CDT_state *cdt)
-{
- const LinkNode *ln;
- const CDTEdge *e;
- const SymEdge *se, *senext;
- double lambda, mu;
- int ikind;
-
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (const CDTEdge *)ln->link;
- if (is_deleted_edge(e) || is_border_edge(e, cdt)) {
- continue;
- }
- if (constrained && !is_constrained_edge(e)) {
- continue;
- }
- se = (const SymEdge *)&e->symedges[0];
- senext = se->next;
- if ((a == se->vert || a == senext->vert) || b == se->vert || b == se->next->vert) {
- continue;
- }
- ikind = isect_seg_seg_v2_lambda_mu_db(
- a->co, b->co, se->vert->co, senext->vert->co, &lambda, &mu);
- if (ikind != ISECT_LINE_LINE_NONE) {
- if (ikind == ISECT_LINE_LINE_COLINEAR) {
- /* TODO: special test here for overlap. */
- continue;
- }
- /* Allow an intersection very near or at ends, to allow for numerical error. */
- if (lambda > FLT_EPSILON && (1.0 - lambda) > FLT_EPSILON && mu > FLT_EPSILON &&
- (1.0 - mu) > FLT_EPSILON) {
- return false;
- }
- }
- }
- return true;
-}
-# endif
-
-# ifndef NDEBUG /* Only used in assert. */
-/*
- * Check that edge ab satisfies constrained delaunay condition:
- * That is, for all non-constraint, non-border edges ab,
- * (1) ab is visible in the constraint graph; and
- * (2) there is a circle through a and b such that any vertex v connected by an edge to a or b
- * is not inside that circle.
- * The argument 'se' specifies ab by: a is se's vert and b is se->next's vert.
- * Return true if check is OK.
- */
-static bool is_delaunay_edge(const SymEdge *se)
-{
- int i;
- CDTVert *a, *b, *c;
- const SymEdge *sesym, *curse, *ss;
- bool ok[2];
-
- if (!is_constrained_edge(se->edge)) {
- return true;
- }
- sesym = sym(se);
- a = se->vert;
- b = se->next->vert;
- /* Try both the triangles adjacent to se's edge for circle. */
- for (i = 0; i < 2; i++) {
- ok[i] = true;
- curse = (i == 0) ? se : sesym;
- a = curse->vert;
- b = curse->next->vert;
- c = curse->next->next->vert;
- for (ss = curse->rot; ss != curse; ss = ss->rot) {
- ok[i] |= incircle(a->co, b->co, c->co, ss->next->vert->co) <= 0.0;
- }
- }
- return ok[0] || ok[1];
-}
-# endif
-
-# ifndef NDEBUG
-static bool plausible_non_null_ptr(void *p)
-{
- return p > (void *)0x1000;
-}
-# endif
-
-static void validate_cdt(CDT_state *cdt,
- bool check_all_tris,
- bool check_delaunay,
- bool check_visibility)
-{
- LinkNode *ln;
- int totedges, totfaces, totverts;
- CDTEdge *e;
- SymEdge *se, *sesym, *s;
- CDTVert *v, *v1, *v2, *v3;
- CDTFace *f;
- int i, limit;
- bool isborder;
-
- if (cdt->output_prepared) {
- return;
- }
- if (cdt->edges == NULL || cdt->edges->next == NULL) {
- return;
- }
-
- BLI_assert(cdt != NULL);
- totedges = 0;
- for (ln = cdt->edges; ln; ln = ln->next) {
- e = (CDTEdge *)ln->link;
- se = &e->symedges[0];
- sesym = &e->symedges[1];
- if (is_deleted_edge(e)) {
- BLI_assert(se->rot == NULL && sesym->next == NULL && sesym->rot == NULL);
- continue;
- }
- totedges++;
- isborder = is_border_edge(e, cdt);
- BLI_assert(se->vert != sesym->vert);
- BLI_assert(se->edge == sesym->edge && se->edge == e);
- BLI_assert(sym(se) == sesym && sym(sesym) == se);
- for (i = 0; i < 2; i++) {
- se = &e->symedges[i];
- v = se->vert;
- f = se->face;
- BLI_assert(plausible_non_null_ptr(v));
- if (f != NULL) {
- BLI_assert(plausible_non_null_ptr(f));
- }
- BLI_assert(plausible_non_null_ptr(se->next));
- BLI_assert(plausible_non_null_ptr(se->rot));
- if (check_all_tris && se->face != cdt->outer_face) {
- limit = 3;
- }
- else {
- limit = 10000;
- }
- BLI_assert(reachable(se->next, se, limit));
- if (limit == 3) {
- v1 = se->vert;
- v2 = se->next->vert;
- v3 = se->next->next->vert;
- /* The triangle should be positively oriented, but because
- * the insertion of intersection vertices doesn't use exact
- * arithmetic, this may not be true, so allow a little slop. */
- BLI_assert(orient2d(v1->co, v2->co, v3->co) >= -FLT_EPSILON);
- BLI_assert(orient2d(v2->co, v3->co, v1->co) >= -FLT_EPSILON);
- BLI_assert(orient2d(v3->co, v1->co, v2->co) >= -FLT_EPSILON);
- }
- UNUSED_VARS_NDEBUG(limit);
- BLI_assert(se->next->next != se);
- s = se;
- do {
- BLI_assert(prev(s)->next == s);
- BLI_assert(s->rot == sym(prev(s)));
- s = s->next;
- } while (s != se);
- }
- if (check_visibility) {
- BLI_assert(isborder || is_visible(se->vert, se->next->vert, false, cdt));
- }
- if (!isborder && check_delaunay) {
- BLI_assert(is_delaunay_edge(se));
- }
- }
- totverts = 0;
- for (i = 0; i < cdt->vert_array_len; i++) {
- v = cdt->vert_array[i];
- BLI_assert(plausible_non_null_ptr(v));
- if (v->merge_to_index != -1) {
- BLI_assert(v->merge_to_index >= 0 && v->merge_to_index < cdt->vert_array_len);
- continue;
- }
- totverts++;
- BLI_assert(cdt->vert_array_len <= 1 || v->symedge->vert == v);
- }
- totfaces = 0;
- for (ln = cdt->faces; ln; ln = ln->next) {
- f = (CDTFace *)ln->link;
- BLI_assert(plausible_non_null_ptr(f));
- if (f->deleted) {
- continue;
- }
- totfaces++;
- if (f == cdt->outer_face) {
- continue;
- }
- }
- /* Euler's formula for planar graphs. */
- if (check_all_tris && totfaces > 1) {
- BLI_assert(totverts - totedges + totfaces == 2);
- }
-}
-#endif
-
-/* Jonathan Shewchuk's adaptive predicates, trimmed to those needed here.
- * Permission obtained by private communication from Jonathan to include this code in Blender.
- */
-
-/*
- * Routines for Arbitrary Precision Floating-point Arithmetic
- * and Fast Robust Geometric Predicates
- * (predicates.c)
- *
- * May 18, 1996
- *
- * Placed in the public domain by
- * Jonathan Richard Shewchuk
- * School of Computer Science
- * Carnegie Mellon University
- * 5000 Forbes Avenue
- * Pittsburgh, Pennsylvania 15213-3891
- * jrs@cs.cmu.edu
- *
- * This file contains C implementation of algorithms for exact addition
- * and multiplication of floating-point numbers, and predicates for
- * robustly performing the orientation and incircle tests used in
- * computational geometry. The algorithms and underlying theory are
- * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating-
- * Point Arithmetic and Fast Robust Geometric Predicates." Technical
- * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon
- * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to
- * Discrete & Computational Geometry.)
- *
- * This file, the paper listed above, and other information are available
- * from the Web page http://www.cs.cmu.edu/~quake/robust.html .
- *
- * Using this code:
- *
- * First, read the short or long version of the paper (from the Web page
- * above).
- *
- * Be sure to call exactinit() once, before calling any of the arithmetic
- * functions or geometric predicates. Also be sure to turn on the
- * optimizer when compiling this file.
- *
- * On some machines, the exact arithmetic routines might be defeated by the
- * use of internal extended precision floating-point registers. Sometimes
- * this problem can be fixed by defining certain values to be volatile,
- * thus forcing them to be stored to memory and rounded off. This isn't
- * a great solution, though, as it slows the arithmetic down.
- *
- * To try this out, write "#define INEXACT volatile" below. Normally,
- * however, INEXACT should be defined to be nothing. ("#define INEXACT".)
- */
-
-#define INEXACT /* Nothing */
-/* #define INEXACT volatile */
-
-/* Which of the following two methods of finding the absolute values is
- * fastest is compiler-dependent. A few compilers can inline and optimize
- * the fabs() call; but most will incur the overhead of a function call,
- * which is disastrously slow. A faster way on IEEE machines might be to
- * mask the appropriate bit, but that's difficult to do in C.
- */
-
-#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
-/* #define Absolute(a) fabs(a) */
-
-/* Many of the operations are broken up into two pieces, a main part that
- * performs an approximate operation, and a "tail" that computes the
- * roundoff error of that operation.
- *
- * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),
- * Split(), and Two_Product() are all implemented as described in the
- * reference. Each of these macros requires certain variables to be
- * defined in the calling routine. The variables `bvirt', `c', `abig',
- * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because
- * they store the result of an operation that may incur roundoff error.
- * The input parameter `x' (or the highest numbered `x_' parameter) must
- * also be declared `INEXACT'.
- */
-
-#define Fast_Two_Sum_Tail(a, b, x, y) \
- bvirt = x - a; \
- y = b - bvirt
-
-#define Fast_Two_Sum(a, b, x, y) \
- x = (double)(a + b); \
- Fast_Two_Sum_Tail(a, b, x, y)
-
-#define Fast_Two_Diff_Tail(a, b, x, y) \
- bvirt = a - x; \
- y = bvirt - b
-
-#define Fast_Two_Diff(a, b, x, y) \
- x = (double)(a - b); \
- Fast_Two_Diff_Tail(a, b, x, y)
-
-#define Two_Sum_Tail(a, b, x, y) \
- bvirt = (double)(x - a); \
- avirt = x - bvirt; \
- bround = b - bvirt; \
- around = a - avirt; \
- y = around + bround
-
-#define Two_Sum(a, b, x, y) \
- x = (double)(a + b); \
- Two_Sum_Tail(a, b, x, y)
-
-#define Two_Diff_Tail(a, b, x, y) \
- bvirt = (double)(a - x); \
- avirt = x + bvirt; \
- bround = bvirt - b; \
- around = a - avirt; \
- y = around + bround
-
-#define Two_Diff(a, b, x, y) \
- x = (double)(a - b); \
- Two_Diff_Tail(a, b, x, y)
-
-#define Split(a, ahi, alo) \
- c = (double)(splitter * a); \
- abig = (double)(c - a); \
- ahi = c - abig; \
- alo = a - ahi
-
-#define Two_Product_Tail(a, b, x, y) \
- Split(a, ahi, alo); \
- Split(b, bhi, blo); \
- err1 = x - (ahi * bhi); \
- err2 = err1 - (alo * bhi); \
- err3 = err2 - (ahi * blo); \
- y = (alo * blo) - err3
-
-#define Two_Product(a, b, x, y) \
- x = (double)(a * b); \
- Two_Product_Tail(a, b, x, y)
-
-#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
- x = (double)(a * b); \
- Split(a, ahi, alo); \
- err1 = x - (ahi * bhi); \
- err2 = err1 - (alo * bhi); \
- err3 = err2 - (ahi * blo); \
- y = (alo * blo) - err3
-
-#define Square_Tail(a, x, y) \
- Split(a, ahi, alo); \
- err1 = x - (ahi * ahi); \
- err3 = err1 - ((ahi + ahi) * alo); \
- y = (alo * alo) - err3
-
-#define Square(a, x, y) \
- x = (double)(a * a); \
- Square_Tail(a, x, y)
-
-#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
- Two_Sum(a0, b, _i, x0); \
- Two_Sum(a1, _i, x2, x1)
-
-#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
- Two_Diff(a0, b, _i, x0); \
- Two_Sum(a1, _i, x2, x1)
-
-#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
- Two_One_Sum(a1, a0, b0, _j, _0, x0); \
- Two_One_Sum(_j, _0, b1, x3, x2, x1)
-
-#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
- Two_One_Diff(a1, a0, b0, _j, _0, x0); \
- Two_One_Diff(_j, _0, b1, x3, x2, x1)
-
-static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */
-static double m_epsilon; /* = 2^(-p). Used to estimate roundoff errors. */
-/* A set of coefficients used to calculate maximum roundoff errors. */
-static double resulterrbound;
-static double ccwerrboundA, ccwerrboundB, ccwerrboundC;
-static double o3derrboundA, o3derrboundB, o3derrboundC;
-static double iccerrboundA, iccerrboundB, iccerrboundC;
-static double isperrboundA, isperrboundB, isperrboundC;
-
-/* exactinit() Initialize the variables used for exact arithmetic.
- *
- * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in
- * floating-point arithmetic. `epsilon' bounds the relative roundoff
- * error. It is used for floating-point error analysis.
- *
- * `splitter' is used to split floating-point numbers into two
- * half-length significands for exact multiplication.
- *
- * I imagine that a highly optimizing compiler might be too smart for its
- * own good, and somehow cause this routine to fail, if it pretends that
- * floating-point arithmetic is too much like real arithmetic.
- *
- * Don't change this routine unless you fully understand it.
- */
-
-static void exactinit(void)
-{
- double half;
- double check, lastcheck;
- int every_other;
-
- every_other = 1;
- half = 0.5;
- m_epsilon = 1.0;
- splitter = 1.0;
- check = 1.0;
- /* Repeatedly divide `epsilon' by two until it is too small to add to
- * one without causing roundoff. (Also check if the sum is equal to
- * the previous sum, for machines that round up instead of using exact
- * rounding. Not that this library will work on such machines anyway.
- */
- do {
- lastcheck = check;
- m_epsilon *= half;
- if (every_other) {
- splitter *= 2.0;
- }
- every_other = !every_other;
- check = 1.0 + m_epsilon;
- } while ((check != 1.0) && (check != lastcheck));
- splitter += 1.0;
-
- /* Error bounds for orientation and incircle tests. */
- resulterrbound = (3.0 + 8.0 * m_epsilon) * m_epsilon;
- ccwerrboundA = (3.0 + 16.0 * m_epsilon) * m_epsilon;
- ccwerrboundB = (2.0 + 12.0 * m_epsilon) * m_epsilon;
- ccwerrboundC = (9.0 + 64.0 * m_epsilon) * m_epsilon * m_epsilon;
- o3derrboundA = (7.0 + 56.0 * m_epsilon) * m_epsilon;
- o3derrboundB = (3.0 + 28.0 * m_epsilon) * m_epsilon;
- o3derrboundC = (26.0 + 288.0 * m_epsilon) * m_epsilon * m_epsilon;
- iccerrboundA = (10.0 + 96.0 * m_epsilon) * m_epsilon;
- iccerrboundB = (4.0 + 48.0 * m_epsilon) * m_epsilon;
- iccerrboundC = (44.0 + 576.0 * m_epsilon) * m_epsilon * m_epsilon;
- isperrboundA = (16.0 + 224.0 * m_epsilon) * m_epsilon;
- isperrboundB = (5.0 + 72.0 * m_epsilon) * m_epsilon;
- isperrboundC = (71.0 + 1408.0 * m_epsilon) * m_epsilon * m_epsilon;
-}
-
-/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero
- * components from the output expansion.
- *
- * Sets h = e + f. See the long version of my paper for details.
- *
- * If round-to-even is used (as with IEEE 754), maintains the strongly
- * non-overlapping property. (That is, if e is strongly non-overlapping, h
- * will be also.) Does NOT maintain the non-overlapping or non-adjacent
- * properties.
- */
-
-static int fast_expansion_sum_zeroelim(
- int elen, double *e, int flen, double *f, double *h) /* h cannot be e or f. */
-{
- double Q;
- INEXACT double Qnew;
- INEXACT double hh;
- INEXACT double bvirt;
- double avirt, bround, around;
- int eindex, findex, hindex;
- double enow, fnow;
-
- enow = e[0];
- fnow = f[0];
- eindex = findex = 0;
- if ((fnow > enow) == (fnow > -enow)) {
- Q = enow;
- enow = e[++eindex];
- }
- else {
- Q = fnow;
- fnow = f[++findex];
- }
- hindex = 0;
- if ((eindex < elen) && (findex < flen)) {
- if ((fnow > enow) == (fnow > -enow)) {
- Fast_Two_Sum(enow, Q, Qnew, hh);
- enow = e[++eindex];
- }
- else {
- Fast_Two_Sum(fnow, Q, Qnew, hh);
- fnow = f[++findex];
- }
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- while ((eindex < elen) && (findex < flen)) {
- if ((fnow > enow) == (fnow > -enow)) {
- Two_Sum(Q, enow, Qnew, hh);
- enow = e[++eindex];
- }
- else {
- Two_Sum(Q, fnow, Qnew, hh);
- fnow = f[++findex];
- }
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- }
- }
- while (eindex < elen) {
- Two_Sum(Q, enow, Qnew, hh);
- enow = e[++eindex];
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- }
- while (findex < flen) {
- Two_Sum(Q, fnow, Qnew, hh);
- fnow = f[++findex];
- Q = Qnew;
- if (hh != 0.0) {
- h[hindex++] = hh;
- }
- }
- if ((Q != 0.0) || (hindex == 0)) {
- h[hindex++] = Q;
- }
- return hindex;
-}
-
-/* scale_expansion_zeroelim() Multiply an expansion by a scalar,
- * eliminating zero components from the
- * output expansion.
- *
- * Sets h = be. See either version of my paper for details.
- *
- * Maintains the nonoverlapping property. If round-to-even is used (as
- * with IEEE 754), maintains the strongly nonoverlapping and nonadjacent
- * properties as well. (That is, if e has one of these properties, so
- * will h.)
- */
-
-static int scale_expansion_zeroelim(int elen,
- double *e,
- double b,
- double *h) /* e and h cannot be the same. */
-{
- INEXACT double Q, sum;
- double hh;
- INEXACT double product1;
- double product0;
- int eindex, hindex;
- double enow;
- INEXACT double bvirt;
- double avirt, bround, around;
- INEXACT double c;
- INEXACT double abig;
- double ahi, alo, bhi, blo;
- double err1, err2, err3;
-
- Split(b, bhi, blo);
- Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
- hindex = 0;
- if (hh != 0) {
- h[hindex++] = hh;
- }
- for (eindex = 1; eindex < elen; eindex++) {
- enow = e[eindex];
- Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
- Two_Sum(Q, product0, sum, hh);
- if (hh != 0) {
- h[hindex++] = hh;
- }
- Fast_Two_Sum(product1, sum, Q, hh);
- if (hh != 0) {
- h[hindex++] = hh;
- }
- }
- if ((Q != 0.0) || (hindex == 0)) {
- h[hindex++] = Q;
- }
- return hindex;
-}
-
-/* estimate() Produce a one-word estimate of an expansion's value.
- *
- * See either version of my paper for details.
- */
-
-static double estimate(int elen, double *e)
-{
- double Q;
- int eindex;
-
- Q = e[0];
- for (eindex = 1; eindex < elen; eindex++) {
- Q += e[eindex];
- }
- return Q;
-}
-
-/* orient2d() Adaptive exact 2D orientation test. Robust.
- *
- * Return a positive value if the points pa, pb, and pc occur
- * in counterclockwise order; a negative value if they occur
- * in clockwise order; and zero if they are collinear. The
- * result is also a rough approximation of twice the signed
- * area of the triangle defined by the three points.
- *
- * This uses exact arithmetic to ensure a correct answer. The
- * result returned is the determinant of a matrix.
- * This determinant is computed adaptively, in the sense that exact
- * arithmetic is used only to the degree it is needed to ensure that the
- * returned value has the correct sign. Hence, orient2d() is usually quite
- * fast, but will run more slowly when the input points are collinear or
- * nearly so.
- */
-
-static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum)
-{
- INEXACT double acx, acy, bcx, bcy;
- double acxtail, acytail, bcxtail, bcytail;
- INEXACT double detleft, detright;
- double detlefttail, detrighttail;
- double det, errbound;
- double B[4], C1[8], C2[12], D[16];
- INEXACT double B3;
- int C1length, C2length, Dlength;
- double u[4];
- INEXACT double u3;
- INEXACT double s1, t1;
- double s0, t0;
-
- INEXACT double bvirt;
- double avirt, bround, around;
- INEXACT double c;
- INEXACT double abig;
- double ahi, alo, bhi, blo;
- double err1, err2, err3;
- INEXACT double _i, _j;
- double _0;
-
- acx = (double)(pa[0] - pc[0]);
- bcx = (double)(pb[0] - pc[0]);
- acy = (double)(pa[1] - pc[1]);
- bcy = (double)(pb[1] - pc[1]);
-
- Two_Product(acx, bcy, detleft, detlefttail);
- Two_Product(acy, bcx, detright, detrighttail);
-
- Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]);
- B[3] = B3;
-
- det = estimate(4, B);
- errbound = ccwerrboundB * detsum;
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
- Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
- Two_Diff_Tail(pa[1], pc[1], acy, acytail);
- Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
-
- if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) {
- return det;
- }
-
- errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
- det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- Two_Product(acxtail, bcy, s1, s0);
- Two_Product(acytail, bcx, t1, t0);
- Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
-
- Two_Product(acx, bcytail, s1, s0);
- Two_Product(acy, bcxtail, t1, t0);
- Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
-
- Two_Product(acxtail, bcytail, s1, s0);
- Two_Product(acytail, bcxtail, t1, t0);
- Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
-
- return (D[Dlength - 1]);
-}
-
-static double orient2d(const double *pa, const double *pb, const double *pc)
-{
- double detleft, detright, det;
- double detsum, errbound;
-
- detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
- detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
- det = detleft - detright;
-
- if (detleft > 0.0) {
- if (detright <= 0.0) {
- return det;
- }
- else {
- detsum = detleft + detright;
- }
- }
- else if (detleft < 0.0) {
- if (detright >= 0.0) {
- return det;
- }
- else {
- detsum = -detleft - detright;
- }
- }
- else {
- return det;
- }
-
- errbound = ccwerrboundA * detsum;
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- return orient2dadapt(pa, pb, pc, detsum);
-}
-
-/* incircle() Adaptive exact 2D incircle test. Robust.
- *
- * Return a positive value if the point pd lies inside the
- * circle passing through pa, pb, and pc; a negative value if
- * it lies outside; and zero if the four points are cocircular.
- * The points pa, pb, and pc must be in counterclockwise
- * order, or the sign of the result will be reversed.
- *
- * This uses exact arithmetic to ensure a correct answer.
- * The result returned is the determinant of a matrix.
- * This determinant is computed adaptively, in the sense that exact
- * arithmetic is used only to the degree it is needed to ensure that the
- * returned value has the correct sign. Hence, incircle() is usually quite
- * fast, but will run more slowly when the input points are cocircular or
- * nearly so.
- */
-
-static double incircleadapt(
- const double *pa, const double *pb, const double *pc, const double *pd, double permanent)
-{
- INEXACT double adx, bdx, cdx, ady, bdy, cdy;
- double det, errbound;
-
- INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
- double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
- double bc[4], ca[4], ab[4];
- INEXACT double bc3, ca3, ab3;
- double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
- int axbclen, axxbclen, aybclen, ayybclen, alen;
- double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
- int bxcalen, bxxcalen, bycalen, byycalen, blen;
- double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
- int cxablen, cxxablen, cyablen, cyyablen, clen;
- double abdet[64];
- int ablen;
- double fin1[1152], fin2[1152];
- double *finnow, *finother, *finswap;
- int finlength;
-
- double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
- INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
- double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
- double aa[4], bb[4], cc[4];
- INEXACT double aa3, bb3, cc3;
- INEXACT double ti1, tj1;
- double ti0, tj0;
- double u[4], v[4];
- INEXACT double u3, v3;
- double temp8[8], temp16a[16], temp16b[16], temp16c[16];
- double temp32a[32], temp32b[32], temp48[48], temp64[64];
- int temp8len, temp16alen, temp16blen, temp16clen;
- int temp32alen, temp32blen, temp48len, temp64len;
- double axtbb[8], axtcc[8], aytbb[8], aytcc[8];
- int axtbblen, axtcclen, aytbblen, aytcclen;
- double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
- int bxtaalen, bxtcclen, bytaalen, bytcclen;
- double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
- int cxtaalen, cxtbblen, cytaalen, cytbblen;
- double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
- int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
- double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
- int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
- double axtbctt[8], aytbctt[8], bxtcatt[8];
- double bytcatt[8], cxtabtt[8], cytabtt[8];
- int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
- double abt[8], bct[8], cat[8];
- int abtlen, bctlen, catlen;
- double abtt[4], bctt[4], catt[4];
- int abttlen, bcttlen, cattlen;
- INEXACT double abtt3, bctt3, catt3;
- double negate;
-
- INEXACT double bvirt;
- double avirt, bround, around;
- INEXACT double c;
- INEXACT double abig;
- double ahi, alo, bhi, blo;
- double err1, err2, err3;
- INEXACT double _i, _j;
- double _0;
-
- adx = (double)(pa[0] - pd[0]);
- bdx = (double)(pb[0] - pd[0]);
- cdx = (double)(pc[0] - pd[0]);
- ady = (double)(pa[1] - pd[1]);
- bdy = (double)(pb[1] - pd[1]);
- cdy = (double)(pc[1] - pd[1]);
-
- Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
- Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
- Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
- bc[3] = bc3;
- axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
- axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
- aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
- ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
- alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
-
- Two_Product(cdx, ady, cdxady1, cdxady0);
- Two_Product(adx, cdy, adxcdy1, adxcdy0);
- Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
- ca[3] = ca3;
- bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
- bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
- bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
- byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
- blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
-
- Two_Product(adx, bdy, adxbdy1, adxbdy0);
- Two_Product(bdx, ady, bdxady1, bdxady0);
- Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
- ab[3] = ab3;
- cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
- cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
- cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
- cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
- clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
-
- ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
- finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
-
- det = estimate(finlength, fin1);
- errbound = iccerrboundB * permanent;
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
- Two_Diff_Tail(pa[1], pd[1], ady, adytail);
- Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
- Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
- Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
- Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
- if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) &&
- (bdytail == 0.0) && (cdytail == 0.0)) {
- return det;
- }
-
- errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
- det += ((adx * adx + ady * ady) *
- ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) +
- 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) +
- ((bdx * bdx + bdy * bdy) *
- ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) +
- 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) +
- ((cdx * cdx + cdy * cdy) *
- ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) +
- 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
- if ((det >= errbound) || (-det >= errbound)) {
- return det;
- }
-
- finnow = fin1;
- finother = fin2;
-
- if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
- Square(adx, adxadx1, adxadx0);
- Square(ady, adyady1, adyady0);
- Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
- aa[3] = aa3;
- }
- if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
- Square(bdx, bdxbdx1, bdxbdx0);
- Square(bdy, bdybdy1, bdybdy0);
- Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
- bb[3] = bb3;
- }
- if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
- Square(cdx, cdxcdx1, cdxcdx0);
- Square(cdy, cdycdy1, cdycdy0);
- Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
- cc[3] = cc3;
- }
-
- if (adxtail != 0.0) {
- axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
- temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a);
-
- axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
- temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
-
- axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
- temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (adytail != 0.0) {
- aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
- temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a);
-
- aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
- temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
-
- aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
- temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdxtail != 0.0) {
- bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
- temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a);
-
- bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
- temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
-
- bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
- temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdytail != 0.0) {
- bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
- temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a);
-
- bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
- temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
-
- bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
- temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdxtail != 0.0) {
- cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
- temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a);
-
- cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
- temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
-
- cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
- temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdytail != 0.0) {
- cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
- temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a);
-
- cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
- temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
-
- cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
- temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
-
- temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- if ((adxtail != 0.0) || (adytail != 0.0)) {
- if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
- Two_Product(bdxtail, cdy, ti1, ti0);
- Two_Product(bdx, cdytail, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- negate = -bdy;
- Two_Product(cdxtail, negate, ti1, ti0);
- negate = -bdytail;
- Two_Product(cdx, negate, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
- v[3] = v3;
- bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
-
- Two_Product(bdxtail, cdytail, ti1, ti0);
- Two_Product(cdxtail, bdytail, tj1, tj0);
- Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
- bctt[3] = bctt3;
- bcttlen = 4;
- }
- else {
- bct[0] = 0.0;
- bctlen = 1;
- bctt[0] = 0.0;
- bcttlen = 1;
- }
-
- if (adxtail != 0.0) {
- temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
- axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
- temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- if (bdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a);
- axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
- temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a);
- temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (adytail != 0.0) {
- temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
- aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
- temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
-
- temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a);
- aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
- temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a);
- temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- }
- if ((bdxtail != 0.0) || (bdytail != 0.0)) {
- if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
- Two_Product(cdxtail, ady, ti1, ti0);
- Two_Product(cdx, adytail, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- negate = -cdy;
- Two_Product(adxtail, negate, ti1, ti0);
- negate = -cdytail;
- Two_Product(adx, negate, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
- v[3] = v3;
- catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
-
- Two_Product(cdxtail, adytail, ti1, ti0);
- Two_Product(adxtail, cdytail, tj1, tj0);
- Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
- catt[3] = catt3;
- cattlen = 4;
- }
- else {
- cat[0] = 0.0;
- catlen = 1;
- catt[0] = 0.0;
- cattlen = 1;
- }
-
- if (bdxtail != 0.0) {
- temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
- bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
- temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- if (cdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (adytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a);
- bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
- temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a);
- temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdytail != 0.0) {
- temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
- bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
- temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
-
- temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a);
- bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
- temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a);
- temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- }
- if ((cdxtail != 0.0) || (cdytail != 0.0)) {
- if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
- Two_Product(adxtail, bdy, ti1, ti0);
- Two_Product(adx, bdytail, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
- u[3] = u3;
- negate = -ady;
- Two_Product(bdxtail, negate, ti1, ti0);
- negate = -adytail;
- Two_Product(bdx, negate, tj1, tj0);
- Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
- v[3] = v3;
- abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
-
- Two_Product(adxtail, bdytail, ti1, ti0);
- Two_Product(bdxtail, adytail, tj1, tj0);
- Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
- abtt[3] = abtt3;
- abttlen = 4;
- }
- else {
- abt[0] = 0.0;
- abtlen = 1;
- abtt[0] = 0.0;
- abttlen = 1;
- }
-
- if (cdxtail != 0.0) {
- temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
- cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
- temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- if (adytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (bdytail != 0.0) {
- temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
- temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
-
- temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a);
- cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
- temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a);
- temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- if (cdytail != 0.0) {
- temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
- cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
- temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a);
- temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
-
- temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a);
- cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
- temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a);
- temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b);
- temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
- temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
- finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
- finswap = finnow;
- finnow = finother;
- finother = finswap;
- }
- }
-
- return finnow[finlength - 1];
-}
-
-static double incircle(const double *pa, const double *pb, const double *pc, const double *pd)
-{
- double adx, bdx, cdx, ady, bdy, cdy;
- double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
- double alift, blift, clift;
- double det;
- double permanent, errbound;
-
- adx = pa[0] - pd[0];
- bdx = pb[0] - pd[0];
- cdx = pc[0] - pd[0];
- ady = pa[1] - pd[1];
- bdy = pb[1] - pd[1];
- cdy = pc[1] - pd[1];
-
- bdxcdy = bdx * cdy;
- cdxbdy = cdx * bdy;
- alift = adx * adx + ady * ady;
-
- cdxady = cdx * ady;
- adxcdy = adx * cdy;
- blift = bdx * bdx + bdy * bdy;
-
- adxbdy = adx * bdy;
- bdxady = bdx * ady;
- clift = cdx * cdx + cdy * cdy;
-
- det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
-
- permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift +
- (Absolute(cdxady) + Absolute(adxcdy)) * blift +
- (Absolute(adxbdy) + Absolute(bdxady)) * clift;
- errbound = iccerrboundA * permanent;
- if ((det > errbound) || (-det > errbound)) {
- return det;
- }
-
- return incircleadapt(pa, pb, pc, pd, permanent);
-}
diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc
new file mode 100644
index 00000000000..9b00431391a
--- /dev/null
+++ b/source/blender/blenlib/intern/delaunay_2d.cc
@@ -0,0 +1,2262 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include "gmpxx.h"
+
+#include "BLI_array.hh"
+#include "BLI_double2.hh"
+#include "BLI_linklist.h"
+#include "BLI_mempool.h"
+#include "BLI_mpq2.hh"
+#include "BLI_vector.hh"
+
+#include "BLI_delaunay_2d.h"
+
+namespace BLI {
+namespace CDT {
+
+/* Throughout this file, template argument T will be an
+ * arithmetic-like type, like float, double, or gmp.
+ */
+
+template<typename T> T math_abs(const T v)
+{
+ if (v < 0) {
+ return -v;
+ }
+ else {
+ return v;
+ }
+}
+
+template<> mpq_class math_abs<mpq_class>(const mpq_class v)
+{
+ return abs(v);
+}
+
+template<> double math_abs<double>(const double v)
+{
+ return fabs(v);
+}
+
+template<typename T> double math_to_double(const T UNUSED(v))
+{
+ BLI_assert(false); /* Need implementation for other type. */
+ return 0.0;
+}
+
+template<> double math_to_double<mpq_class>(const mpq_class v)
+{
+ return v.get_d();
+}
+
+template<> double math_to_double<double>(const double v)
+{
+ return v;
+}
+
+template<typename T> class CDTVert;
+template<typename T> class CDTEdge;
+template<typename T> class CDTFace;
+
+template<typename T> class SymEdge {
+ public:
+ SymEdge<T> *next{nullptr}; /* In face, doing CCW traversal of face. */
+ SymEdge<T> *rot{nullptr}; /* CCW around vert. */
+ CDTVert<T> *vert{nullptr}; /* Vert at origin. */
+ CDTEdge<T> *edge{nullptr}; /* Undirected edge this is for. */
+ CDTFace<T> *face{nullptr}; /* Face on left side. */
+
+ SymEdge() = default;
+};
+
+template<typename T> class CDTVert {
+ public:
+ vec2<T> co; /* Coordinate. */
+ SymEdge<T> *symedge{nullptr}; /* Some edge attached to it. */
+ LinkNode *input_ids{nullptr}; /* List of corresponding vertex input ids. */
+ int index{-1}; /* Index into array that cdt keeps. */
+ int merge_to_index{-1}; /* Index of a CDTVert that this has merged to. -1 if no merge. */
+ int visit_index{0}; /* Which visit epoch has this been seen. */
+
+ CDTVert() = default;
+ explicit CDTVert(const vec2<T> &pt);
+};
+
+template<typename T> class CDTEdge {
+ public:
+ LinkNode *input_ids{nullptr}; /* List of input edge ids that this is part of. */
+ SymEdge<T> symedges[2]{SymEdge<T>(), SymEdge<T>()}; /* The directed edges for this edge. */
+ bool in_queue{false}; /* Used in flipping algorithm. */
+
+ CDTEdge() = default;
+};
+
+template<typename T> class CDTFace {
+ public:
+ SymEdge<T> *symedge{
+ nullptr}; /* A symedge in face; only used during output, so only valid then. */
+ LinkNode *input_ids{nullptr}; /* List of input face ids that this is part of. */
+ int visit_index{0}; /* Which visit epoch has this been seen. */
+ bool deleted{false}; /* Marks this face no longer used. */
+ bool in_queue{false}; /* Used in remove_small_features algorithm. */
+
+ CDTFace() = default;
+};
+
+template<typename T> class CDT_state {
+ public:
+ /* Since the following can resize, need to store pointers there, not actual values. */
+ Vector<CDTVert<T> *> verts; /* The verts. */
+ Vector<CDTEdge<T> *> edges; /* The edges. */
+ Vector<CDTFace<T> *> faces; /* The faces. */
+ CDTFace<T> *outer_face; /* Which CDTFace is the outer face. */
+ int input_vert_tot; /* How many verts were in input (will be first in vert_array). */
+ int visit_count; /* Used for visiting things without having to initialized their visit fields. */
+ int face_edge_offset; /* Input edge id where we start numbering the face edges. */
+ T epsilon; /* How close before coords considered equal. */
+ BLI_mempool *listpool; /* Allocations of ListNodes done from this pool. */
+
+ explicit CDT_state(int num_input_verts,
+ int num_input_edges,
+ int num_input_faces,
+ const T epsilon);
+ ~CDT_state()
+ {
+ BLI_mempool_destroy(this->listpool);
+ this->listpool = nullptr;
+ }
+ CDTVert<T> *add_vert(const vec2<T> &pt);
+ CDTEdge<T> *add_edge(CDTVert<T> *v1, CDTVert<T> *v2, CDTFace<T> *fleft, CDTFace<T> *fright);
+ CDTFace<T> *add_face();
+ CDTEdge<T> *add_vert_to_symedge_edge(CDTVert<T> *v, SymEdge<T> *se);
+ CDTEdge<T> *add_diagonal(SymEdge<T> *s1, SymEdge<T> *s2);
+ CDTEdge<T> *connect_separate_parts(SymEdge<T> *se1, SymEdge<T> *se2);
+ CDTEdge<T> *split_edge(SymEdge<T> *se, T lambda);
+ void delete_edge(SymEdge<T> *se);
+ CDTVert<T> *get_vert_resolve_merge(int i);
+};
+
+#define DEBUG_CDT
+#ifdef DEBUG_CDT
+/* Some functions to aid in debugging. */
+template<typename T> const std::string vertname(const CDTVert<T> *v)
+{
+ std::stringstream ss;
+ ss << "[" << v->index << "]";
+ return ss.str();
+}
+
+/* Abbreviated pointer value is easier to read in dumps. */
+static const std::string trunc_ptr(const void *p)
+{
+ std::stringstream ss;
+ ss << std::hex << (POINTER_AS_INT(p) & 0xFFFF);
+ return ss.str();
+}
+
+template<typename T> const std::string sename(const SymEdge<T> *se)
+{
+ std::stringstream ss;
+ ss << "{" << trunc_ptr(se) << "}";
+ return ss.str();
+}
+
+template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> &se)
+{
+ if (se.next) {
+ os << vertname(se.vert) << "(" << se.vert->co << "->" << se.next->vert->co << ")"
+ << vertname(se.next->vert);
+ }
+ else {
+ os << vertname(se.vert) << "(" << se.vert->co << "->NULL)";
+ }
+ return os;
+}
+
+template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> *se)
+{
+ os << *se;
+ return os;
+}
+
+template<typename T> const std::string short_se_dump(const SymEdge<T> *se)
+{
+ if (se == nullptr) {
+ return std::string("NULL");
+ }
+ else {
+ return vertname(se->vert) +
+ (se->next == nullptr ? std::string("[NULL]") : vertname(se->next->vert));
+ }
+}
+
+template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state<T> &cdt)
+{
+ os << "\nCDT\n\nVERTS\n";
+ for (const CDTVert<T> *v : cdt.verts) {
+ os << vertname(v) << " " << trunc_ptr(v) << ": " << v->co
+ << " symedge=" << trunc_ptr(v->symedge);
+ if (v->merge_to_index == -1) {
+ os << "\n";
+ }
+ else {
+ os << " merge to " << vertname(cdt.verts[v->merge_to_index]) << "\n";
+ }
+ const SymEdge<T> *se = v->symedge;
+ int cnt = 0;
+ if (se) {
+ os << " edges out:\n";
+ do {
+ if (se->next == NULL) {
+ os << " [NULL] next/rot symedge, se=" << trunc_ptr(se) << "\n";
+ break;
+ }
+ if (se->next->next == NULL) {
+ os << " [NULL] next-next/rot symedge, se=" << trunc_ptr(se) << "\n";
+ break;
+ }
+ const CDTVert<T> *vother = sym(se)->vert;
+ os << " " << vertname(vother) << "(e=" << trunc_ptr(se->edge)
+ << ", se=" << trunc_ptr(se) << ")\n";
+ se = se->rot;
+ cnt++;
+ } while (se != v->symedge && cnt < 25);
+ os << "\n";
+ }
+ }
+ os << "\nEDGES\n";
+ for (const CDTEdge<T> *e : cdt.edges) {
+ if (e->symedges[0].next == nullptr) {
+ continue;
+ }
+ os << trunc_ptr(&e) << ":\n";
+ for (int i = 0; i < 2; ++i) {
+ const SymEdge<T> *se = &e->symedges[i];
+ os << " se[" << i << "] @" << trunc_ptr(se) << " next=" << trunc_ptr(se->next)
+ << ", rot=" << trunc_ptr(se->rot) << ", vert=" << trunc_ptr(se->vert) << " "
+ << vertname(se->vert) << " " << se->vert->co << ", edge=" << trunc_ptr(se->edge)
+ << ", face=" << trunc_ptr(se->face) << "\n";
+ }
+ }
+ os << "\nFACES\n";
+ os << "outer_face=" << trunc_ptr(cdt.outer_face) << "\n";
+ /* Only after prepare_output do faces have non-null symedges. */
+ if (cdt.outer_face->symedge != nullptr) {
+ for (const CDTFace<T> *f : cdt.faces) {
+ if (!f->deleted) {
+ os << trunc_ptr(f) << " symedge=" << trunc_ptr(f->symedge) << "\n";
+ }
+ }
+ }
+ return os;
+}
+
+template<typename T> void cdt_draw(const std::string &label, const CDT_state<T> &cdt)
+{
+ static bool append = false; /* Will be set to true after first call. */
+ constexpr const char *drawfile = "/home/howard/debug_draw.html";
+ constexpr int max_draw_width = 1800;
+ constexpr int max_draw_height = 1600;
+ constexpr int thin_line = 1;
+ constexpr int thick_line = 4;
+ constexpr int vert_radius = 3;
+ constexpr bool draw_vert_labels = true;
+ constexpr bool draw_edge_labels = false;
+
+ if (cdt.verts.size() == 0) {
+ return;
+ }
+ vec2<double> vmin(1e10, 1e10);
+ vec2<double> vmax(-1e10, -1e10);
+ for (const CDTVert<T> *v : cdt.verts) {
+ for (int i = 0; i < 2; ++i) {
+ double dvi = math_to_double(v->co[i]);
+ if (dvi < vmin[i]) {
+ vmin[i] = dvi;
+ }
+ if (dvi > vmax[i]) {
+ vmax[i] = dvi;
+ }
+ }
+ }
+ double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * 0.05;
+ double minx = vmin.x - draw_margin;
+ double maxx = vmax.x + draw_margin;
+ double miny = vmin.y - draw_margin;
+ double maxy = vmax.y + draw_margin;
+
+ double width = maxx - minx;
+ double height = maxy - miny;
+ double aspect = height / width;
+ int view_width = max_draw_width;
+ int view_height = static_cast<int>(view_width * aspect);
+ if (view_height > max_draw_height) {
+ view_height = max_draw_height;
+ view_width = static_cast<int>(view_height / aspect);
+ }
+ double scale = view_width / width;
+
+# define SX(x) ((math_to_double(x) - minx) * scale)
+# define SY(y) ((maxy - math_to_double(y)) * scale)
+
+ std::ofstream f;
+ if (append) {
+ f.open(drawfile, std::ios_base::app);
+ }
+ else {
+ f.open(drawfile);
+ }
+ if (!f) {
+ std::cout << "Could not open file " << drawfile << "\n";
+ return;
+ }
+
+ f << "<div>" << label << "</div>\n<div>\n"
+ << "<svg version=\"1.1\" "
+ "xmlns=\"http://www.w3.org/2000/svg\" "
+ "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
+ "xml:space=\"preserve\"\n"
+ << "width=\"" << view_width << "\" height=\"" << view_height << "\">n";
+
+ for (const CDTEdge<T> *e : cdt.edges) {
+ if (e->symedges[0].next == nullptr) {
+ continue;
+ }
+ const CDTVert<T> *u = e->symedges[0].vert;
+ const CDTVert<T> *v = e->symedges[1].vert;
+ const vec2<T> &uco = u->co;
+ const vec2<T> &vco = v->co;
+ int strokew = e->input_ids == nullptr ? thin_line : thick_line;
+ f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\""
+ << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\""
+ << SY(vco[1]) << "\">\n";
+ f << " <title>" << vertname(u) << vertname(v) << "</title>\n";
+ f << "</line>\n";
+ if (draw_edge_labels) {
+ f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1]))
+ << "\" font-size=\"small\">";
+ f << vertname(u) << vertname(v) << sename(&e->symedges[0]) << sename(&e->symedges[1])
+ << "</text>\n";
+ }
+ }
+
+ int i = 0;
+ for (const CDTVert<T> *v : cdt.verts) {
+ f << "<circle fill=\"black\" cx=\"" << SX(v->co[0]) << "\" cy=\"" << SY(v->co[1]) << "\" r=\""
+ << vert_radius << "\">\n";
+ f << " <title>[" << i << "]" << v->co << "</title>\n";
+ f << "</circle>\n";
+ if (draw_vert_labels) {
+ f << "<text x=\"" << SX(v->co[0]) + vert_radius << "\" y=\"" << SY(v->co[1]) - vert_radius
+ << "\" font-size=\"small\">[" << i << "]</text>\n";
+ }
+ ++i;
+ }
+
+ append = true;
+# undef SX
+# undef SY
+}
+#endif
+
+/* Return other SymEdge for same CDTEdge as se. */
+template<typename T> inline SymEdge<T> *sym(const SymEdge<T> *se)
+{
+ return se->next->rot;
+}
+
+/* Return SymEdge whose next is se. */
+template<typename T> inline SymEdge<T> *prev(const SymEdge<T> *se)
+{
+ return se->rot->next->rot;
+}
+
+/*
+ * Return true if a -- b -- c are in that order, assuming they are on a straight line according to
+ * orient2d and we know the order is either `abc` or `bac`.
+ * This means `ab . ac` and `bc . ac` must both be non-negative. */
+template<typename T> bool in_line(const vec2<T> &a, const vec2<T> &b, const vec2<T> &c)
+{
+ vec2<T> ab = b - a;
+ vec2<T> bc = c - b;
+ vec2<T> ac = c - a;
+ if (vec2<T>::dot(ab, ac) < 0) {
+ return false;
+ }
+ return vec2<T>::dot(bc, ac) >= 0;
+}
+
+template<typename T> CDTVert<T>::CDTVert(const vec2<T> &pt)
+{
+ this->co = pt;
+ this->input_ids = nullptr;
+ this->symedge = nullptr;
+ this->index = -1;
+ this->merge_to_index = -1;
+ this->visit_index = 0;
+}
+
+template<typename T> CDTVert<T> *CDT_state<T>::add_vert(const vec2<T> &pt)
+{
+ CDTVert<T> *v = new CDTVert<T>(pt); /* TODO: use pooled or arena allocator. */
+ int index = static_cast<int>(this->verts.append_and_get_index(v));
+ v->index = index;
+ return v;
+}
+
+template<typename T>
+CDTEdge<T> *CDT_state<T>::add_edge(CDTVert<T> *v1,
+ CDTVert<T> *v2,
+ CDTFace<T> *fleft,
+ CDTFace<T> *fright)
+{
+ CDTEdge<T> *e = new CDTEdge<T>(); /* TODO: use pooled or arena allocator. */
+ this->edges.append(e);
+ SymEdge<T> *se = &e->symedges[0];
+ SymEdge<T> *sesym = &e->symedges[1];
+ se->edge = sesym->edge = e;
+ se->face = fleft;
+ sesym->face = fright;
+ se->vert = v1;
+ if (v1->symedge == nullptr) {
+ v1->symedge = se;
+ }
+ sesym->vert = v2;
+ if (v2->symedge == nullptr) {
+ v2->symedge = sesym;
+ }
+ se->next = sesym->next = se->rot = sesym->rot = nullptr;
+ return e;
+}
+
+template<typename T> CDTFace<T> *CDT_state<T>::add_face()
+{
+ CDTFace<T> *f = new CDTFace<T>(); /* TODO: use pooled or arena allocator. */
+ this->faces.append(f);
+ return f;
+}
+
+template<typename T> inline CDTVert<T> *CDT_state<T>::get_vert_resolve_merge(int i)
+{
+ CDTVert<T> *v = this->verts[i];
+ if (v->merge_to_index != -1) {
+ v = this->verts[v->merge_to_index];
+ }
+ return v;
+}
+
+template<typename T>
+CDT_state<T>::CDT_state(int num_input_verts,
+ int num_input_edges,
+ int num_input_faces,
+ const T epsilon)
+{
+ this->input_vert_tot = num_input_verts;
+ /* These reserves are just guesses; OK if they aren't exactly right since vectors will resize. */
+ this->verts.reserve(2 * num_input_verts);
+ this->edges.reserve(3 * num_input_verts + 2 * num_input_edges + 5 * num_input_faces);
+ this->faces.reserve(2 * num_input_verts + 2 * num_input_edges + 2 * num_input_faces);
+ this->listpool = BLI_mempool_create(
+ sizeof(LinkNode), 128 + 4 * num_input_verts, 128 + num_input_verts, 0);
+ this->outer_face = this->add_face();
+ this->epsilon = epsilon;
+ this->visit_count = 0;
+}
+
+static bool id_in_list(const LinkNode *id_list, int id)
+{
+ const LinkNode *ln;
+
+ for (ln = id_list; ln; ln = ln->next) {
+ if (POINTER_AS_INT(ln->link) == id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Is any id in (range_start, range_start+1, ... , range_end) in id_list? */
+static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end)
+{
+ const LinkNode *ln;
+ int id;
+
+ for (ln = id_list; ln; ln = ln->next) {
+ id = POINTER_AS_INT(ln->link);
+ if (id >= range_start && id <= range_end) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template<typename T> void add_to_input_ids(LinkNode **dst, int input_id, CDT_state<T> *cdt)
+{
+ if (!id_in_list(*dst, input_id)) {
+ BLI_linklist_prepend_pool(dst, POINTER_FROM_INT(input_id), cdt->listpool);
+ }
+}
+
+template<typename T>
+void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state<T> *cdt)
+{
+ const LinkNode *ln;
+
+ for (ln = src; ln; ln = ln->next) {
+ add_to_input_ids(dst, POINTER_AS_INT(ln->link), cdt);
+ }
+}
+
+template<typename T> inline bool is_border_edge(const CDTEdge<T> *e, const CDT_state<T> *cdt)
+{
+ return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face;
+}
+
+template<typename T> inline bool is_constrained_edge(const CDTEdge<T> *e)
+{
+ return e->input_ids != NULL;
+}
+
+template<typename T> inline bool is_deleted_edge(const CDTEdge<T> *e)
+{
+ return e->symedges[0].next == NULL;
+}
+
+template<typename T> inline bool is_original_vert(const CDTVert<T> *v, CDT_state<T> *cdt)
+{
+ return (v->index < cdt->input_vert_tot);
+}
+
+/* Return the Symedge that goes from v1 to v2, if it exists, else return nullptr. */
+template<typename T>
+SymEdge<T> *find_symedge_between_verts(const CDTVert<T> *v1, const CDTVert<T> *v2)
+{
+ SymEdge<T> *t = v1->symedge;
+ SymEdge<T> *tstart = t;
+ do {
+ if (t->next->vert == v2) {
+ return t;
+ }
+ } while ((t = t->rot) != tstart);
+ return nullptr;
+}
+
+/* Return the SymEdge attached to v that has face f, if it exists, else return nullptr. */
+template<typename T> SymEdge<T> *find_symedge_with_face(const CDTVert<T> *v, const CDTFace<T> *f)
+{
+ SymEdge<T> *t = v->symedge;
+ SymEdge<T> *tstart = t;
+ do {
+ if (t->face == f) {
+ return t;
+ }
+ } while ((t = t->rot) != tstart);
+ return nullptr;
+}
+
+/* Is there already an edge between a and b? */
+template<typename T> inline bool exists_edge(const CDTVert<T> *v1, const CDTVert<T> *v2)
+{
+ return find_symedge_between_verts(v1, v2) != nullptr;
+}
+
+/* Is the vertex v incident on face f? */
+template<typename T> bool vert_touches_face(const CDTVert<T> *v, const CDTFace<T> *f)
+{
+ SymEdge<T> *se = v->symedge;
+ do {
+ if (se->face == f) {
+ return true;
+ }
+ } while ((se = se->rot) != v->symedge);
+ return false;
+}
+
+/*
+ * Assume s1 and s2 are both SymEdges in a face with > 3 sides,
+ * and one is not the next of the other.
+ * Add an edge from s1->v to s2->v, splitting the face in two.
+ * The original face will continue to be associated with the subface
+ * that has s1, and a new face will be made for s2's new face.
+ * Return the new diagonal's CDTEdge *.
+ */
+template<typename T> CDTEdge<T> *CDT_state<T>::add_diagonal(SymEdge<T> *s1, SymEdge<T> *s2)
+{
+ CDTFace<T> *fold = s1->face;
+ CDTFace<T> *fnew = this->add_face();
+ SymEdge<T> *s1prev = prev(s1);
+ SymEdge<T> *s1prevsym = sym(s1prev);
+ SymEdge<T> *s2prev = prev(s2);
+ SymEdge<T> *s2prevsym = sym(s2prev);
+ CDTEdge<T> *ediag = this->add_edge(s1->vert, s2->vert, fnew, fold);
+ SymEdge<T> *sdiag = &ediag->symedges[0];
+ SymEdge<T> *sdiagsym = &ediag->symedges[1];
+ sdiag->next = s2;
+ sdiagsym->next = s1;
+ s2prev->next = sdiagsym;
+ s1prev->next = sdiag;
+ s1->rot = sdiag;
+ sdiag->rot = s1prevsym;
+ s2->rot = sdiagsym;
+ sdiagsym->rot = s2prevsym;
+ for (SymEdge<T> *se = s2; se != sdiag; se = se->next) {
+ se->face = fnew;
+ }
+ add_list_to_input_ids(&fnew->input_ids, fold->input_ids, this);
+ return ediag;
+}
+
+template<typename T>
+CDTEdge<T> *CDT_state<T>::add_vert_to_symedge_edge(CDTVert<T> *v, SymEdge<T> *se)
+{
+ SymEdge<T> *se_rot = se->rot;
+ SymEdge<T> *se_rotsym = sym(se_rot);
+ CDTEdge<T> *e = this->add_edge(v, se->vert, se->face, se->face);
+ SymEdge<T> *new_se = &e->symedges[0];
+ SymEdge<T> *new_se_sym = &e->symedges[1];
+ new_se->next = se;
+ new_se_sym->next = new_se;
+ new_se->rot = new_se;
+ new_se_sym->rot = se_rot;
+ se->rot = new_se_sym;
+ se_rotsym->next = new_se_sym;
+ return e;
+}
+
+/* Connect the verts of se1 and se2, assuming that currently those two SymEdges are on
+ * the outer boundary (have face == outer_face) of two components that are isolated from
+ * each other.
+ */
+template<typename T>
+CDTEdge<T> *CDT_state<T>::connect_separate_parts(SymEdge<T> *se1, SymEdge<T> *se2)
+{
+ BLI_assert(se1->face == this->outer_face && se2->face == this->outer_face);
+ SymEdge<T> *se1_rot = se1->rot;
+ SymEdge<T> *se1_rotsym = sym(se1_rot);
+ SymEdge<T> *se2_rot = se2->rot;
+ SymEdge<T> *se2_rotsym = sym(se2_rot);
+ CDTEdge<T> *e = this->add_edge(se1->vert, se2->vert, this->outer_face, this->outer_face);
+ SymEdge<T> *new_se = &e->symedges[0];
+ SymEdge<T> *new_se_sym = &e->symedges[1];
+ new_se->next = se2;
+ new_se_sym->next = se1;
+ new_se->rot = se1_rot;
+ new_se_sym->rot = se2_rot;
+ se1->rot = new_se;
+ se2->rot = new_se_sym;
+ se1_rotsym->next = new_se;
+ se2_rotsym->next = new_se_sym;
+ return e;
+}
+
+/*
+ * Split se at fraction lambda,
+ * and return the new CDTEdge that is the new second half.
+ * Copy the edge input_ids into the new one.
+ */
+template<typename T> CDTEdge<T> *CDT_state<T>::split_edge(SymEdge<T> *se, T lambda)
+{
+ /* Split e at lambda. */
+ const vec2<T> *a = &se->vert->co;
+ const vec2<T> *b = &se->next->vert->co;
+ SymEdge<T> *sesym = sym(se);
+ SymEdge<T> *sesymprev = prev(sesym);
+ SymEdge<T> *sesymprevsym = sym(sesymprev);
+ SymEdge<T> *senext = se->next;
+ CDTVert<T> *v = this->add_vert(vec2<T>::interpolate(*a, *b, lambda));
+ CDTEdge<T> *e = this->add_edge(v, se->next->vert, se->face, sesym->face);
+ sesym->vert = v;
+ SymEdge<T> *newse = &e->symedges[0];
+ SymEdge<T> *newsesym = &e->symedges[1];
+ se->next = newse;
+ newsesym->next = sesym;
+ newse->next = senext;
+ newse->rot = sesym;
+ sesym->rot = newse;
+ senext->rot = newsesym;
+ newsesym->rot = sesymprevsym;
+ sesymprev->next = newsesym;
+ if (newsesym->vert->symedge == sesym) {
+ newsesym->vert->symedge = newsesym;
+ }
+ add_list_to_input_ids(&e->input_ids, se->edge->input_ids, this);
+ return e;
+}
+
+/*
+ * Delete an edge from the structure. The new combined face on either side of
+ * the deleted edge will be the one that was e's face.
+ * There will be now an unused face, marked by setting its deleted flag,
+ * and an unused #CDTEdge, marked by setting the next and rot pointers of
+ * its SymEdges to NULL.
+ * <pre>
+ * . v2 .
+ * / \ / \
+ * /f|j\ / \
+ * / | \ / \
+ * |
+ * A | B A
+ * \ e| / \ /
+ * \ | / \ /
+ * \h|i/ \ /
+ * . v1 .
+ * </pre>
+ * Also handle variant cases where one or both ends
+ * are attached only to e.
+ */
+template<typename T> void CDT_state<T>::delete_edge(SymEdge<T> *se)
+{
+ SymEdge<T> *sesym = sym(se);
+ CDTVert<T> *v1 = se->vert;
+ CDTVert<T> *v2 = sesym->vert;
+ CDTFace<T> *aface = se->face;
+ CDTFace<T> *bface = sesym->face;
+ SymEdge<T> *f = se->next;
+ SymEdge<T> *h = prev(se);
+ SymEdge<T> *i = sesym->next;
+ SymEdge<T> *j = prev(sesym);
+ SymEdge<T> *jsym = sym(j);
+ SymEdge<T> *hsym = sym(h);
+ bool v1_isolated = (i == se);
+ bool v2_isolated = (f == sesym);
+
+ if (!v1_isolated) {
+ h->next = i;
+ i->rot = hsym;
+ }
+ if (!v2_isolated) {
+ j->next = f;
+ f->rot = jsym;
+ }
+ if (!v1_isolated && !v2_isolated && aface != bface) {
+ for (SymEdge<T> *k = i; k != f; k = k->next) {
+ k->face = aface;
+ }
+ }
+
+ /* If e was representative symedge for v1 or v2, fix that. */
+ if (v1_isolated) {
+ v1->symedge = nullptr;
+ }
+ else if (v1->symedge == se) {
+ v1->symedge = i;
+ }
+ if (v2_isolated) {
+ v2->symedge = nullptr;
+ }
+ else if (v2->symedge == sesym) {
+ v2->symedge = f;
+ }
+
+ /* Mark SymEdge as deleted by setting all its pointers to NULL. */
+ se->next = se->rot = nullptr;
+ sesym->next = sesym->rot = nullptr;
+ if (!v1_isolated && !v2_isolated && aface != bface) {
+ bface->deleted = true;
+ if (this->outer_face == bface) {
+ this->outer_face = aface;
+ }
+ }
+}
+
+template<typename T> class SiteInfo {
+ public:
+ CDTVert<T> *v;
+ int orig_index;
+};
+
+/* Compare function for lexicographic sort: x, then y, then index. */
+template<typename T> bool site_lexicographic_sort(const SiteInfo<T> &a, const SiteInfo<T> &b)
+{
+ const vec2<T> &co_a = a.v->co;
+ const vec2<T> &co_b = b.v->co;
+ if (co_a[0] < co_b[0]) {
+ return true;
+ }
+ else if (co_a[0] > co_b[0]) {
+ return false;
+ }
+ else if (co_a[1] < co_b[1]) {
+ return true;
+ }
+ else if (co_a[1] > co_b[1]) {
+ return false;
+ }
+ else {
+ return a.orig_index < b.orig_index;
+ }
+}
+
+/* Find series of equal vertices in the sorted sites array
+ * and use the vertice's merge_to_index to indicate that
+ * all vertices after the first merge to the first.
+ */
+template<typename T> void find_site_merges(Array<SiteInfo<T>> &sites)
+{
+ int n = static_cast<int>(sites.size());
+ for (int i = 0; i < n - 1; ++i) {
+ int j = i + 1;
+ while (j < n && sites[j].v->co == sites[i].v->co) {
+ sites[j].v->merge_to_index = sites[i].orig_index;
+ ++j;
+ }
+ if (j - i > 1) {
+ i = j - 1; /* j-1 because loop head will add another 1. */
+ }
+ }
+}
+
+template<typename T> inline bool vert_left_of_symedge(CDTVert<T> *v, SymEdge<T> *se)
+{
+ return vec2<T>::orient2d(v->co, se->vert->co, se->next->vert->co) > 0;
+}
+
+template<typename T> inline bool vert_right_of_symedge(CDTVert<T> *v, SymEdge<T> *se)
+{
+ return vec2<T>::orient2d(v->co, se->next->vert->co, se->vert->co) > 0;
+}
+
+/* Is se above basel? */
+template<typename T>
+inline bool dc_tri_valid(SymEdge<T> *se, SymEdge<T> *basel, SymEdge<T> *basel_sym)
+{
+ return vec2<T>::orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0;
+}
+
+/* Delaunay triangulate sites[start} to sites[end-1].
+ * Assume sites are lexicographically sorted by coordinate.
+ * Return SymEdge of ccw convex hull at left-most point in *r_le
+ * and that of right-most point of cw convex null in *r_re.
+ */
+template<typename T>
+void dc_tri(CDT_state<T> *cdt,
+ Array<SiteInfo<T>> &sites,
+ int start,
+ int end,
+ SymEdge<T> **r_le,
+ SymEdge<T> **r_re)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "DC_TRI start=" << start << " end=" << end << "\n";
+ }
+ int n = end - start;
+ if (n <= 1) {
+ *r_le = nullptr;
+ *r_re = nullptr;
+ return;
+ }
+
+ /* Base case: if n <= 3, triangulate directly. */
+ if (n <= 3) {
+ CDTVert<T> *v1 = sites[start].v;
+ CDTVert<T> *v2 = sites[start + 1].v;
+ CDTEdge<T> *ea = cdt->add_edge(v1, v2, cdt->outer_face, cdt->outer_face);
+ ea->symedges[0].next = &ea->symedges[1];
+ ea->symedges[1].next = &ea->symedges[0];
+ ea->symedges[0].rot = &ea->symedges[0];
+ ea->symedges[1].rot = &ea->symedges[1];
+ if (n == 2) {
+ *r_le = &ea->symedges[0];
+ *r_re = &ea->symedges[1];
+ return;
+ }
+ CDTVert<T> *v3 = sites[start + 2].v;
+ CDTEdge<T> *eb = cdt->add_vert_to_symedge_edge(v3, &ea->symedges[1]);
+ int orient = vec2<T>::orient2d(v1->co, v2->co, v3->co);
+ if (orient > 0) {
+ cdt->add_diagonal(&eb->symedges[0], &ea->symedges[0]);
+ *r_le = &ea->symedges[0];
+ *r_re = &eb->symedges[0];
+ }
+ else if (orient < 0) {
+ cdt->add_diagonal(&ea->symedges[0], &eb->symedges[0]);
+ *r_le = ea->symedges[0].rot;
+ *r_re = eb->symedges[0].rot;
+ }
+ else {
+ /* Collinear points. Just return a line. */
+ *r_le = &ea->symedges[0];
+ *r_re = &eb->symedges[0];
+ }
+ return;
+ }
+ /* Recursive case. Do left (L) and right (R) halves seperately, then join. */
+ int n2 = n / 2;
+ BLI_assert(n2 >= 2 && end - (start + n2) >= 2);
+ SymEdge<T> *ldo, *ldi, *rdi, *rdo;
+ dc_tri(cdt, sites, start, start + n2, &ldo, &ldi);
+ dc_tri(cdt, sites, start + n2, end, &rdi, &rdo);
+ if (dbg_level > 0) {
+ std::cout << "\nDC_TRI merge step for start=" << start << ", end=" << end << "\n";
+ std::cout << "ldo " << ldo << "\n"
+ << "ldi " << ldi << "\n"
+ << "rdi " << rdi << "\n"
+ << "rdo " << rdo << "\n";
+ if (dbg_level > 1) {
+ std::string lab = "dc_tri (" + std::to_string(start) + "," + std::to_string(start + n2) +
+ ")(" + std::to_string(start + n2) + "," + std::to_string(end) + ")";
+ cdt_draw(lab, *cdt);
+ }
+ }
+ /* Find lower common tangent of L and R. */
+ for (;;) {
+ if (vert_left_of_symedge(rdi->vert, ldi)) {
+ ldi = ldi->next;
+ }
+ else if (vert_right_of_symedge(ldi->vert, rdi)) {
+ rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */
+ }
+ else {
+ break;
+ }
+ }
+ if (dbg_level > 0) {
+ std::cout << "common lower tangent in between\n"
+ << "rdi " << rdi << "\n"
+ << "ldi" << ldi << "\n";
+ }
+
+ CDTEdge<T> *ebasel = cdt->connect_separate_parts(sym(rdi)->next, ldi);
+ SymEdge<T> *basel = &ebasel->symedges[0];
+ SymEdge<T> *basel_sym = &ebasel->symedges[1];
+ if (dbg_level > 1) {
+ std::cout << "basel " << basel;
+ cdt_draw("after basel made", *cdt);
+ }
+ if (ldi->vert == ldo->vert) {
+ ldo = basel_sym;
+ }
+ if (rdi->vert == rdo->vert) {
+ rdo = basel;
+ }
+
+ /* Merge loop. */
+ for (;;) {
+ /* Locate the first point lcand->next->vert encountered by rising bubble,
+ * and delete L edges out of basel->next->vert that fail the circle test. */
+ SymEdge<T> *lcand = basel_sym->rot;
+ SymEdge<T> *rcand = basel_sym->next;
+ if (dbg_level > 1) {
+ std::cout << "\ntop of merge loop\n";
+ std::cout << "lcand " << lcand << "\n"
+ << "rcand " << rcand << "\n"
+ << "basel " << basel << "\n";
+ }
+ if (dc_tri_valid(lcand, basel, basel_sym)) {
+ if (dbg_level > 1) {
+ std::cout << "found valid lcand\n";
+ std::cout << " lcand" << lcand << "\n";
+ }
+ while (vec2<T>::incircle(basel_sym->vert->co,
+ basel->vert->co,
+ lcand->next->vert->co,
+ lcand->rot->next->vert->co) > 0.0) {
+ if (dbg_level > 1) {
+ std::cout << "incircle says to remove lcand\n";
+ std::cout << " lcand" << lcand << "\n";
+ }
+ SymEdge<T> *t = lcand->rot;
+ cdt->delete_edge(sym(lcand));
+ lcand = t;
+ }
+ }
+ /* Symmetrically, locate first R point to be hit and delete R edges. */
+ if (dc_tri_valid(rcand, basel, basel_sym)) {
+ if (dbg_level > 1) {
+ std::cout << "found valid rcand\n";
+ std::cout << " rcand" << rcand << "\n";
+ }
+ while (vec2<T>::incircle(basel_sym->vert->co,
+ basel->vert->co,
+ rcand->next->vert->co,
+ sym(rcand)->next->next->vert->co) > 0.0) {
+ if (dbg_level > 0) {
+ std::cout << "incircle says to remove rcand\n";
+ std::cout << " rcand" << rcand << "\n";
+ }
+ SymEdge<T> *t = sym(rcand)->next;
+ cdt->delete_edge(rcand);
+ rcand = t;
+ }
+ }
+ /* If both lcand and rcand are invalid, then basel is the common upper tangent. */
+ bool valid_lcand = dc_tri_valid(lcand, basel, basel_sym);
+ bool valid_rcand = dc_tri_valid(rcand, basel, basel_sym);
+ if (dbg_level > 0) {
+ std::cout << "after bubbling up, valid_lcand=" << valid_lcand
+ << ", valid_rand=" << valid_rcand << "\n";
+ std::cout << "lcand" << lcand << "\n"
+ << "rcand" << rcand << "\n";
+ }
+ if (!valid_lcand && !valid_rcand) {
+ break;
+ }
+ /* The next cross edge to be connected is to either lcand->next->vert or rcand->next->vert;
+ * if both are valid, choose the appropriate one using the incircle test.
+ */
+ if (!valid_lcand || (valid_rcand && vec2<T>::incircle(lcand->next->vert->co,
+ lcand->vert->co,
+ rcand->vert->co,
+ rcand->next->vert->co) > 0)) {
+ if (dbg_level > 0) {
+ std::cout << "connecting rcand\n";
+ std::cout << " se1=basel_sym" << basel_sym << "\n";
+ std::cout << " se2=rcand->next" << rcand->next << "\n";
+ }
+ ebasel = cdt->add_diagonal(rcand->next, basel_sym);
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "connecting lcand\n";
+ std::cout << " se1=sym(lcand)" << sym(lcand) << "\n";
+ std::cout << " se2=basel_sym->next" << basel_sym->next << "\n";
+ }
+ ebasel = cdt->add_diagonal(basel_sym->next, sym(lcand));
+ }
+ basel = &ebasel->symedges[0];
+ basel_sym = &ebasel->symedges[1];
+ BLI_assert(basel_sym->face == cdt->outer_face);
+ if (dbg_level > 2) {
+ cdt_draw("after adding new crossedge", *cdt);
+ }
+ }
+ *r_le = ldo;
+ *r_re = rdo;
+ BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face);
+}
+
+/* Guibas-Stolfi Divide-and_Conquer algorithm. */
+template<typename T> void dc_triangulate(CDT_state<T> *cdt, Array<SiteInfo<T>> &sites)
+{
+ /* Compress sites in place to eliminted verts that merge to others. */
+ int i = 0;
+ int j = 0;
+ int nsites = static_cast<int>(sites.size());
+ while (j < nsites) {
+ /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */
+ sites[i] = sites[j++];
+ if (sites[i].v->merge_to_index < 0) {
+ i++;
+ }
+ }
+ int n = i;
+ if (n == 0) {
+ return;
+ }
+ SymEdge<T> *le, *re;
+ dc_tri(cdt, sites, 0, n, &le, &re);
+}
+
+/*
+ * Do a Delaunay Triangulation of the points in cdt.verts.
+ * This is only a first step in the Constrained Delaunay triangulation,
+ * because it doesn't yet deal with the segment constraints.
+ * The algorithm used is the Divide & Conquer algorithm from the
+ * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision
+ * and the Computation of Voronoi Diagrams" paper.
+ * The data structure here is similar to but not exactly the same as
+ * the quad-edge structure described in that paper.
+ * If T is not exact arithmetic, incircle and ccw tests are done using
+ * Shewchuk's exact primitives, so that this routine is robust.
+ *
+ * As a preprocessing step, we want to merge all vertices that the same.
+ * This is accomplished by lexicographically
+ * sorting the coordinates first (which is needed anyway for the D&C algorithm).
+ * The CDTVerts with merge_to_index not equal to -1 are after this regarded
+ * as having been merged into the vertex with the corresponding index.
+ */
+template<typename T> void initial_triangulation(CDT_state<T> *cdt)
+{
+ int n = static_cast<int>(cdt->verts.size());
+ if (n <= 1) {
+ return;
+ }
+ Array<SiteInfo<T>> sites(n);
+ for (int i = 0; i < n; ++i) {
+ sites[i].v = cdt->verts[i];
+ sites[i].orig_index = i;
+ }
+ std::sort(sites.begin(), sites.end(), site_lexicographic_sort<T>);
+ find_site_merges(sites);
+ dc_triangulate(cdt, sites);
+}
+
+/*
+ * Re-triangulates, assuring constrained delaunay condition,
+ * the pseudo-polygon that cycles from se.
+ * "pseudo" because a vertex may be repeated.
+ * See Anglada paper, "An Improved incremental algorithm
+ * for constructing restricted Delaunay triangulations".
+ */
+template<typename T> static void re_delaunay_triangulate(CDT_state<T> *cdt, SymEdge<T> *se)
+{
+ if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) {
+ return;
+ }
+ /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */
+ int count = 1;
+ for (SymEdge<T> *ss = se->next; ss != se; ss = ss->next) {
+ count++;
+ }
+ if (count <= 3) {
+ return;
+ }
+ /* First and last are the SymEdges whose verts are first and last off of base,
+ * continuing from 'se'. */
+ SymEdge<T> *first = se->next->next;
+ /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */
+ CDTVert<T> *a = se->vert;
+ CDTVert<T> *b = se->next->vert;
+ CDTVert<T> *c = first->vert;
+ SymEdge<T> *cse = first;
+ for (SymEdge<T> *ss = first->next; ss != se; ss = ss->next) {
+ CDTVert<T> *v = ss->vert;
+ if (vec2<T>::incircle(a->co, b->co, c->co, v->co) > 0) {
+ c = v;
+ cse = ss;
+ }
+ }
+ /* Add diagonals necessary to make abc a triangle. */
+ CDTEdge<T> *ebc = nullptr;
+ CDTEdge<T> *eca = nullptr;
+ if (!exists_edge(b, c)) {
+ ebc = cdt->add_diagonal(se->next, cse);
+ }
+ if (!exists_edge(c, a)) {
+ eca = cdt->add_diagonal(cse, se);
+ }
+ /* Now recurse. */
+ if (ebc) {
+ re_delaunay_triangulate(cdt, &ebc->symedges[1]);
+ }
+ if (eca) {
+ re_delaunay_triangulate(cdt, &eca->symedges[1]);
+ }
+}
+
+template<typename T> inline int tri_orient(const SymEdge<T> *t)
+{
+ return vec2<T>::orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co);
+}
+
+/*
+ * The CrossData class defines either an endpoint or an intermediate point
+ * in the path we will take to insert an edge constraint.
+ * Each such point will either be
+ * (a) a vertex or
+ * (b) a fraction lambda (0 < lambda < 1) along some SymEdge.]
+ *
+ * In general, lambda=0 indicates case a and lambda != 0 indicates case be.
+ * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing,
+ * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing.
+ * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL.
+ *
+ * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the SymEdge from
+ * 'vert' that has as face the one that you go through to get to this vertex. If you go exactly
+ * along an edge then we set 'in' to NULL, since it won't be needed. The first crossing will have
+ * 'in' = NULL. We set 'out' to the SymEdge that has the face we go though to get to the next
+ * crossing, or, if the next crossing is a case (a), then it is the edge that goes to that next
+ * vertex. 'out' wlll be NULL for the last one.
+ *
+ * For case (b), vert will be NULL at first, and later filled in with the created split vertex,
+ * and 'in' will be the SymEdge that we go through, and lambda will be between 0 and 1,
+ * the fraction from in's vert to in->next's vert to put the split vertex.
+ * 'out' is not needed in this case, since the attachment point will be the sym of the first
+ * half of the split edge.
+ */
+template<typename T> class CrossData {
+ public:
+ T lambda;
+ CDTVert<T> *vert;
+ SymEdge<T> *in;
+ SymEdge<T> *out;
+
+ CrossData() : lambda(T(0)), vert(nullptr), in(nullptr), out(nullptr)
+ {
+ }
+ CrossData(T l, CDTVert<T> *v, SymEdge<T> *i, SymEdge<T> *o) : lambda(l), vert(v), in(i), out(o)
+ {
+ }
+};
+
+template<typename T>
+bool get_next_crossing_from_vert(CDT_state<T> *cdt,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const CDTVert<T> *v2);
+
+/*
+ * As part of finding crossings, we found a case where the next crossing goes through vert v.
+ * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v.
+ * Else cd_out can be NULL, because it won't be used.
+ * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the
+ * current cd.
+ */
+template<typename T>
+void fill_crossdata_for_through_vert(CDTVert<T> *v,
+ SymEdge<T> *cd_out,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next)
+{
+ SymEdge<T> *se;
+
+ cd_next->lambda = T(0);
+ cd_next->vert = v;
+ cd_next->in = NULL;
+ cd_next->out = NULL;
+ if (cd->lambda == 0) {
+ cd->out = cd_out;
+ }
+ else {
+ /* One of the edges in the triangle with edge sym(cd->in) contains v. */
+ se = sym(cd->in);
+ if (se->vert != v) {
+ se = se->next;
+ if (se->vert != v) {
+ se = se->next;
+ }
+ }
+ BLI_assert(se->vert == v);
+ cd_next->in = se;
+ }
+}
+
+/*
+ * As part of finding crossings, we found a case where orient tests say that the next crossing
+ * is on the SymEdge t, while intersecting with the ray from curco to v2.
+ * Find the intersection point and fill in the CrossData for that point.
+ * It may turn out that when doing the intersection, we get an answer that says that
+ * this case is better handled as through-vertex case instead, so we may do that.
+ * In the latter case, we want to avoid a situation where the current crossing is on an edge
+ * and the next will be an endpoint of the same edge. When that happens, we "rewrite history"
+ * and turn the current crossing into a vert one, and then extend from there.
+ *
+ * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert
+ * case. We need to fill in cd's 'out' edge if it was a vert case.
+ */
+template<typename T>
+void fill_crossdata_for_intersect(const vec2<T> &curco,
+ const CDTVert<T> *v2,
+ SymEdge<T> *t,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const T epsilon)
+{
+ CDTVert<T> *va = t->vert;
+ CDTVert<T> *vb = t->next->vert;
+ CDTVert<T> *vc = t->next->next->vert;
+ SymEdge<T> *se_vcvb = sym(t->next);
+ SymEdge<T> *se_vcva = t->next->next;
+ BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va);
+ BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb);
+ UNUSED_VARS_NDEBUG(vc);
+ auto isect = vec2<T>::isect_seg_seg(va->co, vb->co, curco, v2->co);
+ T &lambda = isect.lambda;
+ switch (isect.kind) {
+ case vec2<T>::isect_result::LINE_LINE_CROSS:
+ if (!std::is_same<T, mpq_class>::value) {
+ T len_ab = vec2<T>::distance(va->co, vb->co);
+ if (lambda * len_ab <= epsilon) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else if ((1 - lambda) * len_ab <= epsilon) {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ else {
+ *cd_next = CrossData<T>(lambda, nullptr, t, nullptr);
+ if (cd->lambda == 0) {
+ cd->out = se_vcva;
+ }
+ }
+ }
+ else {
+ *cd_next = CrossData<T>(lambda, nullptr, t, nullptr);
+ if (cd->lambda == 0) {
+ cd->out = se_vcva;
+ }
+ }
+ break;
+ case vec2<T>::isect_result::LINE_LINE_EXACT:
+ if (lambda == 0) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else if (lambda == 1) {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ else {
+ *cd_next = CrossData<T>(lambda, nullptr, t, nullptr);
+ if (cd->lambda == 0) {
+ cd->out = se_vcva;
+ }
+ }
+ break;
+ case vec2<T>::isect_result::LINE_LINE_NONE:
+ if (std::is_same<T, mpq_class>::value) {
+ BLI_assert(false);
+ }
+ /* It should be very near one end or other of segment. */
+ if (lambda <= T(0.5)) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ break;
+ case vec2<T>::isect_result::LINE_LINE_COLINEAR:
+ if (vec2<T>::distance_squared(va->co, v2->co) <= vec2<T>::distance_squared(vb->co, v2->co)) {
+ fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next);
+ }
+ else {
+ fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next);
+ }
+ break;
+ }
+}
+
+/*
+ * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
+ * 'cd' represents a crossing that goes through a vertex.
+ *
+ * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert
+ * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges.
+ */
+template<typename T>
+bool get_next_crossing_from_vert(CDT_state<T> *cdt,
+ CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const CDTVert<T> *v2)
+{
+ SymEdge<T> *tstart = cd->vert->symedge;
+ SymEdge<T> *t = tstart;
+ CDTVert<T> *vcur = cd->vert;
+ bool ok = false;
+ do {
+ /*
+ * The ray from vcur to v2 has to go either between two successive
+ * edges around vcur or exactly along them. This time through the
+ * loop, check to see if the ray goes along vcur-va
+ * or between vcur-va and vcur-vb, where va is the end of t
+ * and vb is the next vertex (on the next rot edge around vcur, but
+ * should also be the next vert of triangle starting with vcur-va.
+ */
+ if (t->face != cdt->outer_face && tri_orient(t) < 0) {
+ BLI_assert(false); /* Shouldn't happen. */
+ }
+ CDTVert<T> *va = t->next->vert;
+ CDTVert<T> *vb = t->next->next->vert;
+ int orient1 = vec2<T>::orient2d(t->vert->co, va->co, v2->co);
+ if (orient1 == 0 && in_line<T>(vcur->co, va->co, v2->co)) {
+ fill_crossdata_for_through_vert(va, t, cd, cd_next);
+ ok = true;
+ break;
+ }
+ else if (t->face != cdt->outer_face) {
+ int orient2 = vec2<T>::orient2d(vcur->co, vb->co, v2->co);
+ /* Don't handle orient2 == 0 case here: next rotation will get it. */
+ if (orient1 > 0 && orient2 < 0) {
+ /* Segment intersection. */
+ t = t->next;
+ fill_crossdata_for_intersect(vcur->co, v2, t, cd, cd_next, cdt->epsilon);
+ ok = true;
+ break;
+ }
+ }
+ } while ((t = t->rot) != tstart);
+ return ok;
+}
+
+/*
+ * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming
+ * 'cd' represents a crossing that goes through a an edge, not at either end of that edge.
+ *
+ * We have the triangle vb-va-vc, where va and vb are the split edge and vc is the third vertex on
+ * that new side of the edge (should be closer to v2). The next crossing should be through vc or
+ * intersecting vb-vc or va-vc.
+ */
+template<typename T>
+void get_next_crossing_from_edge(CrossData<T> *cd,
+ CrossData<T> *cd_next,
+ const CDTVert<T> *v2,
+ const T epsilon)
+{
+ CDTVert<T> *va = cd->in->vert;
+ CDTVert<T> *vb = cd->in->next->vert;
+ vec2<T> curco = vec2<T>::interpolate(va->co, vb->co, cd->lambda);
+ SymEdge<T> *se_ac = sym(cd->in)->next;
+ CDTVert<T> *vc = se_ac->next->vert;
+ int orient = vec2<T>::orient2d(curco, v2->co, vc->co);
+ if (orient < 0) {
+ fill_crossdata_for_intersect<T>(curco, v2, se_ac->next, cd, cd_next, epsilon);
+ }
+ else if (orient > 0.0) {
+ fill_crossdata_for_intersect(curco, v2, se_ac, cd, cd_next, epsilon);
+ }
+ else {
+ *cd_next = CrossData<T>{0.0, vc, se_ac->next, nullptr};
+ }
+}
+
+template<typename T> void dump_crossings(const Vector<CrossData<T>, 128> &crossings)
+{
+ std::cout << "CROSSINGS\n";
+ for (int i = 0; i < static_cast<int>(crossings.size()); ++i) {
+ std::cout << i << ": ";
+ const CrossData<T> &cd = crossings[i];
+ if (cd.lambda == 0) {
+ std::cout << "v" << cd.vert->index;
+ }
+ else {
+ std::cout << "lambda=" << cd.lambda;
+ }
+ if (cd.in != nullptr) {
+ std::cout << " in=" << short_se_dump(cd.in);
+ std::cout << " out=" << short_se_dump(cd.out);
+ }
+ std::cout << "\n";
+ }
+}
+
+/*
+ * Add a constrained edge between v1 and v2 to cdt structure.
+ * This may result in a number of #CDTEdges created, due to intersections
+ * and partial overlaps with existing cdt vertices and edges.
+ * Each created #CDTEdge will have input_id added to its input_ids list.
+ *
+ * If \a r_edges is not NULL, the #CDTEdges generated or found that go from
+ * v1 to v2 are put into that linked list, in order.
+ *
+ * Assumes that #BLI_constrained_delaunay_get_output has not been called yet.
+ */
+template<typename T>
+void add_edge_constraint(
+ CDT_state<T> *cdt, CDTVert<T> *v1, CDTVert<T> *v2, int input_id, LinkNode **r_edges)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "\nADD EDGE CONSTRAINT\n" << vertname(v1) << " " << vertname(v2) << "\n";
+ }
+ LinkNodePair edge_list = {NULL, NULL};
+
+ if (r_edges) {
+ *r_edges = NULL;
+ }
+
+ /*
+ * Handle two special cases first:
+ * 1) The two end vertices are the same (can happen because of merging).
+ * 2) There is already an edge between v1 and v2.
+ */
+ if (v1 == v2) {
+ return;
+ }
+ SymEdge<T> *t = find_symedge_between_verts(v1, v2);
+ if (t != nullptr) {
+ /* Segment already there. */
+ add_to_input_ids(&t->edge->input_ids, input_id, cdt);
+ if (r_edges != NULL) {
+ BLI_linklist_append_pool(&edge_list, t->edge, cdt->listpool);
+ *r_edges = edge_list.list;
+ }
+ return;
+ }
+
+ /*
+ * Fill crossings array with CrossData points for intersection path from v1 to v2.
+ *
+ * At every point, the crossings array has the path so far, except that
+ * the .out field of the last element of it may not be known yet -- if that
+ * last element is a vertex, then we won't know the output edge until we
+ * find the next crossing.
+ *
+ * To protect against infinite loops, we keep track of which vertices
+ * we have visited by setting their visit_index to a new visit epoch.
+ *
+ * We check a special case first: where the segment is already there in
+ * one hop. Saves a bunch of orient2d tests in that common case.
+ */
+ int visit = ++cdt->visit_count;
+ Vector<CrossData<T>, 128> crossings;
+ crossings.append(CrossData<T>(T(0), v1, nullptr, nullptr));
+ int n;
+ while (!((n = static_cast<int>(crossings.size())) > 0 && crossings[n - 1].vert == v2)) {
+ crossings.append(CrossData<T>());
+ CrossData<T> *cd = &crossings[n - 1];
+ CrossData<T> *cd_next = &crossings[n];
+ bool ok;
+ if (crossings[n - 1].lambda == 0) {
+ ok = get_next_crossing_from_vert(cdt, cd, cd_next, v2);
+ }
+ else {
+ get_next_crossing_from_edge(cd, cd_next, v2, cdt->epsilon);
+ ok = true;
+ }
+ if (!ok || crossings.size() == 100000) {
+ /* Shouldn't happen but if does, just bail out. */
+ BLI_assert(false);
+ return;
+ }
+ if (crossings[n].lambda == 0) {
+ if (crossings[n].vert->visit_index == visit) {
+ /* Shouldn't happen but if it does, just bail out. */
+ BLI_assert(false);
+ return;
+ }
+ crossings[n].vert->visit_index = visit;
+ }
+ }
+
+ if (dbg_level > 0) {
+ dump_crossings(crossings);
+ }
+
+ /*
+ * Postprocess crossings.
+ * Some crossings may have an intersection crossing followed
+ * by a vertex crossing that is on the same edge that was just
+ * intersected. We prefer to go directly from the previous
+ * crossing directly to the vertex. This may chain backwards.
+ *
+ * This loop marks certain crossings as "deleted", by setting
+ * their lambdas to -1.0.
+ */
+ int ncrossings = static_cast<int>(crossings.size());
+ for (int i = 2; i < ncrossings; ++i) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda == 0.0) {
+ CDTVert<T> *v = cd->vert;
+ int j;
+ CrossData<T> *cd_prev;
+ for (j = i - 1; j > 0; --j) {
+ cd_prev = &crossings[j];
+ if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) ||
+ (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) {
+ break;
+ }
+ else {
+ cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */
+ }
+ }
+ if (j < i - 1) {
+ /* Some crossings were deleted. Fix the in and out edges across gap. */
+ cd_prev = &crossings[j];
+ SymEdge<T> *se;
+ if (cd_prev->lambda == 0.0) {
+ se = find_symedge_between_verts(cd_prev->vert, v);
+ if (se == NULL) {
+ return;
+ }
+ cd_prev->out = se;
+ cd->in = NULL;
+ }
+ else {
+ se = find_symedge_with_face(v, sym(cd_prev->in)->face);
+ if (se == NULL) {
+ return;
+ }
+ cd->in = se;
+ }
+ }
+ }
+ }
+
+ /*
+ * Insert all intersection points on constrained edges.
+ */
+ for (int i = 0; i < ncrossings; ++i) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) {
+ CDTEdge<T> *edge = cdt->split_edge(cd->in, cd->lambda);
+ cd->vert = edge->symedges[0].vert;
+ }
+ }
+
+ /*
+ * Remove any crossed, non-intersected edges.
+ */
+ for (int i = 0; i < ncrossings; ++i) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) {
+ cdt->delete_edge(cd->in);
+ }
+ }
+
+ /*
+ * Insert segments for v1->v2.
+ */
+ SymEdge<T> *tstart = crossings[0].out;
+ for (int i = 1; i < ncrossings; i++) {
+ CrossData<T> *cd = &crossings[i];
+ if (cd->lambda == -1.0) {
+ continue; /* This crossing was deleted. */
+ }
+ t = NULL;
+ SymEdge<T> *tnext = t;
+ CDTEdge<T> *edge;
+ if (cd->lambda != 0.0) {
+ if (is_constrained_edge(cd->in->edge)) {
+ t = cd->vert->symedge;
+ tnext = sym(t)->next;
+ }
+ }
+ else if (cd->lambda == 0.0) {
+ t = cd->in;
+ tnext = cd->out;
+ if (t == NULL) {
+ /* Previous non-deleted crossing must also have been a vert, and segment should exist. */
+ int j;
+ CrossData<T> *cd_prev;
+ for (j = i - 1; j >= 0; j--) {
+ cd_prev = &crossings[j];
+ if (cd_prev->lambda != -1.0) {
+ break;
+ }
+ }
+ BLI_assert(cd_prev->lambda == 0.0);
+ BLI_assert(cd_prev->out->next->vert == cd->vert);
+ edge = cd_prev->out->edge;
+ add_to_input_ids(&edge->input_ids, input_id, cdt);
+ if (r_edges != NULL) {
+ BLI_linklist_append_pool(&edge_list, edge, cdt->listpool);
+ }
+ }
+ }
+ if (t != NULL) {
+ if (tstart->next->vert == t->vert) {
+ edge = tstart->edge;
+ }
+ else {
+ edge = cdt->add_diagonal(tstart, t);
+ }
+ add_to_input_ids(&edge->input_ids, input_id, cdt);
+ if (r_edges != NULL) {
+ BLI_linklist_append_pool(&edge_list, edge, cdt->listpool);
+ }
+ /* Now retriangulate upper and lower gaps. */
+ re_delaunay_triangulate(cdt, &edge->symedges[0]);
+ re_delaunay_triangulate(cdt, &edge->symedges[1]);
+ }
+ if (i < ncrossings - 1) {
+ if (tnext != NULL) {
+ tstart = tnext;
+ }
+ }
+ }
+
+ if (r_edges) {
+ *r_edges = edge_list.list;
+ }
+}
+
+/* Incrementally add edge input edge as a constraint. This may cause the graph structure
+ * to change, in cases where the constraints intersect existing edges.
+ * The code will ensure that CDTEdges created will have ids that tie them back
+ * to the original edge constraint index.
+ */
+template<typename T> void add_edge_constraints(CDT_state<T> *cdt, const CDT_input<T> &input)
+{
+ int ne = static_cast<int>(input.edge.size());
+ int nv = static_cast<int>(input.vert.size());
+ for (int i = 0; i < ne; i++) {
+ int iv1 = input.edge[i].first;
+ int iv2 = input.edge[i].second;
+ if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
+ /* Ignore invalid indices in edges. */
+ continue;
+ }
+ CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1);
+ CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2);
+ add_edge_constraint(cdt, v1, v2, i, nullptr);
+ }
+ cdt->face_edge_offset = ne;
+}
+
+/*
+ * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that
+ * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is
+ * on the left of that SymEdge.
+ *
+ * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then
+ * process all adjacent faces where the adjacency isn't across an edge that was a constraint added
+ * for the boundary of the input face.
+ * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face.
+ *
+ * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside.
+ * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the
+ * contiguous set of faces enclosed by parts of the boundary, leaving the other such untagged. This
+ * may be a feature instead of a bug if the first contiguous section is most of the face and the
+ * others are tiny self-crossing triangles at some parts of the boundary. On the other hand, if
+ * decide we want to handle these in full generality, then will need a more complicated algorithm
+ * (using "inside" tests and a parity rule) to decide on the interior.
+ */
+template<typename T>
+void add_face_ids(
+ CDT_state<T> *cdt, SymEdge<T> *face_symedge, int face_id, int fedge_start, int fedge_end)
+{
+ /* Can't loop forever since eventually would visit every face. */
+ cdt->visit_count++;
+ int visit = cdt->visit_count;
+ Vector<SymEdge<T> *> stack;
+ stack.append(face_symedge);
+ while (!stack.is_empty()) {
+ SymEdge<T> *se = stack.pop_last();
+ CDTFace<T> *face = se->face;
+ if (face->visit_index == visit) {
+ continue;
+ }
+ face->visit_index = visit;
+ add_to_input_ids(&face->input_ids, face_id, cdt);
+ SymEdge<T> *se_start = se;
+ for (se = se->next; se != se_start; se = se->next) {
+ if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) {
+ SymEdge<T> *se_sym = sym(se);
+ CDTFace<T> *face_other = se_sym->face;
+ if (face_other->visit_index != visit) {
+ stack.append(se_sym);
+ }
+ }
+ }
+ }
+}
+
+/* Incrementally each edge of each input face as an edge constraint.
+ * The code will ensure that the CDTEdges created will have ids that tie them
+ * back to the original face edge (using a numbering system for those edges
+ * that starts with cdt->face_edge_offset, and continues with the edges in
+ * order around each face in turn.
+ */
+template<typename T> void add_face_constraints(CDT_state<T> *cdt, const CDT_input<T> &input)
+{
+ int nv = static_cast<int>(input.vert.size());
+ int nf = static_cast<int>(input.face.size());
+ int fstart = 0;
+ SymEdge<T> *face_symedge0 = nullptr;
+ for (int f = 0; f < nf; f++) {
+ int flen = static_cast<int>(input.face[f].size());
+ if (flen <= 2) {
+ /* Ignore faces with fewer than 3 vertices. */
+ fstart += flen;
+ continue;
+ }
+ for (int i = 0; i < flen; i++) {
+ int face_edge_id = cdt->face_edge_offset + fstart + i;
+ int iv1 = input.face[f][i];
+ int iv2 = input.face[f][(i + 1) % flen];
+ if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) {
+ /* Ignore face edges with invalid vertices. */
+ continue;
+ }
+ CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1);
+ CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2);
+ LinkNode *edge_list;
+ add_edge_constraint(cdt, v1, v2, face_edge_id, &edge_list);
+ /* Set a new face_symedge0 each time since earlier ones may not
+ * survive later symedge splits. Really, just want the one when
+ * i == flen -1, but this code guards against that one somehow
+ * being null.
+ */
+ if (edge_list != nullptr) {
+ CDTEdge<T> *face_edge = static_cast<CDTEdge<T> *>(edge_list->link);
+ face_symedge0 = &face_edge->symedges[0];
+ if (face_symedge0->vert != v1) {
+ face_symedge0 = &face_edge->symedges[1];
+ BLI_assert(face_symedge0->vert == v1);
+ }
+ }
+ BLI_linklist_free_pool(edge_list, nullptr, cdt->listpool);
+ }
+ int fedge_start = cdt->face_edge_offset + fstart;
+ int fedge_end = fedge_start + flen - 1;
+ if (face_symedge0 != nullptr) {
+ add_face_ids(cdt, face_symedge0, f, fedge_start, fedge_end);
+ }
+ fstart += flen;
+ }
+}
+
+/* Delete_edge but try not to mess up outer face.
+ * Also faces have symedges now, so make sure not
+ * to mess those up either. */
+template<typename T> void dissolve_symedge(CDT_state<T> *cdt, SymEdge<T> *se)
+{
+ SymEdge<T> *symse = sym(se);
+ if (symse->face == cdt->outer_face) {
+ se = sym(se);
+ symse = sym(se);
+ }
+ if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) {
+ /* Advancing by 2 to get past possible 'sym(se)'. */
+ if (se->next->next == se) {
+ cdt->outer_face->symedge = NULL;
+ }
+ else {
+ cdt->outer_face->symedge = se->next->next;
+ }
+ }
+ else {
+ if (se->face->symedge == se) {
+ se->face->symedge = se->next;
+ }
+ if (symse->face->symedge == symse) {
+ symse->face->symedge = symse->next;
+ }
+ }
+ cdt->delete_edge(se);
+}
+
+/* Remove all non-constraint edges. */
+template<typename T> void remove_non_constraint_edges(CDT_state<T> *cdt)
+{
+ for (CDTEdge<T> *e : cdt->edges) {
+ SymEdge<T> *se = &e->symedges[0];
+ if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
+ dissolve_symedge(cdt, se);
+ }
+ }
+}
+
+/*
+ * Remove the non-constraint edges, but leave enough of them so that all of the
+ * faces that would be bmesh faces (that is, the faces that have some input representative)
+ * are valid: they can't have holes, they can't have repeated vertices, and they can't have
+ * repeated edges.
+ *
+ * Not essential, but to make the result look more aesthetically nice,
+ * remove the edges in order of decreasing length, so that it is more likely that the
+ * final remaining support edges are short, and therefore likely to make a fairly
+ * direct path from an outer face to an inner hole face.
+ */
+
+/* For sorting edges by decreasing length (squared). */
+template<typename T> struct EdgeToSort {
+ T len_squared;
+ CDTEdge<T> *e{nullptr};
+};
+
+template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_state<T> *cdt)
+{
+ size_t nedges = cdt->edges.size();
+ if (nedges == 0) {
+ return;
+ }
+ Vector<EdgeToSort<T>> dissolvable_edges;
+ dissolvable_edges.reserve(cdt->edges.size());
+ int i = 0;
+ for (CDTEdge<T> *e : cdt->edges) {
+ if (!is_deleted_edge(e) && !is_constrained_edge(e)) {
+ dissolvable_edges.append(EdgeToSort<T>());
+ dissolvable_edges[i].e = e;
+ const vec2<T> &co1 = e->symedges[0].vert->co;
+ const vec2<T> &co2 = e->symedges[1].vert->co;
+ dissolvable_edges[i].len_squared = vec2<T>::distance_squared(co1, co2);
+ i++;
+ }
+ }
+ std::sort(dissolvable_edges.begin(),
+ dissolvable_edges.end(),
+ [](const EdgeToSort<T> &a, const EdgeToSort<T> &b) -> bool {
+ return (a.len_squared < b.len_squared);
+ });
+ for (EdgeToSort<T> &ets : dissolvable_edges) {
+ CDTEdge<T> *e = ets.e;
+ SymEdge<T> *se = &e->symedges[0];
+ bool dissolve = true;
+ CDTFace<T> *fleft = se->face;
+ CDTFace<T> *fright = sym(se)->face;
+ if (fleft != cdt->outer_face && fright != cdt->outer_face &&
+ (fleft->input_ids != nullptr || fright->input_ids != nullptr)) {
+ /* Is there another symedge with same left and right faces?
+ * Or is there a vertex not part of e touching the same left and right faces?
+ */
+ for (SymEdge<T> *se2 = se->next; dissolve && se2 != se; se2 = se2->next) {
+ if (sym(se2)->face == fright ||
+ (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) {
+ dissolve = false;
+ }
+ }
+ }
+
+ if (dissolve) {
+ dissolve_symedge(cdt, se);
+ }
+ }
+}
+
+template<typename T> void remove_outer_edges_until_constraints(CDT_state<T> *cdt)
+{
+ // LinkNode *fstack = NULL;
+ // SymEdge *se, *se_start;
+ // CDTFace *f, *fsym;
+ int visit = ++cdt->visit_count;
+
+ cdt->outer_face->visit_index = visit;
+ /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */
+ Vector<CDTFace<T> *> fstack;
+ SymEdge<T> *se_start = cdt->outer_face->symedge;
+ SymEdge<T> *se = se_start;
+ do {
+ if (!is_constrained_edge(se->edge)) {
+ CDTFace<T> *fsym = sym(se)->face;
+ if (fsym->visit_index != visit) {
+ fstack.append(fsym);
+ }
+ }
+ } while ((se = se->next) != se_start);
+
+ while (!fstack.is_empty()) {
+ LinkNode *to_dissolve = nullptr;
+ bool dissolvable;
+ CDTFace<T> *f = fstack.pop_last();
+ if (f->visit_index == visit) {
+ continue;
+ }
+ BLI_assert(f != cdt->outer_face);
+ f->visit_index = visit;
+ se_start = se = f->symedge;
+ do {
+ dissolvable = !is_constrained_edge(se->edge);
+ if (dissolvable) {
+ CDTFace<T> *fsym = sym(se)->face;
+ if (fsym->visit_index != visit) {
+ fstack.append(fsym);
+ }
+ else {
+ BLI_linklist_prepend_pool(&to_dissolve, se, cdt->listpool);
+ }
+ }
+ se = se->next;
+ } while (se != se_start);
+ while (to_dissolve != NULL) {
+ se = static_cast<SymEdge<T> *>(BLI_linklist_pop_pool(&to_dissolve, cdt->listpool));
+ if (se->next != NULL) {
+ dissolve_symedge(cdt, se);
+ }
+ }
+ }
+}
+
+/*
+ * Remove edges and merge faces to get desired output, as per options.
+ * \note the cdt cannot be further changed after this.
+ */
+template<typename T>
+void prepare_cdt_for_output(CDT_state<T> *cdt, const CDT_output_type output_type)
+{
+ if (cdt->edges.is_empty()) {
+ return;
+ }
+
+ /* Make sure all non-deleted faces have a symedge. */
+ for (CDTEdge<T> *e : cdt->edges) {
+ if (!is_deleted_edge(e)) {
+ if (e->symedges[0].face->symedge == nullptr) {
+ e->symedges[0].face->symedge = &e->symedges[0];
+ }
+ if (e->symedges[1].face->symedge == nullptr) {
+ e->symedges[1].face->symedge = &e->symedges[1];
+ }
+ }
+ }
+
+ if (output_type == CDT_CONSTRAINTS) {
+ remove_non_constraint_edges(cdt);
+ }
+ else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) {
+ remove_non_constraint_edges_leave_valid_bmesh(cdt);
+ }
+ else if (output_type == CDT_INSIDE) {
+ remove_outer_edges_until_constraints(cdt);
+ }
+}
+
+template<typename T>
+CDT_result<T> get_cdt_output(CDT_state<T> *cdt,
+ const CDT_input<T> UNUSED(input),
+ CDT_output_type output_type)
+{
+ prepare_cdt_for_output(cdt, output_type);
+ CDT_result<T> result;
+ result.face_edge_offset = cdt->face_edge_offset;
+
+ /* All verts without a merge_to_index will be output.
+ * vert_to_output_map[i] will hold the output vertex index
+ * corresponding to the vert in position i in cdt->verts.
+ * This first loop sets vert_to_output_map for unmerged verts.
+ */
+ int verts_size = static_cast<int>(cdt->verts.size());
+ Array<int> vert_to_output_map(verts_size);
+ int nv = 0;
+ for (int i = 0; i < verts_size; ++i) {
+ CDTVert<T> *v = cdt->verts[i];
+ if (v->merge_to_index == -1) {
+ vert_to_output_map[i] = nv;
+ ++nv;
+ }
+ }
+ if (nv <= 0) {
+ return result;
+ }
+ /* Now we can set vert_to_output_map for merged verts,
+ * and also add the input indices of merged verts to the input_ids
+ * list of the merge target if they were an original input id.
+ */
+ if (nv < verts_size) {
+ for (int i = 0; i < verts_size; ++i) {
+ CDTVert<T> *v = cdt->verts[i];
+ if (v->merge_to_index != -1) {
+ if (i < cdt->input_vert_tot) {
+ add_to_input_ids(&cdt->verts[v->merge_to_index]->input_ids, i, cdt);
+ }
+ vert_to_output_map[i] = vert_to_output_map[v->merge_to_index];
+ }
+ }
+ }
+ result.vert = Array<vec2<T>>(nv);
+ result.vert_orig = Array<Vector<int>>(nv);
+ int i_out = 0;
+ for (int i = 0; i < verts_size; ++i) {
+ CDTVert<T> *v = cdt->verts[i];
+ if (v->merge_to_index == -1) {
+ result.vert[i_out] = v->co;
+ if (i < cdt->input_vert_tot) {
+ result.vert_orig[i_out].append(i);
+ }
+ for (LinkNode *ln = v->input_ids; ln; ln = ln->next) {
+ result.vert_orig[i_out].append(POINTER_AS_INT(ln->link));
+ }
+ ++i_out;
+ }
+ }
+
+ /* All non-deleted edges will be output. */
+ int ne = std::count_if(cdt->edges.begin(), cdt->edges.end(), [](const CDTEdge<T> *e) -> bool {
+ return !is_deleted_edge(e);
+ });
+ result.edge = Array<std::pair<int, int>>(ne);
+ result.edge_orig = Array<Vector<int>>(ne);
+ int e_out = 0;
+ for (const CDTEdge<T> *e : cdt->edges) {
+ if (!is_deleted_edge(e)) {
+ int vo1 = vert_to_output_map[e->symedges[0].vert->index];
+ int vo2 = vert_to_output_map[e->symedges[1].vert->index];
+ result.edge[e_out] = std::pair<int, int>(vo1, vo2);
+ for (LinkNode *ln = e->input_ids; ln; ln = ln->next) {
+ result.edge_orig[e_out].append(POINTER_AS_INT(ln->link));
+ }
+ ++e_out;
+ }
+ }
+
+ /* All non-deleted, non-outer faces will be output. */
+ int nf = std::count_if(cdt->faces.begin(), cdt->faces.end(), [=](const CDTFace<T> *f) -> bool {
+ return !f->deleted && f != cdt->outer_face;
+ });
+ result.face = Array<Vector<int>>(nf);
+ result.face_orig = Array<Vector<int>>(nf);
+ int f_out = 0;
+ for (const CDTFace<T> *f : cdt->faces) {
+ if (!f->deleted && f != cdt->outer_face) {
+ SymEdge<T> *se = f->symedge;
+ BLI_assert(se != nullptr);
+ SymEdge<T> *se_start = se;
+ do {
+ result.face[f_out].append(vert_to_output_map[se->vert->index]);
+ se = se->next;
+ } while (se != se_start);
+ for (LinkNode *ln = f->input_ids; ln; ln = ln->next) {
+ result.face_orig[f_out].append(POINTER_AS_INT(ln->link));
+ }
+ ++f_out;
+ }
+ }
+ return result;
+}
+
+/* Add all the input verts into cdt. This will dedup, setting vert's merge_to_index to show merges.
+ */
+template<typename T> void add_input_verts(CDT_state<T> *cdt, const CDT_input<T> &input)
+{
+ for (int i = 0; i < cdt->input_vert_tot; ++i) {
+ cdt->add_vert(input.vert[i]);
+ }
+}
+
+template<typename T>
+CDT_result<T> delaunay_calc(const CDT_input<T> &input, CDT_output_type output_type)
+{
+ int nv = static_cast<int>(input.vert.size());
+ int ne = static_cast<int>(input.edge.size());
+ int nf = static_cast<int>(input.face.size());
+ CDT_state<T> cdt(nv, ne, nf, input.epsilon);
+ add_input_verts(&cdt, input);
+ initial_triangulation(&cdt);
+ add_edge_constraints(&cdt, input);
+ add_face_constraints(&cdt, input);
+ return get_cdt_output(&cdt, input, output_type);
+}
+
+} /* namespace CDT */
+
+CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, CDT_output_type output_type)
+{
+ return CDT::delaunay_calc(input, output_type);
+}
+
+CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input,
+ CDT_output_type output_type)
+{
+ return CDT::delaunay_calc(input, output_type);
+}
+
+} /* namespace BLI */
+
+/* C interface. */
+
+/* This function uses the double version of CDT::delaunay_calc.
+ * Almost all of the work here is to convert between C++ Arrays<Vector<int>>
+ * and a C version that linearizes all the elements and uses a "start"
+ * and "len" array to say where the individual vectors start and how
+ * long they are.
+ */
+extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input,
+ const CDT_output_type output_type)
+{
+ BLI::CDT_input<double> in;
+ in.vert = BLI::Array<BLI::vec2<double>>(input->verts_len);
+ in.edge = BLI::Array<std::pair<int, int>>(input->edges_len);
+ in.face = BLI::Array<BLI::Vector<int>>(input->faces_len);
+ for (int v = 0; v < input->verts_len; ++v) {
+ double x = static_cast<double>(input->vert_coords[v][0]);
+ double y = static_cast<double>(input->vert_coords[v][1]);
+ in.vert[v] = BLI::vec2<double>(x, y);
+ }
+ for (int e = 0; e < input->edges_len; ++e) {
+ in.edge[e] = std::pair<int, int>(input->edges[e][0], input->edges[e][1]);
+ }
+ for (int f = 0; f < input->faces_len; ++f) {
+ in.face[f] = BLI::Vector<int>(input->faces_len_table[f]);
+ int fstart = input->faces_start_table[f];
+ for (int j = 0; j < input->faces_len_table[f]; ++j) {
+ in.face[f][j] = input->faces[fstart + j];
+ }
+ }
+ in.epsilon = static_cast<double>(input->epsilon);
+
+ BLI::CDT_result<double> res = BLI::CDT::delaunay_calc(in, output_type);
+
+ ::CDT_result *output = static_cast<::CDT_result *>(MEM_mallocN(sizeof(*output), __func__));
+ int nv = output->verts_len = static_cast<int>(res.vert.size());
+ int ne = output->edges_len = static_cast<int>(res.edge.size());
+ int nf = output->faces_len = static_cast<int>(res.face.size());
+ int tot_v_orig = 0;
+ int tot_e_orig = 0;
+ int tot_f_orig = 0;
+ int tot_f_lens = 0;
+ for (int v = 0; v < nv; ++v) {
+ tot_v_orig += static_cast<int>(res.vert_orig[v].size());
+ }
+ for (int e = 0; e < ne; ++e) {
+ tot_e_orig += static_cast<int>(res.edge_orig[e].size());
+ }
+ for (int f = 0; f < nf; ++f) {
+ tot_f_orig += static_cast<int>(res.face_orig[f].size());
+ tot_f_lens += static_cast<int>(res.face[f].size());
+ }
+
+ output->vert_coords = static_cast<decltype(output->vert_coords)>(
+ MEM_malloc_arrayN(nv, sizeof(output->vert_coords[0]), __func__));
+ output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__));
+ output->verts_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(nv, sizeof(int), __func__));
+ output->verts_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nv, sizeof(int), __func__));
+ output->edges = static_cast<decltype(output->edges)>(
+ MEM_malloc_arrayN(ne, sizeof(output->edges[0]), __func__));
+ output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__));
+ output->edges_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(ne, sizeof(int), __func__));
+ output->edges_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(ne, sizeof(int), __func__));
+ output->faces = static_cast<int *>(MEM_malloc_arrayN(tot_f_lens, sizeof(int), __func__));
+ output->faces_start_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__));
+ output->faces_orig_start_table = static_cast<int *>(
+ MEM_malloc_arrayN(nf, sizeof(int), __func__));
+ output->faces_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__));
+
+ int v_orig_index = 0;
+ for (int v = 0; v < nv; ++v) {
+ output->vert_coords[v][0] = static_cast<float>(res.vert[v][0]);
+ output->vert_coords[v][1] = static_cast<float>(res.vert[v][1]);
+ int this_start = v_orig_index;
+ output->verts_orig_start_table[v] = this_start;
+ for (uint j = 0; j < res.vert_orig[v].size(); ++j) {
+ output->verts_orig[v_orig_index++] = res.vert_orig[v][j];
+ }
+ output->verts_orig_len_table[v] = v_orig_index - this_start;
+ }
+ int e_orig_index = 0;
+ for (int e = 0; e < ne; ++e) {
+ output->edges[e][0] = res.edge[e].first;
+ output->edges[e][1] = res.edge[e].second;
+ int this_start = e_orig_index;
+ output->edges_orig_start_table[e] = this_start;
+ for (uint j = 0; j < res.edge_orig[e].size(); ++j) {
+ output->edges_orig[e_orig_index++] = res.vert_orig[e][j];
+ }
+ output->edges_orig_len_table[e] = e_orig_index - this_start;
+ }
+ int f_orig_index = 0;
+ int f_index = 0;
+ for (int f = 0; f < nf; ++f) {
+ output->faces_start_table[f] = f_index;
+ int flen = static_cast<int>(res.face[f].size());
+ output->faces_len_table[f] = flen;
+ for (int j = 0; j < flen; ++j) {
+ output->faces[f_index++] = res.face[f][j];
+ }
+ int this_start = f_orig_index;
+ output->faces_orig_start_table[f] = this_start;
+ for (uint k = 0; k < res.face_orig[f].size(); ++k) {
+ output->faces_orig[f_orig_index++] = res.face_orig[f][k];
+ }
+ output->faces_orig_len_table[f] = f_orig_index - this_start;
+ }
+ return output;
+}
+
+extern "C" void BLI_delaunay_2d_cdt_free(::CDT_result *result)
+{
+ MEM_freeN(result->vert_coords);
+ MEM_freeN(result->edges);
+ MEM_freeN(result->faces);
+ MEM_freeN(result->faces_start_table);
+ MEM_freeN(result->faces_len_table);
+ MEM_freeN(result->verts_orig);
+ MEM_freeN(result->verts_orig_start_table);
+ MEM_freeN(result->verts_orig_len_table);
+ MEM_freeN(result->edges_orig_start_table);
+ MEM_freeN(result->edges_orig_len_table);
+ MEM_freeN(result->faces_orig);
+ MEM_freeN(result->faces_orig_start_table);
+ MEM_freeN(result->faces_orig_len_table);
+ MEM_freeN(result);
+}
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 4ee32efb681..1b388dcf11f 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -576,11 +576,6 @@ MINLINE int compare_ff(float a, float b, const float max_diff)
return fabsf(a - b) <= max_diff;
}
-MINLINE int compare_dd(double a, double b, const double max_diff)
-{
- return fabs(a - b) <= max_diff;
-}
-
/**
* Almost-equal for IEEE floats, using their integer representation
* (mixing ULP and absolute difference methods).
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 7691b0fa6fb..e7c1fc8c2d9 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -432,15 +432,6 @@ void closest_to_plane3_normalized_v3(float r_close[3], const float plane[3], con
madd_v3_v3v3fl(r_close, pt, plane, -side);
}
-void closest_to_plane3_normalized_v3_db(double r_close[3],
- const double plane[3],
- const double pt[3])
-{
- const double side = dot_v3v3_db(plane, pt);
- BLI_ASSERT_UNIT_V3_DB(plane);
- madd_v3_v3v3db_db(r_close, pt, plane, -side);
-}
-
float dist_signed_squared_to_plane_v3(const float pt[3], const float plane[4])
{
const float len_sq = len_squared_v3(plane);
@@ -523,16 +514,6 @@ float dist_squared_to_line_v3(const float p[3], const float l1[3], const float l
return len_squared_v3v3(closest, p);
}
-
-double dist_squared_to_line_v3_db(const double p[3], const double l1[3], const double l2[3])
-{
- double closest[3];
-
- closest_to_line_v3_db(closest, p, l1, l2);
-
- return len_squared_v3v3_db(closest, p);
-}
-
float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3])
{
return sqrtf(dist_squared_to_line_v3(p, l1, l2));
@@ -2327,41 +2308,6 @@ bool isect_plane_plane_v3(const float plane_a[4],
}
}
-bool isect_plane_plane_v3_db(const double plane_a[4],
- const double plane_b[4],
- double r_isect_co[3],
- double r_isect_no[3])
-{
- double det, plane_c[3];
-
- /* direction is simply the cross product */
- cross_v3_v3v3_db(plane_c, plane_a, plane_b);
-
- /* in this case we don't need to use 'determinant_m3' */
- det = len_squared_v3_db(plane_c);
-
- if (det != 0.0) {
- double tmp[3];
-
- /* (plane_b.xyz.cross(plane_c.xyz) * -plane_a[3] +
- * plane_c.xyz.cross(plane_a.xyz) * -plane_b[3]) / det; */
- cross_v3_v3v3_db(tmp, plane_c, plane_b);
- mul_v3db_v3dbdb(r_isect_co, tmp, plane_a[3]);
-
- cross_v3_v3v3_db(tmp, plane_a, plane_c);
- madd_v3db_v3dbdb(r_isect_co, tmp, plane_b[3]);
-
- mul_v3db_db(r_isect_co, 1.0 / det);
-
- copy_v3_v3_db(r_isect_no, plane_c);
-
- return true;
- }
- else {
- return false;
- }
-}
-
/**
* Intersect two triangles.
*
@@ -3061,71 +3007,6 @@ int isect_line_line_epsilon_v3(const float v1[3],
}
}
-int isect_line_line_epsilon_v3_db(const double v1[3],
- const double v2[3],
- const double v3[3],
- const double v4[3],
- double r_i1[3],
- double r_i2[3],
- const double epsilon)
-{
- double a[3], b[3], c[3], ab[3], cb[3];
- double d, div;
-
- sub_v3_v3v3_db(c, v3, v1);
- sub_v3_v3v3_db(a, v2, v1);
- sub_v3_v3v3_db(b, v4, v3);
-
- cross_v3_v3v3_db(ab, a, b);
- d = dot_v3v3_db(c, ab);
- div = dot_v3v3_db(ab, ab);
-
- /* important not to use an epsilon here, see: T45919 */
- /* test zero length line */
- if (UNLIKELY(div == 0.0)) {
- return 0;
- }
- /* test if the two lines are coplanar */
- else if (UNLIKELY(fabs(d) <= epsilon)) {
- cross_v3_v3v3_db(cb, c, b);
-
- mul_v3db_db(a, dot_v3v3_db(cb, ab) / div);
- add_v3_v3v3_db(r_i1, v1, a);
- copy_v3_v3_db(r_i2, r_i1);
-
- return 1; /* one intersection only */
- }
- /* if not */
- else {
- double n[3], t[3];
- double v3t[3], v4t[3];
- sub_v3_v3v3_db(t, v1, v3);
-
- /* offset between both plane where the lines lies */
- cross_v3_v3v3_db(n, a, b);
- project_v3_v3v3_db(t, t, n);
-
- /* for the first line, offset the second line until it is coplanar */
- add_v3_v3v3_db(v3t, v3, t);
- add_v3_v3v3_db(v4t, v4, t);
-
- sub_v3_v3v3_db(c, v3t, v1);
- sub_v3_v3v3_db(a, v2, v1);
- sub_v3_v3v3_db(b, v4t, v3t);
-
- cross_v3_v3v3_db(ab, a, b);
- cross_v3_v3v3_db(cb, c, b);
-
- mul_v3db_db(a, dot_v3v3_db(cb, ab) / dot_v3v3_db(ab, ab));
- add_v3_v3v3_db(r_i1, v1, a);
-
- /* for the second line, just substract the offset from the first intersection point */
- sub_v3_v3v3_db(r_i2, r_i1, t);
-
- return 2; /* two nearest points */
- }
-}
-
int isect_line_line_v3(const float v1[3],
const float v2[3],
const float v3[3],
@@ -3260,15 +3141,6 @@ bool isect_aabb_aabb_v3(const float min1[3],
min2[1] < max1[1] && min2[2] < max1[2]);
}
-bool isect_aabb_aabb_v3_db(const double min1[3],
- const double max1[3],
- const double min2[3],
- const double max2[3])
-{
- return (min1[0] < max2[0] && min1[1] < max2[1] && min1[2] < max2[2] && min2[0] < max1[0] &&
- min2[1] < max1[1] && min2[2] < max1[2]);
-}
-
void isect_ray_aabb_v3_precalc(struct IsectRayAABB_Precalc *data,
const float ray_origin[3],
const float ray_direction[3])
@@ -3391,21 +3263,6 @@ float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3],
return lambda;
}
-double closest_to_line_v3_db(double r_close[3],
- const double p[3],
- const double l1[3],
- const double l2[3])
-{
- double h[3], u[3], lambda;
- sub_v3_v3v3_db(u, l2, l1);
- sub_v3_v3v3_db(h, p, l1);
- lambda = dot_v3v3_db(u, h) / dot_v3v3_db(u, u);
- r_close[0] = l1[0] + u[0] * lambda;
- r_close[1] = l1[1] + u[1] * lambda;
- r_close[2] = l1[2] + u[2] * lambda;
- return lambda;
-}
-
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
{
float h[2], u[2], lambda;
@@ -3799,19 +3656,6 @@ void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
is_zero_v3(normal));
}
-void axis_dominant_v3_to_m3_db(double r_mat[3][3], const double normal[3])
-{
- BLI_ASSERT_UNIT_V3_DB(normal);
-
- copy_v3_v3_db(r_mat[2], normal);
- ortho_basis_v3v3_v3_db(r_mat[0], r_mat[1], r_mat[2]);
-
- BLI_ASSERT_UNIT_V3_DB(r_mat[0]);
- BLI_ASSERT_UNIT_V3_DB(r_mat[1]);
-
- transpose_m3_db(r_mat);
-}
-
/**
* Same as axis_dominant_v3_to_m3, but flips the normal
*/
diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c
index 2178e925204..893c978d084 100644
--- a/source/blender/blenlib/intern/math_geom_inline.c
+++ b/source/blender/blenlib/intern/math_geom_inline.c
@@ -261,11 +261,6 @@ MINLINE float plane_point_side_v3(const float plane[4], const float co[3])
return dot_v3v3(co, plane) + plane[3];
}
-MINLINE double plane_point_side_v3_db(const double plane[4], const double co[3])
-{
- return dot_v3v3_db(co, plane) + plane[3];
-}
-
/* useful to calculate an even width shell, by taking the angle between 2 planes.
* The return value is a scale on the offset.
* no angle between planes is 1.0, as the angle between the 2 planes approaches 180d
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index c557b5cb609..9e398239bc7 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -1103,13 +1103,6 @@ float determinant_m3_array(const float m[3][3])
m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]));
}
-double determinant_m3_array_db(const double m[3][3])
-{
- return (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) -
- m[1][0] * (m[0][1] * m[2][2] - m[0][2] * m[2][1]) +
- m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]));
-}
-
float determinant_m4_mat3_array(const float m[4][4])
{
return (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) -
@@ -1188,32 +1181,6 @@ bool invert_m3_m3(float m1[3][3], const float m2[3][3])
return success;
}
-bool invert_m3_m3_db(double m1[3][3], const double m2[3][3])
-{
- double det;
- int a, b;
- bool success;
-
- /* calc adjoint */
- adjoint_m3_m3_db(m1, m2);
-
- /* then determinant old matrix! */
- det = determinant_m3_array_db(m2);
-
- success = (det != 0.0);
-
- if (LIKELY(det != 0.0)) {
- det = 1.0 / det;
- for (a = 0; a < 3; a++) {
- for (b = 0; b < 3; b++) {
- m1[a][b] *= det;
- }
- }
- }
-
- return success;
-}
-
bool invert_m4(float m[4][4])
{
float tmp[4][4];
@@ -1354,21 +1321,6 @@ void transpose_m3(float mat[3][3])
mat[2][1] = t;
}
-void transpose_m3_db(double mat[3][3])
-{
- double t;
-
- t = mat[0][1];
- mat[0][1] = mat[1][0];
- mat[1][0] = t;
- t = mat[0][2];
- mat[0][2] = mat[2][0];
- mat[2][0] = t;
- t = mat[1][2];
- mat[1][2] = mat[2][1];
- mat[2][1] = t;
-}
-
void transpose_m3_m3(float rmat[3][3], const float mat[3][3])
{
BLI_assert(rmat != mat);
@@ -1967,22 +1919,6 @@ void adjoint_m3_m3(float m1[3][3], const float m[3][3])
m1[2][2] = m[0][0] * m[1][1] - m[0][1] * m[1][0];
}
-void adjoint_m3_m3_db(double m1[3][3], const double m[3][3])
-{
- BLI_assert(m1 != m);
- m1[0][0] = m[1][1] * m[2][2] - m[1][2] * m[2][1];
- m1[0][1] = -m[0][1] * m[2][2] + m[0][2] * m[2][1];
- m1[0][2] = m[0][1] * m[1][2] - m[0][2] * m[1][1];
-
- m1[1][0] = -m[1][0] * m[2][2] + m[1][2] * m[2][0];
- m1[1][1] = m[0][0] * m[2][2] - m[0][2] * m[2][0];
- m1[1][2] = -m[0][0] * m[1][2] + m[0][2] * m[1][0];
-
- m1[2][0] = m[1][0] * m[2][1] - m[1][1] * m[2][0];
- m1[2][1] = -m[0][0] * m[2][1] + m[0][1] * m[2][0];
- m1[2][2] = m[0][0] * m[1][1] - m[0][1] * m[1][0];
-}
-
void adjoint_m4_m4(float out[4][4], const float in[4][4]) /* out = ADJ(in) */
{
float a1, a2, a3, a4, b1, b2, b3, b4;
diff --git a/source/blender/blenlib/intern/math_vec.cc b/source/blender/blenlib/intern/math_vec.cc
new file mode 100644
index 00000000000..089f941820c
--- /dev/null
+++ b/source/blender/blenlib/intern/math_vec.cc
@@ -0,0 +1,2592 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "gmpxx.h"
+
+#include "BLI_double2.hh"
+#include "BLI_double3.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+#include "BLI_mpq2.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_utildefines.h"
+
+namespace BLI {
+
+float2::isect_result float2::isect_seg_seg(const float2 &v1,
+ const float2 &v2,
+ const float2 &v3,
+ const float2 &v4)
+{
+ float2::isect_result ans;
+ float div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
+ if (div == 0.0f) {
+ ans.lambda = 0.0f;
+ ans.mu = 0.0f;
+ ans.kind = float2::isect_result::LINE_LINE_COLINEAR;
+ }
+ else {
+ ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
+ ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
+ if (ans.lambda >= 0.0f && ans.lambda <= 1.0f && ans.mu >= 0.0f && ans.mu <= 1.0f) {
+ if (ans.lambda == 0.0f || ans.lambda == 1.0f || ans.mu == 0.0f || ans.mu == 1.0f) {
+ ans.kind = float2::isect_result::LINE_LINE_EXACT;
+ }
+ else {
+ ans.kind = float2::isect_result::LINE_LINE_CROSS;
+ }
+ }
+ else {
+ ans.kind = float2::isect_result::LINE_LINE_NONE;
+ }
+ }
+ return ans;
+}
+
+double2::isect_result double2::isect_seg_seg(const double2 &v1,
+ const double2 &v2,
+ const double2 &v3,
+ const double2 &v4)
+{
+ double2::isect_result ans;
+ double div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
+ if (div == 0.0) {
+ ans.lambda = 0.0;
+ ans.mu = 0.0;
+ ans.kind = double2::isect_result::LINE_LINE_COLINEAR;
+ }
+ else {
+ ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
+ ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
+ if (ans.lambda >= 0.0 && ans.lambda <= 1.0 && ans.mu >= 0.0 && ans.mu <= 1.0) {
+ if (ans.lambda == 0.0 || ans.lambda == 1.0 || ans.mu == 0.0 || ans.mu == 1.0) {
+ ans.kind = double2::isect_result::LINE_LINE_EXACT;
+ }
+ else {
+ ans.kind = double2::isect_result::LINE_LINE_CROSS;
+ }
+ }
+ else {
+ ans.kind = double2::isect_result::LINE_LINE_NONE;
+ }
+ }
+ return ans;
+}
+
+mpq2::isect_result mpq2::isect_seg_seg(const mpq2 &v1,
+ const mpq2 &v2,
+ const mpq2 &v3,
+ const mpq2 &v4)
+{
+ mpq2::isect_result ans;
+ mpq_class div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]);
+ if (div == 0.0) {
+ ans.lambda = 0.0;
+ ans.mu = 0.0;
+ ans.kind = mpq2::isect_result::LINE_LINE_COLINEAR;
+ }
+ else {
+ ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div;
+ ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div;
+ if (ans.lambda >= 0 && ans.lambda <= 1 && ans.mu >= 0 && ans.mu <= 1) {
+ if (ans.lambda == 0 || ans.lambda == 1 || ans.mu == 0 || ans.mu == 1) {
+ ans.kind = mpq2::isect_result::LINE_LINE_EXACT;
+ }
+ else {
+ ans.kind = mpq2::isect_result::LINE_LINE_CROSS;
+ }
+ }
+ else {
+ ans.kind = mpq2::isect_result::LINE_LINE_NONE;
+ }
+ }
+ return ans;
+}
+
+int mpq2::orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c)
+{
+ mpq_class detleft = (a[0] - c[0]) * (b[1] - c[1]);
+ mpq_class detright = (a[1] - c[1]) * (b[0] - c[0]);
+ mpq_class det = detleft - detright;
+ return sgn(det);
+}
+
+int mpq2::incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d)
+{
+ mpq_class adx = a[0] - d[0];
+ mpq_class bdx = b[0] - d[0];
+ mpq_class cdx = c[0] - d[0];
+ mpq_class ady = a[1] - d[1];
+ mpq_class bdy = b[1] - d[1];
+ mpq_class cdy = c[1] - d[1];
+
+ mpq_class bdxcdy = bdx * cdy;
+ mpq_class cdxbdy = cdx * bdy;
+ mpq_class alift = adx * adx + ady * ady;
+
+ mpq_class cdxady = cdx * ady;
+ mpq_class adxcdy = adx * cdy;
+ mpq_class blift = bdx * bdx + bdy * bdy;
+
+ mpq_class adxbdy = adx * bdy;
+ mpq_class bdxady = bdx * ady;
+ mpq_class clift = cdx * cdx + cdy * cdy;
+
+ mpq_class det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) +
+ clift * (adxbdy - bdxady);
+ return sgn(det);
+}
+
+int mpq3::orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d)
+{
+ mpq_class adx = a[0] - d[0];
+ mpq_class bdx = b[0] - d[0];
+ mpq_class cdx = c[0] - d[0];
+ mpq_class ady = a[1] - d[1];
+ mpq_class bdy = b[1] - d[1];
+ mpq_class cdy = c[1] - d[1];
+ mpq_class adz = a[2] - d[2];
+ mpq_class bdz = b[2] - d[2];
+ mpq_class cdz = c[2] - d[2];
+
+ mpq_class bdxcdy = bdx * cdy;
+ mpq_class cdxbdy = cdx * bdy;
+
+ mpq_class cdxady = cdx * ady;
+ mpq_class adxcdy = adx * cdy;
+
+ mpq_class adxbdy = adx * bdy;
+ mpq_class bdxady = bdx * ady;
+
+ mpq_class det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);
+ return sgn(det);
+}
+
+/* For double versions of orient and incircle functions, use robust predicates
+ * that give exact answers for double inputs.
+ * First, encapsulate functions frm Jonathan Shewchuk's implementation.
+ * After this namespace, see the implementation of the double3 primitives.
+ */
+namespace robust_pred {
+
+/* Using Shewchuk's file here, edited to removed unneeded functions,
+ * change REAL to double everywhere, added const to some arguments,
+ * and to export only the following declared non-static functions.
+ *
+ * Since this is C++, an instantiated singleton class is used to make
+ * sure that exactinit() is called once.
+ * (Because of undefinedness of when this is called in initialization of all
+ * modules, other modules shouldn't use these functions in initialization.)
+ */
+
+void exactinit();
+double orient2dfast(const double *pa, const double *pb, const double *pc);
+double orient2d(const double *pa, const double *pb, const double *pc);
+double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd);
+double orient3d(const double *pa, const double *pb, const double *pc, const double *pd);
+double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd);
+double incircle(const double *pa, const double *pb, const double *pc, const double *pd);
+double inspherefast(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe);
+double insphere(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe);
+
+class RobustInitCaller {
+ public:
+ RobustInitCaller()
+ {
+ exactinit();
+ }
+};
+
+static RobustInitCaller init_caller;
+
+/* Routines for Arbitrary Precision Floating-point Arithmetic
+ * and Fast Robust Geometric Predicates
+ * (predicates.c)
+ *
+ * May 18, 1996
+ *
+ * Placed in the public domain by
+ * Jonathan Richard Shewchuk
+ * School of Computer Science
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, Pennsylvania 15213-3891
+ * jrs@cs.cmu.edu
+ *
+ * This file contains C implementation of algorithms for exact addition
+ * and multiplication of floating-point numbers, and predicates for
+ * robustly performing the orientation and incircle tests used in
+ * computational geometry. The algorithms and underlying theory are
+ * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating-
+ * Point Arithmetic and Fast Robust Geometric Predicates." Technical
+ * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon
+ * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to
+ * Discrete & Computational Geometry.)
+ *
+ * This file, the paper listed above, and other information are available
+ * from the Web page http://www.cs.cmu.edu/~quake/robust.html .
+ *
+ *
+ * Using this code:
+ *
+ * First, read the short or long version of the paper (from the Web page
+ * above).
+ *
+ * Be sure to call exactinit() once, before calling any of the arithmetic
+ * functions or geometric predicates. Also be sure to turn on the
+ * optimizer when compiling this file.
+ */
+
+/* On some machines, the exact arithmetic routines might be defeated by the
+ * use of internal extended precision floating-point registers. Sometimes
+ * this problem can be fixed by defining certain values to be volatile,
+ * thus forcing them to be stored to memory and rounded off. This isn't
+ * a great solution, though, as it slows the arithmetic down.
+ *
+ * To try this out, write "#define INEXACT volatile" below. Normally,
+ * however, INEXACT should be defined to be nothing. ("#define INEXACT".)
+ */
+
+#define INEXACT /* Nothing */
+/* #define INEXACT volatile */
+
+/* Which of the following two methods of finding the absolute values is
+ * fastest is compiler-dependent. A few compilers can inline and optimize
+ * the fabs() call; but most will incur the overhead of a function call,
+ * which is disastrously slow. A faster way on IEEE machines might be to
+ * mask the appropriate bit, but that's difficult to do in C.
+ */
+
+#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
+/* #define Absolute(a) fabs(a) */
+
+/* Many of the operations are broken up into two pieces, a main part that
+ * performs an approximate operation, and a "tail" that computes the
+ * roundoff error of that operation.
+ *
+ * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),
+ * Split(), and Two_Product() are all implemented as described in the
+ * reference. Each of these macros requires certain variables to be
+ * defined in the calling routine. The variables `bvirt', `c', `abig',
+ * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because
+ * they store the result of an operation that may incur roundoff error.
+ * The input parameter `x' (or the highest numbered `x_' parameter) must
+ * also be declared `INEXACT'.
+ */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+ bvirt = x - a; \
+ y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+ x = (double)(a + b); \
+ Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Fast_Two_Diff_Tail(a, b, x, y) \
+ bvirt = a - x; \
+ y = bvirt - b
+
+#define Fast_Two_Diff(a, b, x, y) \
+ x = (double)(a - b); \
+ Fast_Two_Diff_Tail(a, b, x, y)
+
+#define Two_Sum_Tail(a, b, x, y) \
+ bvirt = (double)(x - a); \
+ avirt = x - bvirt; \
+ bround = b - bvirt; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+ x = (double)(a + b); \
+ Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+ bvirt = (double)(a - x); \
+ avirt = x + bvirt; \
+ bround = bvirt - b; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+ x = (double)(a - b); \
+ Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+ c = (double)(splitter * a); \
+ abig = (double)(c - a); \
+ ahi = c - abig; \
+ alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+ Split(a, ahi, alo); \
+ Split(b, bhi, blo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+ x = (double)(a * b); \
+ Two_Product_Tail(a, b, x, y)
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+ x = (double)(a * b); \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \
+ x = (double)(a * b); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Square_Tail(a, x, y) \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * ahi); \
+ err3 = err1 - ((ahi + ahi) * alo); \
+ y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+ x = (double)(a * a); \
+ Square_Tail(a, x, y)
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+ Two_Sum(a0, b, _i, x0); \
+ Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+ Two_Diff(a0, b, _i, x0); \
+ Two_Sum(a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+ Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+ Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b, _j, x1, x0); \
+ Two_One_Sum(a3, a2, _j, x4, x3, x2)
+
+#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \
+ Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \
+ Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1)
+
+#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \
+ Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2)
+
+#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Four_One_Sum(a3, a2, a1, a0, b, _j, x3, x2, x1, x0); \
+ Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4)
+
+#define Eight_Two_Sum( \
+ a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, _1, _0, x0); \
+ Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, x3, x2, x1)
+
+#define Eight_Four_Sum(a7, \
+ a6, \
+ a5, \
+ a4, \
+ a3, \
+ a2, \
+ a1, \
+ a0, \
+ b4, \
+ b3, \
+ b1, \
+ b0, \
+ x11, \
+ x10, \
+ x9, \
+ x8, \
+ x7, \
+ x6, \
+ x5, \
+ x4, \
+ x3, \
+ x2, \
+ x1, \
+ x0) \
+ Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, _2, _1, _0, x1, x0); \
+ Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, x7, x6, x5, x4, x3, x2)
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, x3, x2)
+
+#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, _i, x2); \
+ Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x3); \
+ Fast_Two_Sum(_j, _k, _i, x4); \
+ Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x5); \
+ Fast_Two_Sum(_j, _k, x7, x6)
+
+#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Split(a0, a0hi, a0lo); \
+ Split(b0, bhi, blo); \
+ Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \
+ Split(a1, a1hi, a1lo); \
+ Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, _1); \
+ Fast_Two_Sum(_j, _k, _l, _2); \
+ Split(b1, bhi, blo); \
+ Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \
+ Two_Sum(_1, _0, _k, x1); \
+ Two_Sum(_2, _k, _j, _1); \
+ Two_Sum(_l, _j, _m, _2); \
+ Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _n, _0); \
+ Two_Sum(_1, _0, _i, x2); \
+ Two_Sum(_2, _i, _k, _1); \
+ Two_Sum(_m, _k, _l, _2); \
+ Two_Sum(_j, _n, _k, _0); \
+ Two_Sum(_1, _0, _j, x3); \
+ Two_Sum(_2, _j, _i, _1); \
+ Two_Sum(_l, _i, _m, _2); \
+ Two_Sum(_1, _k, _i, x4); \
+ Two_Sum(_2, _i, _k, x5); \
+ Two_Sum(_m, _k, x7, x6)
+
+#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \
+ Square(a0, _j, x0); \
+ _0 = a0 + a0; \
+ Two_Product(a1, _0, _k, _1); \
+ Two_One_Sum(_k, _1, _j, _l, _2, x1); \
+ Square(a1, _j, _1); \
+ Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2)
+
+static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */
+static double epsilon; /* = 2^(-p). Used to estimate roundoff errors. */
+/* A set of coefficients used to calculate maximum roundoff errors. */
+static double resulterrbound;
+static double ccwerrboundA, ccwerrboundB, ccwerrboundC;
+static double o3derrboundA, o3derrboundB, o3derrboundC;
+static double iccerrboundA, iccerrboundB, iccerrboundC;
+static double isperrboundA, isperrboundB, isperrboundC;
+
+/*
+ * exactinit() Initialize the variables used for exact arithmetic.
+ *
+ * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in
+ * floating-point arithmetic. `epsilon' bounds the relative roundoff
+ * error. It is used for floating-point error analysis.
+ *
+ * `splitter' is used to split floating-point numbers into two half-
+ * length significands for exact multiplication.
+ *
+ * I imagine that a highly optimizing compiler might be too smart for its
+ * own good, and somehow cause this routine to fail, if it pretends that
+ * floating-point arithmetic is too much like real arithmetic.
+ *
+ * Don't change this routine unless you fully understand it.
+ */
+
+void exactinit()
+{
+ double half;
+ double check, lastcheck;
+ int every_other;
+
+ every_other = 1;
+ half = 0.5;
+ epsilon = 1.0;
+ splitter = 1.0;
+ check = 1.0;
+ /* Repeatedly divide `epsilon' by two until it is too small to add to */
+ /* one without causing roundoff. (Also check if the sum is equal to */
+ /* the previous sum, for machines that round up instead of using exact */
+ /* rounding. Not that this library will work on such machines anyway. */
+ do {
+ lastcheck = check;
+ epsilon *= half;
+ if (every_other) {
+ splitter *= 2.0;
+ }
+ every_other = !every_other;
+ check = 1.0 + epsilon;
+ } while ((check != 1.0) && (check != lastcheck));
+ splitter += 1.0;
+
+ /* Error bounds for orientation and incircle tests. */
+ resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+ ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+ ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+ ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+ o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+ o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+ o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+ iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+ iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+ iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+ isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
+ isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
+ isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
+}
+
+/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero
+ * components from the output expansion.
+ *
+ * Sets h = e + f. See the long version of my paper for details.
+ * h cannot be e or f.
+ */
+static int fast_expansion_sum_zeroelim(int elen, double *e, int flen, double *f, double *h)
+{
+ double Q;
+ INEXACT double Qnew;
+ INEXACT double hh;
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ int eindex, findex, hindex;
+ double enow, fnow;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ Q = enow;
+ enow = e[++eindex];
+ }
+ else {
+ Q = fnow;
+ fnow = f[++findex];
+ }
+ hindex = 0;
+ if ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Fast_Two_Sum(enow, Q, Qnew, hh);
+ enow = e[++eindex];
+ }
+ else {
+ Fast_Two_Sum(fnow, Q, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ while ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ }
+ else {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ }
+ while (eindex < elen) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ while (findex < flen) {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/* scale_expansion_zeroelim() Multiply an expansion by a scalar,
+ * eliminating zero components from the
+ * output expansion.
+ *
+ * Sets h = be. See either version of my paper for details.
+ * e and h cannot be the same.
+ */
+static int scale_expansion_zeroelim(int elen, double *e, double b, double *h)
+{
+ INEXACT double Q, sum;
+ double hh;
+ INEXACT double product1;
+ double product0;
+ int eindex, hindex;
+ double enow;
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+
+ Split(b, bhi, blo);
+ Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+ hindex = 0;
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ for (eindex = 1; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+ Two_Sum(Q, product0, sum, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ Fast_Two_Sum(product1, sum, Q, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/* estimate() Produce a one-word estimate of an expansion's value. */
+static double estimate(int elen, double *e)
+{
+ double Q;
+ int eindex;
+
+ Q = e[0];
+ for (eindex = 1; eindex < elen; eindex++) {
+ Q += e[eindex];
+ }
+ return Q;
+}
+
+/* orient2dfast() Approximate 2D orientation test. Nonrobust.
+ * orient2d() Adaptive exact 2D orientation test. Robust.
+ * Return a positive value if the points pa, pb, and pc occur
+ * in counterclockwise order; a negative value if they occur
+ * in clockwise order; and zero if they are collinear. The
+ * result is also a rough approximation of twice the signed
+ * area of the triangle defined by the three points.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In orient2d() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, orient2d() is usually quite
+ * fast, but will run more slowly when the input points are collinear or
+ * nearly so.
+ */
+
+double orient2dfast(const double *pa, const double *pb, const double *pc)
+{
+ double acx, bcx, acy, bcy;
+
+ acx = pa[0] - pc[0];
+ bcx = pb[0] - pc[0];
+ acy = pa[1] - pc[1];
+ bcy = pb[1] - pc[1];
+ return acx * bcy - acy * bcx;
+}
+
+static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum)
+{
+ INEXACT double acx, acy, bcx, bcy;
+ double acxtail, acytail, bcxtail, bcytail;
+ INEXACT double detleft, detright;
+ double detlefttail, detrighttail;
+ double det, errbound;
+ double B[4], C1[8], C2[12], D[16];
+ INEXACT double B3;
+ int C1length, C2length, Dlength;
+ double u[4];
+ INEXACT double u3;
+ INEXACT double s1, t1;
+ double s0, t0;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ acx = (double)(pa[0] - pc[0]);
+ bcx = (double)(pb[0] - pc[0]);
+ acy = (double)(pa[1] - pc[1]);
+ bcy = (double)(pb[1] - pc[1]);
+
+ Two_Product(acx, bcy, detleft, detlefttail);
+ Two_Product(acy, bcx, detright, detrighttail);
+
+ Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]);
+ B[3] = B3;
+
+ det = estimate(4, B);
+ errbound = ccwerrboundB * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+ Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+ Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+ Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+ if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) {
+ return det;
+ }
+
+ errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+ det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Product(acxtail, bcy, s1, s0);
+ Two_Product(acytail, bcx, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+ Two_Product(acx, bcytail, s1, s0);
+ Two_Product(acy, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+ Two_Product(acxtail, bcytail, s1, s0);
+ Two_Product(acytail, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+ return (D[Dlength - 1]);
+}
+
+double orient2d(const double *pa, const double *pb, const double *pc)
+{
+ double detleft, detright, det;
+ double detsum, errbound;
+
+ detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+ detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+ det = detleft - detright;
+
+ if (detleft > 0.0) {
+ if (detright <= 0.0) {
+ return det;
+ }
+ else {
+ detsum = detleft + detright;
+ }
+ }
+ else if (detleft < 0.0) {
+ if (detright >= 0.0) {
+ return det;
+ }
+ else {
+ detsum = -detleft - detright;
+ }
+ }
+ else {
+ return det;
+ }
+
+ errbound = ccwerrboundA * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ return orient2dadapt(pa, pb, pc, detsum);
+}
+
+/* orient3dfast() Approximate 3D orientation test. Nonrobust.
+ * orient3d() Adaptive exact 3D orientation test. Robust.
+ *
+ * Return a positive value if the point pd lies below the
+ * plane passing through pa, pb, and pc; "below" is defined so
+ * that pa, pb, and pc appear in counterclockwise order when
+ * viewed from above the plane. Returns a negative value if
+ * pd lies above the plane. Returns zero if the points are
+ * coplanar. The result is also a rough approximation of six
+ * times the signed volume of the tetrahedron defined by the
+ * four points.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In orient3d() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, orient3d() is usually quite
+ * fast, but will run more slowly when the input points are coplanar or
+ * nearly so.
+ */
+
+double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, bdx, cdx;
+ double ady, bdy, cdy;
+ double adz, bdz, cdz;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adz = pa[2] - pd[2];
+ bdz = pb[2] - pd[2];
+ cdz = pc[2] - pd[2];
+
+ return adx * (bdy * cdz - bdz * cdy) + bdx * (cdy * adz - cdz * ady) +
+ cdx * (ady * bdz - adz * bdy);
+}
+
+static double orient3dadapt(
+ const double *pa, const double *pb, const double *pc, const double *pd, double permanent)
+{
+ INEXACT double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+ double det, errbound;
+
+ INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ double bc[4], ca[4], ab[4];
+ INEXACT double bc3, ca3, ab3;
+ double adet[8], bdet[8], cdet[8];
+ int alen, blen, clen;
+ double abdet[16];
+ int ablen;
+ double *finnow, *finother, *finswap;
+ double fin1[192], fin2[192];
+ int finlength;
+
+ double adxtail, bdxtail, cdxtail;
+ double adytail, bdytail, cdytail;
+ double adztail, bdztail, cdztail;
+ INEXACT double at_blarge, at_clarge;
+ INEXACT double bt_clarge, bt_alarge;
+ INEXACT double ct_alarge, ct_blarge;
+ double at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+ int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+ INEXACT double bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+ INEXACT double adxt_cdy1, adxt_bdy1, bdxt_ady1;
+ double bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+ double adxt_cdy0, adxt_bdy0, bdxt_ady0;
+ INEXACT double bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+ INEXACT double adyt_cdx1, adyt_bdx1, bdyt_adx1;
+ double bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+ double adyt_cdx0, adyt_bdx0, bdyt_adx0;
+ double bct[8], cat[8], abt[8];
+ int bctlen, catlen, abtlen;
+ INEXACT double bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+ INEXACT double adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+ double bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+ double adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+ double u[4], v[12], w[16];
+ INEXACT double u3;
+ int vlength, wlength;
+ double negate;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j, _k;
+ double _0;
+
+ adx = (double)(pa[0] - pd[0]);
+ bdx = (double)(pb[0] - pd[0]);
+ cdx = (double)(pc[0] - pd[0]);
+ ady = (double)(pa[1] - pd[1]);
+ bdy = (double)(pb[1] - pd[1]);
+ cdy = (double)(pc[1] - pd[1]);
+ adz = (double)(pa[2] - pd[2]);
+ bdz = (double)(pb[2] - pd[2]);
+ cdz = (double)(pc[2] - pd[2]);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ alen = scale_expansion_zeroelim(4, bc, adz, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ blen = scale_expansion_zeroelim(4, ca, bdz, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ clen = scale_expansion_zeroelim(4, ab, cdz, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = o3derrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ Two_Diff_Tail(pa[2], pd[2], adz, adztail);
+ Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
+ Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
+
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) &&
+ (bdytail == 0.0) && (cdytail == 0.0) && (adztail == 0.0) && (bdztail == 0.0) &&
+ (cdztail == 0.0)) {
+ return det;
+ }
+
+ errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+ det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) +
+ adztail * (bdx * cdy - bdy * cdx)) +
+ (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) +
+ bdztail * (cdx * ady - cdy * adx)) +
+ (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) +
+ cdztail * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if (adxtail == 0.0) {
+ if (adytail == 0.0) {
+ at_b[0] = 0.0;
+ at_blen = 1;
+ at_c[0] = 0.0;
+ at_clen = 1;
+ }
+ else {
+ negate = -adytail;
+ Two_Product(negate, bdx, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ Two_Product(adytail, cdx, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ }
+ }
+ else {
+ if (adytail == 0.0) {
+ Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ negate = -adxtail;
+ Two_Product(negate, cdy, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ }
+ else {
+ Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+ Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+ Two_Two_Diff(
+ adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, at_blarge, at_b[2], at_b[1], at_b[0]);
+ at_b[3] = at_blarge;
+ at_blen = 4;
+ Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+ Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+ Two_Two_Diff(
+ adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, at_clarge, at_c[2], at_c[1], at_c[0]);
+ at_c[3] = at_clarge;
+ at_clen = 4;
+ }
+ }
+ if (bdxtail == 0.0) {
+ if (bdytail == 0.0) {
+ bt_c[0] = 0.0;
+ bt_clen = 1;
+ bt_a[0] = 0.0;
+ bt_alen = 1;
+ }
+ else {
+ negate = -bdytail;
+ Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ }
+ }
+ else {
+ if (bdytail == 0.0) {
+ Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ negate = -bdxtail;
+ Two_Product(negate, ady, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ }
+ else {
+ Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+ Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+ Two_Two_Diff(
+ bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+ bt_c[3] = bt_clarge;
+ bt_clen = 4;
+ Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+ Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+ Two_Two_Diff(
+ bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+ bt_a[3] = bt_alarge;
+ bt_alen = 4;
+ }
+ }
+ if (cdxtail == 0.0) {
+ if (cdytail == 0.0) {
+ ct_a[0] = 0.0;
+ ct_alen = 1;
+ ct_b[0] = 0.0;
+ ct_blen = 1;
+ }
+ else {
+ negate = -cdytail;
+ Two_Product(negate, adx, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ }
+ }
+ else {
+ if (cdytail == 0.0) {
+ Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ negate = -cdxtail;
+ Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ }
+ else {
+ Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+ Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+ Two_Two_Diff(
+ cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+ ct_a[3] = ct_alarge;
+ ct_alen = 4;
+ Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+ Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+ Two_Two_Diff(
+ cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+ ct_b[3] = ct_blarge;
+ ct_blen = 4;
+ }
+ }
+
+ bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+ wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+ wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ if (adztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, bc, adztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ if (adxtail != 0.0) {
+ if (bdytail != 0.0) {
+ Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (cdztail != 0.0) {
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if (cdytail != 0.0) {
+ negate = -adxtail;
+ Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (bdztail != 0.0) {
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ }
+ if (bdxtail != 0.0) {
+ if (cdytail != 0.0) {
+ Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (adztail != 0.0) {
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if (adytail != 0.0) {
+ negate = -bdxtail;
+ Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (cdztail != 0.0) {
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ }
+ if (cdxtail != 0.0) {
+ if (adytail != 0.0) {
+ Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (bdztail != 0.0) {
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if (bdytail != 0.0) {
+ negate = -cdxtail;
+ Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (adztail != 0.0) {
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ }
+
+ if (adztail != 0.0) {
+ wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdztail != 0.0) {
+ wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdztail != 0.0) {
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ return finnow[finlength - 1];
+}
+
+double orient3d(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+ double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ double det;
+ double permanent, errbound;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adz = pa[2] - pd[2];
+ bdz = pb[2] - pd[2];
+ cdz = pc[2] - pd[2];
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+
+ det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) +
+ (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) +
+ (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
+ errbound = o3derrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return orient3dadapt(pa, pb, pc, pd, permanent);
+}
+
+/* incirclefast() Approximate 2D incircle test. Nonrobust.
+ * incircle()
+ *
+ * Return a positive value if the point pd lies inside the
+ * circle passing through pa, pb, and pc; a negative value if
+ * it lies outside; and zero if the four points are cocircular.
+ * The points pa, pb, and pc must be in counterclockwise
+ * order, or the sign of the result will be reversed.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In incircle() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, incircle() is usually quite
+ * fast, but will run more slowly when the input points are cocircular or
+ * nearly so.
+ */
+
+double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, ady, bdx, bdy, cdx, cdy;
+ double abdet, bcdet, cadet;
+ double alift, blift, clift;
+
+ adx = pa[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdx = pb[0] - pd[0];
+ bdy = pb[1] - pd[1];
+ cdx = pc[0] - pd[0];
+ cdy = pc[1] - pd[1];
+
+ abdet = adx * bdy - bdx * ady;
+ bcdet = bdx * cdy - cdx * bdy;
+ cadet = cdx * ady - adx * cdy;
+ alift = adx * adx + ady * ady;
+ blift = bdx * bdx + bdy * bdy;
+ clift = cdx * cdx + cdy * cdy;
+
+ return alift * bcdet + blift * cadet + clift * abdet;
+}
+
+static double incircleadapt(
+ const double *pa, const double *pb, const double *pc, const double *pd, double permanent)
+{
+ INEXACT double adx, bdx, cdx, ady, bdy, cdy;
+ double det, errbound;
+
+ INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ double bc[4], ca[4], ab[4];
+ INEXACT double bc3, ca3, ab3;
+ double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+ int axbclen, axxbclen, aybclen, ayybclen, alen;
+ double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+ int bxcalen, bxxcalen, bycalen, byycalen, blen;
+ double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+ int cxablen, cxxablen, cyablen, cyyablen, clen;
+ double abdet[64];
+ int ablen;
+ double fin1[1152], fin2[1152];
+ double *finnow, *finother, *finswap;
+ int finlength;
+
+ double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+ INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+ double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+ double aa[4], bb[4], cc[4];
+ INEXACT double aa3, bb3, cc3;
+ INEXACT double ti1, tj1;
+ double ti0, tj0;
+ double u[4], v[4];
+ INEXACT double u3, v3;
+ double temp8[8], temp16a[16], temp16b[16], temp16c[16];
+ double temp32a[32], temp32b[32], temp48[48], temp64[64];
+ int temp8len, temp16alen, temp16blen, temp16clen;
+ int temp32alen, temp32blen, temp48len, temp64len;
+ double axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+ int axtbblen, axtcclen, aytbblen, aytcclen;
+ double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+ int bxtaalen, bxtcclen, bytaalen, bytcclen;
+ double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+ int cxtaalen, cxtbblen, cytaalen, cytbblen;
+ double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+ int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
+ double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+ int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+ double axtbctt[8], aytbctt[8], bxtcatt[8];
+ double bytcatt[8], cxtabtt[8], cytabtt[8];
+ int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+ double abt[8], bct[8], cat[8];
+ int abtlen, bctlen, catlen;
+ double abtt[4], bctt[4], catt[4];
+ int abttlen, bcttlen, cattlen;
+ INEXACT double abtt3, bctt3, catt3;
+ double negate;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ adx = (double)(pa[0] - pd[0]);
+ bdx = (double)(pb[0] - pd[0]);
+ cdx = (double)(pc[0] - pd[0]);
+ ady = (double)(pa[1] - pd[1]);
+ bdy = (double)(pb[1] - pd[1]);
+ cdy = (double)(pc[1] - pd[1]);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+ axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+ aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+ ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+ alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+ bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+ bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+ byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+ blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+ cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+ cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+ cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+ clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = iccerrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) &&
+ (bdytail == 0.0) && (cdytail == 0.0)) {
+ return det;
+ }
+
+ errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+ det += ((adx * adx + ady * ady) *
+ ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) +
+ 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) +
+ ((bdx * bdx + bdy * bdy) *
+ ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) +
+ 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) +
+ ((cdx * cdx + cdy * cdy) *
+ ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) +
+ 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
+ Square(adx, adxadx1, adxadx0);
+ Square(ady, adyady1, adyady0);
+ Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+ aa[3] = aa3;
+ }
+ if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
+ Square(bdx, bdxbdx1, bdxbdx0);
+ Square(bdy, bdybdy1, bdybdy0);
+ Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+ bb[3] = bb3;
+ }
+ if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
+ Square(cdx, cdxcdx1, cdxcdx0);
+ Square(cdy, cdycdy1, cdycdy0);
+ Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+ cc[3] = cc3;
+ }
+
+ if (adxtail != 0.0) {
+ axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+ temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a);
+
+ axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+ temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+ axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+ temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (adytail != 0.0) {
+ aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+ temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a);
+
+ aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+ temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+ aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+ temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdxtail != 0.0) {
+ bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+ temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a);
+
+ bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+ temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+ bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+ temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+ temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a);
+
+ bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+ temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+ bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+ temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdxtail != 0.0) {
+ cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+ temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a);
+
+ cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+ temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+ cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+ temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+ temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a);
+
+ cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+ temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+ cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+ temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ if ((adxtail != 0.0) || (adytail != 0.0)) {
+ if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) {
+ Two_Product(bdxtail, cdy, ti1, ti0);
+ Two_Product(bdx, cdytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -bdy;
+ Two_Product(cdxtail, negate, ti1, ti0);
+ negate = -bdytail;
+ Two_Product(cdx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+ Two_Product(bdxtail, cdytail, ti1, ti0);
+ Two_Product(cdxtail, bdytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+ bctt[3] = bctt3;
+ bcttlen = 4;
+ }
+ else {
+ bct[0] = 0.0;
+ bctlen = 1;
+ bctt[0] = 0.0;
+ bcttlen = 1;
+ }
+
+ if (adxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+ axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+ temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (bdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a);
+ axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+ temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a);
+ temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (adytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+ aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+ temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a);
+ aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+ temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a);
+ temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if ((bdxtail != 0.0) || (bdytail != 0.0)) {
+ if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) {
+ Two_Product(cdxtail, ady, ti1, ti0);
+ Two_Product(cdx, adytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -cdy;
+ Two_Product(adxtail, negate, ti1, ti0);
+ negate = -cdytail;
+ Two_Product(adx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+ Two_Product(cdxtail, adytail, ti1, ti0);
+ Two_Product(adxtail, cdytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+ catt[3] = catt3;
+ cattlen = 4;
+ }
+ else {
+ cat[0] = 0.0;
+ catlen = 1;
+ catt[0] = 0.0;
+ cattlen = 1;
+ }
+
+ if (bdxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+ bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+ temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (cdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (adytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a);
+ bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+ temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a);
+ temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+ bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+ temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a);
+ bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+ temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a);
+ temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+ if ((cdxtail != 0.0) || (cdytail != 0.0)) {
+ if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) {
+ Two_Product(adxtail, bdy, ti1, ti0);
+ Two_Product(adx, bdytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -ady;
+ Two_Product(bdxtail, negate, ti1, ti0);
+ negate = -adytail;
+ Two_Product(bdx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+ Two_Product(adxtail, bdytail, ti1, ti0);
+ Two_Product(bdxtail, adytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+ abtt[3] = abtt3;
+ abttlen = 4;
+ }
+ else {
+ abt[0] = 0.0;
+ abtlen = 1;
+ abtt[0] = 0.0;
+ abttlen = 1;
+ }
+
+ if (cdxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+ cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+ temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ if (adytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a);
+ cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+ temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a);
+ temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+ cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+ temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+
+ temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a);
+ cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+ temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a);
+ temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother);
+ finswap = finnow;
+ finnow = finother;
+ finother = finswap;
+ }
+ }
+
+ return finnow[finlength - 1];
+}
+
+double incircle(const double *pa, const double *pb, const double *pc, const double *pd)
+{
+ double adx, bdx, cdx, ady, bdy, cdy;
+ double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ double alift, blift, clift;
+ double det;
+ double permanent, errbound;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+ alift = adx * adx + ady * ady;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+ blift = bdx * bdx + bdy * bdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+ clift = cdx * cdx + cdy * cdy;
+
+ det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift +
+ (Absolute(cdxady) + Absolute(adxcdy)) * blift +
+ (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+ errbound = iccerrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return incircleadapt(pa, pb, pc, pd, permanent);
+}
+
+/* inspherefast() Approximate 3D insphere test. Nonrobust.
+ * insphere() Adaptive exact 3D insphere test. Robust.
+ *
+ * Return a positive value if the point pe lies inside the
+ * sphere passing through pa, pb, pc, and pd; a negative value
+ * if it lies outside; and zero if the five points are
+ * cospherical. The points pa, pb, pc, and pd must be ordered
+ * so that they have a positive orientation (as defined by
+ * orient3d()), or the sign of the result will be reversed.
+ *
+ * The second uses exact arithmetic to ensure a correct answer. The
+ * result returned is the determinant of a matrix. In insphere() only,
+ * this determinant is computed adaptively, in the sense that exact
+ * arithmetic is used only to the degree it is needed to ensure that the
+ * returned value has the correct sign. Hence, insphere() is usually quite
+ * fast, but will run more slowly when the input points are cospherical or
+ * nearly so.
+ */
+
+double inspherefast(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe)
+{
+ double aex, bex, cex, dex;
+ double aey, bey, cey, dey;
+ double aez, bez, cez, dez;
+ double alift, blift, clift, dlift;
+ double ab, bc, cd, da, ac, bd;
+ double abc, bcd, cda, dab;
+
+ aex = pa[0] - pe[0];
+ bex = pb[0] - pe[0];
+ cex = pc[0] - pe[0];
+ dex = pd[0] - pe[0];
+ aey = pa[1] - pe[1];
+ bey = pb[1] - pe[1];
+ cey = pc[1] - pe[1];
+ dey = pd[1] - pe[1];
+ aez = pa[2] - pe[2];
+ bez = pb[2] - pe[2];
+ cez = pc[2] - pe[2];
+ dez = pd[2] - pe[2];
+
+ ab = aex * bey - bex * aey;
+ bc = bex * cey - cex * bey;
+ cd = cex * dey - dex * cey;
+ da = dex * aey - aex * dey;
+
+ ac = aex * cey - cex * aey;
+ bd = bex * dey - dex * bey;
+
+ abc = aez * bc - bez * ac + cez * ab;
+ bcd = bez * cd - cez * bd + dez * bc;
+ cda = cez * da + dez * ac + aez * cd;
+ dab = dez * ab + aez * bd + bez * da;
+
+ alift = aex * aex + aey * aey + aez * aez;
+ blift = bex * bex + bey * bey + bez * bez;
+ clift = cex * cex + cey * cey + cez * cez;
+ dlift = dex * dex + dey * dey + dez * dez;
+
+ return (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+}
+
+static double insphereexact(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe)
+{
+ INEXACT double axby1, bxcy1, cxdy1, dxey1, exay1;
+ INEXACT double bxay1, cxby1, dxcy1, exdy1, axey1;
+ INEXACT double axcy1, bxdy1, cxey1, dxay1, exby1;
+ INEXACT double cxay1, dxby1, excy1, axdy1, bxey1;
+ double axby0, bxcy0, cxdy0, dxey0, exay0;
+ double bxay0, cxby0, dxcy0, exdy0, axey0;
+ double axcy0, bxdy0, cxey0, dxay0, exby0;
+ double cxay0, dxby0, excy0, axdy0, bxey0;
+ double ab[4], bc[4], cd[4], de[4], ea[4];
+ double ac[4], bd[4], ce[4], da[4], eb[4];
+ double temp8a[8], temp8b[8], temp16[16];
+ int temp8alen, temp8blen, temp16len;
+ double abc[24], bcd[24], cde[24], dea[24], eab[24];
+ double abd[24], bce[24], cda[24], deb[24], eac[24];
+ int abclen, bcdlen, cdelen, dealen, eablen;
+ int abdlen, bcelen, cdalen, deblen, eaclen;
+ double temp48a[48], temp48b[48];
+ int temp48alen, temp48blen;
+ double abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
+ int abcdlen, bcdelen, cdealen, deablen, eabclen;
+ double temp192[192];
+ double det384x[384], det384y[384], det384z[384];
+ int xlen, ylen, zlen;
+ double detxy[768];
+ int xylen;
+ double adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152];
+ int alen, blen, clen, dlen, elen;
+ double abdet[2304], cddet[2304], cdedet[3456];
+ int ablen, cdlen;
+ double deter[5760];
+ int deterlen;
+ int i;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ Two_Product(pa[0], pb[1], axby1, axby0);
+ Two_Product(pb[0], pa[1], bxay1, bxay0);
+ Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+ Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+ Two_Product(pc[0], pb[1], cxby1, cxby0);
+ Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+ Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+ Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+ Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+ Two_Product(pd[0], pe[1], dxey1, dxey0);
+ Two_Product(pe[0], pd[1], exdy1, exdy0);
+ Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
+
+ Two_Product(pe[0], pa[1], exay1, exay0);
+ Two_Product(pa[0], pe[1], axey1, axey0);
+ Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
+
+ Two_Product(pa[0], pc[1], axcy1, axcy0);
+ Two_Product(pc[0], pa[1], cxay1, cxay0);
+ Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+ Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+ Two_Product(pd[0], pb[1], dxby1, dxby0);
+ Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+ Two_Product(pc[0], pe[1], cxey1, cxey0);
+ Two_Product(pe[0], pc[1], excy1, excy0);
+ Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
+
+ Two_Product(pd[0], pa[1], dxay1, dxay0);
+ Two_Product(pa[0], pd[1], axdy1, axdy0);
+ Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+ Two_Product(pe[0], pb[1], exby1, exby0);
+ Two_Product(pb[0], pe[1], bxey1, bxey0);
+ Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
+
+ temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
+ abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abc);
+
+ temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
+ bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bcd);
+
+ temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
+ cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cde);
+
+ temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
+ dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, dea);
+
+ temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
+ eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eab);
+
+ temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
+ abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abd);
+
+ temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
+ bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bce);
+
+ temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
+ cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cda);
+
+ temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
+ deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, deb);
+
+ temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
+ eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eac);
+
+ temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, bcde);
+ xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x);
+ ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y);
+ zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet);
+
+ temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, cdea);
+ xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x);
+ ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y);
+ zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet);
+
+ temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, deab);
+ xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x);
+ ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y);
+ zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet);
+
+ temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, eabc);
+ xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x);
+ ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y);
+ zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet);
+
+ temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
+ temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
+ for (i = 0; i < temp48blen; i++) {
+ temp48b[i] = -temp48b[i];
+ }
+ abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, abcd);
+ xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192);
+ xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x);
+ ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192);
+ ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y);
+ zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192);
+ zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z);
+ xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+ elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+ cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
+ deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
+
+ return deter[deterlen - 1];
+}
+
+static double insphereadapt(const double *pa,
+ const double *pb,
+ const double *pc,
+ const double *pd,
+ const double *pe,
+ double permanent)
+{
+ INEXACT double aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+ double det, errbound;
+
+ INEXACT double aexbey1, bexaey1, bexcey1, cexbey1;
+ INEXACT double cexdey1, dexcey1, dexaey1, aexdey1;
+ INEXACT double aexcey1, cexaey1, bexdey1, dexbey1;
+ double aexbey0, bexaey0, bexcey0, cexbey0;
+ double cexdey0, dexcey0, dexaey0, aexdey0;
+ double aexcey0, cexaey0, bexdey0, dexbey0;
+ double ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+ INEXACT double ab3, bc3, cd3, da3, ac3, bd3;
+ double abeps, bceps, cdeps, daeps, aceps, bdeps;
+ double temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48];
+ int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len;
+ double xdet[96], ydet[96], zdet[96], xydet[192];
+ int xlen, ylen, zlen, xylen;
+ double adet[288], bdet[288], cdet[288], ddet[288];
+ int alen, blen, clen, dlen;
+ double abdet[576], cddet[576];
+ int ablen, cdlen;
+ double fin1[1152];
+ int finlength;
+
+ double aextail, bextail, cextail, dextail;
+ double aeytail, beytail, ceytail, deytail;
+ double aeztail, beztail, ceztail, deztail;
+
+ INEXACT double bvirt;
+ double avirt, bround, around;
+ INEXACT double c;
+ INEXACT double abig;
+ double ahi, alo, bhi, blo;
+ double err1, err2, err3;
+ INEXACT double _i, _j;
+ double _0;
+
+ aex = (double)(pa[0] - pe[0]);
+ bex = (double)(pb[0] - pe[0]);
+ cex = (double)(pc[0] - pe[0]);
+ dex = (double)(pd[0] - pe[0]);
+ aey = (double)(pa[1] - pe[1]);
+ bey = (double)(pb[1] - pe[1]);
+ cey = (double)(pc[1] - pe[1]);
+ dey = (double)(pd[1] - pe[1]);
+ aez = (double)(pa[2] - pe[2]);
+ bez = (double)(pb[2] - pe[2]);
+ cez = (double)(pc[2] - pe[2]);
+ dez = (double)(pd[2] - pe[2]);
+
+ Two_Product(aex, bey, aexbey1, aexbey0);
+ Two_Product(bex, aey, bexaey1, bexaey0);
+ Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+
+ Two_Product(bex, cey, bexcey1, bexcey0);
+ Two_Product(cex, bey, cexbey1, cexbey0);
+ Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+
+ Two_Product(cex, dey, cexdey1, cexdey0);
+ Two_Product(dex, cey, dexcey1, dexcey0);
+ Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
+ cd[3] = cd3;
+
+ Two_Product(dex, aey, dexaey1, dexaey0);
+ Two_Product(aex, dey, aexdey1, aexdey0);
+ Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
+ da[3] = da3;
+
+ Two_Product(aex, cey, aexcey1, aexcey0);
+ Two_Product(cex, aey, cexaey1, cexaey0);
+ Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
+ ac[3] = ac3;
+
+ Two_Product(bex, dey, bexdey1, bexdey0);
+ Two_Product(dex, bey, dexbey1, dexbey0);
+ Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
+ bd[3] = bd3;
+
+ temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet);
+
+ temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet);
+
+ temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet);
+
+ temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
+ temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
+ temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
+ temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16);
+ temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48);
+ xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48);
+ ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet);
+ temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48);
+ zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet);
+ xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+ dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = isperrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pe[0], aex, aextail);
+ Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
+ Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
+ Two_Diff_Tail(pb[0], pe[0], bex, bextail);
+ Two_Diff_Tail(pb[1], pe[1], bey, beytail);
+ Two_Diff_Tail(pb[2], pe[2], bez, beztail);
+ Two_Diff_Tail(pc[0], pe[0], cex, cextail);
+ Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
+ Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
+ Two_Diff_Tail(pd[0], pe[0], dex, dextail);
+ Two_Diff_Tail(pd[1], pe[1], dey, deytail);
+ Two_Diff_Tail(pd[2], pe[2], dez, deztail);
+ if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) && (bextail == 0.0) &&
+ (beytail == 0.0) && (beztail == 0.0) && (cextail == 0.0) && (ceytail == 0.0) &&
+ (ceztail == 0.0) && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) {
+ return det;
+ }
+
+ errbound = isperrboundC * permanent + resulterrbound * Absolute(det);
+ abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail);
+ bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail);
+ cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail);
+ daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail);
+ aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail);
+ bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail);
+ det +=
+ (((bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) +
+ (ceztail * da3 + deztail * ac3 + aeztail * cd3)) +
+ (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) +
+ (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) -
+ ((aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) +
+ (beztail * cd3 - ceztail * bd3 + deztail * bc3)) +
+ (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) +
+ (deztail * ab3 + aeztail * bd3 + beztail * da3)))) +
+ 2.0 *
+ (((bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3) +
+ (dex * dextail + dey * deytail + dez * deztail) *
+ (aez * bc3 - bez * ac3 + cez * ab3)) -
+ ((aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3) +
+ (cex * cextail + cey * ceytail + cez * ceztail) *
+ (dez * ab3 + aez * bd3 + bez * da3)));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ return insphereexact(pa, pb, pc, pd, pe);
+}
+
+double insphere(
+ const double *pa, const double *pb, const double *pc, const double *pd, const double *pe)
+{
+ double aex, bex, cex, dex;
+ double aey, bey, cey, dey;
+ double aez, bez, cez, dez;
+ double aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
+ double aexcey, cexaey, bexdey, dexbey;
+ double alift, blift, clift, dlift;
+ double ab, bc, cd, da, ac, bd;
+ double abc, bcd, cda, dab;
+ double aezplus, bezplus, cezplus, dezplus;
+ double aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
+ double cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
+ double aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
+ double det;
+ double permanent, errbound;
+
+ aex = pa[0] - pe[0];
+ bex = pb[0] - pe[0];
+ cex = pc[0] - pe[0];
+ dex = pd[0] - pe[0];
+ aey = pa[1] - pe[1];
+ bey = pb[1] - pe[1];
+ cey = pc[1] - pe[1];
+ dey = pd[1] - pe[1];
+ aez = pa[2] - pe[2];
+ bez = pb[2] - pe[2];
+ cez = pc[2] - pe[2];
+ dez = pd[2] - pe[2];
+
+ aexbey = aex * bey;
+ bexaey = bex * aey;
+ ab = aexbey - bexaey;
+ bexcey = bex * cey;
+ cexbey = cex * bey;
+ bc = bexcey - cexbey;
+ cexdey = cex * dey;
+ dexcey = dex * cey;
+ cd = cexdey - dexcey;
+ dexaey = dex * aey;
+ aexdey = aex * dey;
+ da = dexaey - aexdey;
+
+ aexcey = aex * cey;
+ cexaey = cex * aey;
+ ac = aexcey - cexaey;
+ bexdey = bex * dey;
+ dexbey = dex * bey;
+ bd = bexdey - dexbey;
+
+ abc = aez * bc - bez * ac + cez * ab;
+ bcd = bez * cd - cez * bd + dez * bc;
+ cda = cez * da + dez * ac + aez * cd;
+ dab = dez * ab + aez * bd + bez * da;
+
+ alift = aex * aex + aey * aey + aez * aez;
+ blift = bex * bex + bey * bey + bez * bez;
+ clift = cex * cex + cey * cey + cez * cez;
+ dlift = dex * dex + dey * dey + dez * dez;
+
+ det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+
+ aezplus = Absolute(aez);
+ bezplus = Absolute(bez);
+ cezplus = Absolute(cez);
+ dezplus = Absolute(dez);
+ aexbeyplus = Absolute(aexbey);
+ bexaeyplus = Absolute(bexaey);
+ bexceyplus = Absolute(bexcey);
+ cexbeyplus = Absolute(cexbey);
+ cexdeyplus = Absolute(cexdey);
+ dexceyplus = Absolute(dexcey);
+ dexaeyplus = Absolute(dexaey);
+ aexdeyplus = Absolute(aexdey);
+ aexceyplus = Absolute(aexcey);
+ cexaeyplus = Absolute(cexaey);
+ bexdeyplus = Absolute(bexdey);
+ dexbeyplus = Absolute(dexbey);
+ permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus +
+ (bexceyplus + cexbeyplus) * dezplus) *
+ alift +
+ ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus +
+ (cexdeyplus + dexceyplus) * aezplus) *
+ blift +
+ ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus +
+ (dexaeyplus + aexdeyplus) * bezplus) *
+ clift +
+ ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus +
+ (aexbeyplus + bexaeyplus) * cezplus) *
+ dlift;
+ errbound = isperrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return insphereadapt(pa, pb, pc, pd, pe, permanent);
+}
+
+} /* namespace robust_pred */
+
+static int sgn(double x)
+{
+ return (x > 0) ? 1 : ((x < 0) ? -1 : 0);
+}
+
+int double2::orient2d(const double2 &a, const double2 &b, const double2 &c)
+{
+ return sgn(BLI::robust_pred::orient2d(a, b, c));
+}
+
+int double2::orient2d_fast(const double2 &a, const double2 &b, const double2 &c)
+{
+ return sgn(BLI::robust_pred::orient2dfast(a, b, c));
+}
+
+int double2::incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d)
+{
+ return sgn(robust_pred::incircle(a, b, c, d));
+}
+
+int double2::incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d)
+{
+ return sgn(robust_pred::incirclefast(a, b, c, d));
+}
+
+int double3::orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
+{
+ return sgn(robust_pred::orient3d(a, b, c, d));
+}
+
+int double3::orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
+{
+ return sgn(robust_pred::orient3dfast(a, b, c, d));
+}
+
+int double3::insphere(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e)
+{
+ return sgn(robust_pred::insphere(a, b, c, d, e));
+}
+
+int double3::insphere_fast(
+ const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e)
+{
+ return sgn(robust_pred::inspherefast(a, b, c, d, e));
+}
+
+} // namespace BLI
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index 9439d0890b5..6df93efd3d7 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -745,16 +745,6 @@ void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const floa
out[2] = p[2] - (mul * v_plane[2]);
}
-void project_plane_normalized_v3_v3v3_db(double out[3], const double p[3], const double v_plane[3])
-{
- BLI_ASSERT_UNIT_V3_DB(v_plane);
- const double mul = dot_v3v3_db(p, v_plane);
-
- out[0] = p[0] - (mul * v_plane[0]);
- out[1] = p[1] - (mul * v_plane[1]);
- out[2] = p[2] - (mul * v_plane[2]);
-}
-
void project_plane_normalized_v2_v2v2(float out[2], const float p[2], const float v_plane[2])
{
BLI_ASSERT_UNIT_V2(v_plane);
@@ -817,6 +807,17 @@ void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3])
out[2] = v[2] - (dot2 * normal[2]);
}
+void reflect_v3_v3v3_db(double out[3], const double v[3], const double normal[3])
+{
+ const double dot2 = 2.0 * dot_v3v3_db(v, normal);
+
+ BLI_ASSERT_UNIT_V3_DB(normal);
+
+ out[0] = v[0] - (dot2 * normal[0]);
+ out[1] = v[1] - (dot2 * normal[1]);
+ out[2] = v[2] - (dot2 * normal[2]);
+}
+
/**
* Takes a vector and computes 2 orthogonal directions.
*
@@ -847,31 +848,6 @@ void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3])
}
}
-void ortho_basis_v3v3_v3_db(double r_n1[3], double r_n2[3], const double n[3])
-{
- const double eps = FLT_EPSILON;
- const double f = len_squared_v2_db(n);
-
- if (f > eps) {
- const double d = 1.0 / sqrt(f);
-
- BLI_assert(isfinite(d));
-
- r_n1[0] = n[1] * d;
- r_n1[1] = -n[0] * d;
- r_n1[2] = 0.0;
- r_n2[0] = -n[2] * r_n1[1];
- r_n2[1] = n[2] * r_n1[0];
- r_n2[2] = n[0] * r_n1[1] - n[1] * r_n1[0];
- }
- else {
- /* degenerate case */
- r_n1[0] = (n[2] < 0.0f) ? -1.0f : 1.0f;
- r_n1[1] = r_n1[2] = r_n2[0] = r_n2[2] = 0.0f;
- r_n2[1] = 1.0;
- }
-}
-
/**
* Calculates \a p - a perpendicular vector to \a v
*
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index dd4a7972c54..c5d4878bbc5 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -232,21 +232,6 @@ MINLINE void zero_v3_db(double r[3])
r[2] = 0.0;
}
-MINLINE void zero_v4_db(double r[4])
-{
- r[0] = 0.0;
- r[1] = 0.0;
- r[2] = 0.0;
- r[3] = 0.0;
-}
-
-MINLINE void copy_v3_db(double r[3], double d)
-{
- r[0] = d;
- r[1] = d;
- r[2] = d;
-}
-
MINLINE void copy_v2_v2_db(double r[2], const double a[2])
{
r[0] = a[0];
@@ -442,13 +427,6 @@ MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
r[2] = a[2] + b[2];
}
-MINLINE void add_v3_v3v3_db(double r[3], const double a[3], const double b[3])
-{
- r[0] = a[0] + b[0];
- r[1] = a[1] + b[1];
- r[2] = a[2] + b[2];
-}
-
MINLINE void add_v3fl_v3fl_v3i(float r[3], const float a[3], const int b[3])
{
r[0] = a[0] + (float)b[0];
@@ -587,7 +565,7 @@ MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
r[2] = a[2] * f;
}
-MINLINE void mul_v3db_v3dbdb(double r[3], const double a[3], double f)
+MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f)
{
r[0] = a[0] * f;
r[1] = a[1] * f;
@@ -719,13 +697,6 @@ MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
r[2] += a[2] * f;
}
-MINLINE void madd_v3db_v3dbdb(double r[3], const double a[3], double f)
-{
- r[0] += a[0] * f;
- r[1] += a[1] * f;
- r[2] += a[2] * f;
-}
-
MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3])
{
r[0] += a[0] * b[0];
@@ -733,13 +704,6 @@ MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3])
r[2] += a[2] * b[2];
}
-MINLINE void madd_v3_v3v3_db(double r[3], const double a[3], const double b[3])
-{
- r[0] += a[0] * b[0];
- r[1] += a[1] * b[1];
- r[2] += a[2] * b[2];
-}
-
MINLINE void madd_v2_v2v2fl(float r[2], const float a[2], const float b[2], float f)
{
r[0] = a[0] + b[0] * f;
@@ -753,13 +717,6 @@ MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], floa
r[2] = a[2] + b[2] * f;
}
-MINLINE void madd_v3_v3v3db_db(double r[3], const double a[3], const double b[3], double f)
-{
- r[0] = a[0] + b[0] * f;
- r[1] = a[1] + b[1] * f;
- r[2] = a[2] + b[2] * f;
-}
-
MINLINE void madd_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3])
{
r[0] = a[0] + b[0] * c[0];
@@ -1022,11 +979,6 @@ MINLINE float len_squared_v2(const float v[2])
return v[0] * v[0] + v[1] * v[1];
}
-MINLINE double len_squared_v2_db(const double v[2])
-{
- return v[0] * v[0] + v[1] * v[1];
-}
-
MINLINE float len_squared_v3(const float v[3])
{
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
@@ -1057,6 +1009,11 @@ MINLINE float len_v2(const float v[2])
return sqrtf(v[0] * v[0] + v[1] * v[1]);
}
+MINLINE double len_v2_db(const double v[2])
+{
+ return sqrt(v[0] * v[0] + v[1] * v[1]);
+}
+
MINLINE float len_v2v2(const float v1[2], const float v2[2])
{
float x, y;
@@ -1118,14 +1075,6 @@ MINLINE float len_squared_v3v3(const float a[3], const float b[3])
return dot_v3v3(d, d);
}
-MINLINE double len_squared_v3v3_db(const double a[3], const double b[3])
-{
- double d[3];
-
- sub_v3_v3v3_db(d, b, a);
- return dot_v3v3_db(d, d);
-}
-
MINLINE float len_squared_v4v4(const float a[4], const float b[4])
{
float d[4];
@@ -1166,14 +1115,6 @@ MINLINE float len_v3v3(const float a[3], const float b[3])
return len_v3(d);
}
-MINLINE double len_v3v3_db(const double a[3], const double b[3])
-{
- double d[3];
-
- sub_v3_v3v3_db(d, b, a);
- return len_v3_db(d);
-}
-
MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_length)
{
float d = dot_v2v2(a, a);
@@ -1226,7 +1167,29 @@ MINLINE float normalize_v3_v3(float r[3], const float a[3])
return normalize_v3_v3_length(r, a, 1.0f);
}
-MINLINE double normalize_v3_length_d(double n[3], const double unit_length)
+MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], double unit_length)
+{
+ double d = dot_v3v3_db(a, a);
+
+ /* a larger value causes normalize errors in a
+ * scaled down models with camera extreme close */
+ if (d > 1.0e-70) {
+ d = sqrt(d);
+ mul_v3_v3db_db(r, a, unit_length / d);
+ }
+ else {
+ zero_v3_db(r);
+ d = 0.0;
+ }
+
+ return d;
+}
+MINLINE double normalize_v3_v3_db(double r[3], const double a[3])
+{
+ return normalize_v3_v3_length_db(r, a, 1.0);
+}
+
+MINLINE double normalize_v3_length_db(double n[3], const double unit_length)
{
double d = n[0] * n[0] + n[1] * n[1] + n[2] * n[2];
@@ -1249,9 +1212,9 @@ MINLINE double normalize_v3_length_d(double n[3], const double unit_length)
return d;
}
-MINLINE double normalize_v3_d(double n[3])
+MINLINE double normalize_v3_db(double n[3])
{
- return normalize_v3_length_d(n, 1.0);
+ return normalize_v3_length_db(n, 1.0);
}
MINLINE float normalize_v3_length(float n[3], const float unit_length)
@@ -1350,12 +1313,6 @@ MINLINE bool compare_v3v3(const float v1[3], const float v2[3], const float limi
compare_ff(v1[2], v2[2], limit));
}
-MINLINE bool compare_v3v3_db(const double v1[3], const double v2[3], const double limit)
-{
- return (compare_dd(v1[0], v2[0], limit) && compare_dd(v1[1], v2[1], limit) &&
- compare_dd(v1[2], v2[2], limit));
-}
-
MINLINE bool compare_v4v4(const float v1[4], const float v2[4], const float limit)
{
return (compare_ff(v1[0], v2[0], limit) && compare_ff(v1[1], v2[1], limit) &&
@@ -1463,11 +1420,6 @@ MINLINE float line_point_side_v2(const float l1[2], const float l2[2], const flo
return (((l1[0] - pt[0]) * (l2[1] - pt[1])) - ((l2[0] - pt[0]) * (l1[1] - pt[1])));
}
-MINLINE double line_point_side_v2_db(const double l1[2], const double l2[2], const double pt[2])
-{
- return (((l1[0] - pt[0]) * (l2[1] - pt[1])) - ((l2[0] - pt[0]) * (l1[1] - pt[1])));
-}
-
/** \} */
#endif /* __MATH_VECTOR_INLINE_C__ */
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
new file mode 100644
index 00000000000..e168aa04f65
--- /dev/null
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -0,0 +1,1655 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <fstream>
+#include <iostream>
+
+#include "BLI_allocator.hh"
+#include "BLI_array.hh"
+#include "BLI_array_ref.hh"
+#include "BLI_assert.h"
+#include "BLI_delaunay_2d.h"
+#include "BLI_double3.hh"
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
+#include "BLI_mpq2.hh"
+#include "BLI_mpq3.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "BLI_mesh_intersect.hh"
+
+namespace BLI {
+namespace MeshIntersect {
+
+/* A plane whose equation is dot(n, p) + d = 0. */
+struct planeq {
+ mpq3 n;
+ mpq_class d;
+
+ planeq() = default;
+
+ planeq(mpq3 n, mpq_class d) : n(n), d(d)
+ {
+ }
+
+ operator const mpq_class *() const
+ {
+ return &n.x;
+ }
+
+ operator mpq_class *()
+ {
+ return &n.x;
+ }
+};
+
+/* A Triangle mesh but with a different structure to hold vertices, so that
+ * we can dedup them efficiently.
+ * Also optionally keeps the planeqs of the triangles, as those are needed sometimes.
+ */
+class TMesh {
+ public:
+ TMesh() : m_has_planes(false)
+ {
+ }
+
+ /* Copies verts and triangles from tm_in, but dedups the vertices
+ * and ignores degenerate and invalid triangles.
+ * If want_planes is true, calculate and store the planes for each triangle.
+ */
+ TMesh(const TriMesh &tm_in, bool want_planes)
+ {
+ int nvert = static_cast<int>(tm_in.vert.size());
+ this->m_verts.reserve(nvert);
+ Array<int> input_v_to_tm_v = Array<int>(nvert); /* Input vert index -> our vert index. */
+ for (int v = 0; v < nvert; ++v) {
+ const mpq3 &co = tm_in.vert[v];
+ int tmv = this->add_vert(co);
+ input_v_to_tm_v[v] = tmv;
+ }
+ int ntri = static_cast<int>(tm_in.tri.size());
+ this->m_tris.reserve(ntri);
+ for (int t = 0; t < ntri; ++t) {
+ const IndexedTriangle &tri = tm_in.tri[t];
+ int v0 = tri.v0();
+ int v1 = tri.v1();
+ int v2 = tri.v2();
+ int orig = tri.orig();
+ if (orig == -1) {
+ orig = t;
+ }
+ if (v0 == v1 || v0 == v2 || v1 == v2 || v0 < 0 || v0 >= nvert || v1 < 0 || v1 >= nvert ||
+ v2 < 0 || v2 >= nvert) {
+ /* Skip degenerate triangle and ones with invalid indices. */
+ /* TODO: test for collinear v0, v1, v2 and skip if so, too. */
+ continue;
+ }
+ int tmv0 = input_v_to_tm_v[v0];
+ int tmv1 = input_v_to_tm_v[v1];
+ int tmv2 = input_v_to_tm_v[v2];
+ this->m_tris.append(IndexedTriangle(tmv0, tmv1, tmv2, orig));
+ }
+ if (want_planes) {
+ this->init_planes();
+ }
+ else {
+ m_has_planes = false;
+ }
+ }
+
+ /* Copy a single triangle from the source TMesh. */
+ TMesh(const TMesh &source_tm, int t)
+ {
+ BLI_assert(t >= 0 && t < source_tm.tot_tri());
+ this->m_verts.reserve(3);
+ const IndexedTriangle &source_it = source_tm.tri(t);
+ int tmv0 = this->add_vert(source_tm.vert(source_it.v0()));
+ int tmv1 = this->add_vert(source_tm.vert(source_it.v1()));
+ int tmv2 = this->add_vert(source_tm.vert(source_it.v2()));
+ this->m_tris.append(IndexedTriangle(tmv0, tmv1, tmv2, source_it.orig()));
+ if (source_tm.m_has_planes) {
+ this->m_planes.append(source_tm.m_planes[t]);
+ this->m_has_planes = true;
+ }
+ else {
+ this->m_has_planes = false;
+ }
+ }
+
+ /* TODO: make sure assignment, copy, init, move init, and move assignment all work as expected.
+ */
+
+ void init_planes()
+ {
+ int ntri = this->tot_tri();
+ m_planes.reserve(ntri);
+ for (int t = 0; t < ntri; ++t) {
+ IndexedTriangle &tri = m_tris[t];
+ mpq3 tr02 = m_verts[tri[0]] - m_verts[tri[2]];
+ mpq3 tr12 = m_verts[tri[1]] - m_verts[tri[2]];
+ mpq3 n = mpq3::cross(tr02, tr12);
+ mpq_class d = -mpq3::dot(n, m_verts[tri[0]]);
+ this->m_planes.append(planeq(n, d));
+ }
+ m_has_planes = true;
+ }
+
+ int tot_vert() const
+ {
+ return static_cast<int>(m_verts.size());
+ }
+
+ int tot_tri() const
+ {
+ return static_cast<int>(m_tris.size());
+ }
+
+ bool has_planes() const
+ {
+ return m_has_planes;
+ }
+
+ const IndexedTriangle &tri(int index) const
+ {
+ return m_tris[index];
+ }
+
+ const mpq3 &vert(int index) const
+ {
+ return m_verts[index];
+ }
+
+ const planeq &tri_plane(int index) const
+ {
+ BLI_assert(m_has_planes);
+ return m_planes[index];
+ }
+
+ int add_tri(int v0, int v1, int v2, int tri_orig)
+ {
+ int t = this->tot_vert();
+ this->m_tris.append(IndexedTriangle(v0, v1, v2, tri_orig));
+ return t;
+ }
+
+ int add_tri(const IndexedTriangle &itri)
+ {
+ int t = this->tot_vert();
+ this->m_tris.append(itri);
+ return t;
+ }
+
+ int add_vert(const mpq3 &co)
+ {
+ int co_index = this->m_verts.index_try(co);
+ if (co_index == -1) {
+ co_index = static_cast<int>(this->m_verts.size());
+ this->m_verts.add_new(co);
+ }
+ return co_index;
+ }
+
+ private:
+ /* Invariants:
+ * m_verts contains no duplicates.
+ * Every index t in an m_tris element satisfies
+ * 0 <= t < m_verts.size()
+ * No degenerate triangles.
+ * If m_has_planes is true then m_planes parallels
+ * the m_tris vector, and has the corresponding planes.
+ * (The init_planes() function will set that up.)
+ */
+ bool m_has_planes;
+ VectorSet<mpq3> m_verts;
+ Vector<IndexedTriangle> m_tris;
+ Vector<planeq> m_planes;
+};
+
+static std::ostream &operator<<(std::ostream &os, const TMesh &tm);
+
+/* A cluster of coplanar triangles, by index.
+ * A pair of triangles T0 and T1 is said to "nontrivially coplanar-intersect"
+ * if they are coplanar, intersect, and their intersection is not just existing
+ * elements (verts, edges) of both triangles.
+ * A coplanar cluster is said to be "nontrivial" if it has more than one triangle
+ * and every triangle in it nontrivially coplanar-intersects with at least one other
+ * triangle in the cluster.
+ */
+class CoplanarCluster {
+ public:
+ CoplanarCluster() = default;
+ explicit CoplanarCluster(int t)
+ {
+ this->add_tri(t);
+ }
+ ~CoplanarCluster() = default;
+ /* Assume that caller knows this will not be a duplicate. */
+ void add_tri(int t)
+ {
+ m_tris.append(t);
+ }
+ int tot_tri() const
+ {
+ return static_cast<int>(m_tris.size());
+ }
+ int tri(int index) const
+ {
+ return m_tris[index];
+ }
+ const int *begin() const
+ {
+ return m_tris.begin();
+ }
+ const int *end() const
+ {
+ return m_tris.end();
+ }
+
+ private:
+ Vector<int> m_tris;
+};
+
+/* Maintains indexed set of CoplanarCluster, with the added ability
+ * to efficiently find the cluster index of any given triangle
+ * (the max triangle index needs to be given in the initializer).
+ * The tri_cluster(t) function returns -1 if t is not part of any cluster.
+ */
+class CoplanarClusterInfo {
+ public:
+ CoplanarClusterInfo() = default;
+ explicit CoplanarClusterInfo(int numtri) : m_tri_cluster(Array<int>(numtri))
+ {
+ m_tri_cluster.fill(-1);
+ }
+ const int tri_cluster(int t) const
+ {
+ BLI_assert(0 <= t && t < static_cast<int>(m_tri_cluster.size()));
+ return m_tri_cluster[t];
+ }
+ int add_cluster(const CoplanarCluster cl)
+ {
+ int c_index = static_cast<int>(m_clusters.append_and_get_index(cl));
+ for (const int t : cl) {
+ BLI_assert(0 <= t && t < static_cast<int>(m_tri_cluster.size()));
+ m_tri_cluster[t] = c_index;
+ }
+ return c_index;
+ }
+ int tot_cluster() const
+ {
+ return static_cast<int>(m_clusters.size());
+ }
+ const CoplanarCluster &cluster(int index) const
+ {
+ BLI_assert(0 <= index && index < static_cast<int>(m_clusters.size()));
+ return m_clusters[index];
+ }
+
+ private:
+ Vector<CoplanarCluster> m_clusters;
+ Array<int> m_tri_cluster;
+};
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl);
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo);
+
+enum ITT_value_kind { INONE, IPOINT, ISEGMENT, ICOPLANAR };
+
+class ITT_value {
+ public:
+ enum ITT_value_kind kind;
+ mpq3 p1; /* Only relevant for IPOINT and ISEGMENT kind. */
+ mpq3 p2; /* Only relevant for ISEGMENT kind. */
+ int t_source; /* Index of the source triangle that intersected the target one. */
+
+ ITT_value() : kind(INONE), t_source(-1)
+ {
+ }
+ ITT_value(ITT_value_kind k) : kind(k), t_source(-1)
+ {
+ }
+ ITT_value(ITT_value_kind k, int tsrc) : kind(k), t_source(tsrc)
+ {
+ }
+ ITT_value(ITT_value_kind k, const mpq3 &p1) : kind(k), p1(p1), t_source(-1)
+ {
+ }
+ ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2)
+ : kind(k), p1(p1), p2(p2), t_source(-1)
+ {
+ }
+ ~ITT_value()
+ {
+ }
+};
+
+static std::ostream &operator<<(std::ostream &os, const ITT_value &itt);
+
+/*
+ * interesect_tri_tri and helper functions.
+ * This code uses the algorithm of Guigue and Devillers, as described
+ * in "Faster Triangle-Triangle Intersection Tests".
+ * Adapted from github code by Eric Haines:
+ * github.com/erich666/jgt-code/tree/master/Volume_08/Number_1/Guigue2003
+ */
+
+/* Helper function for intersect_tri_tri. Args have been fully canonicalized
+ * and we can construct the segment of intersection (triangles not coplanar).
+ */
+
+static ITT_value itt_canon2(const mpq3 &p1,
+ const mpq3 &q1,
+ const mpq3 &r1,
+ const mpq3 &p2,
+ const mpq3 &q2,
+ const mpq3 &r2,
+ const mpq3 &n1,
+ const mpq3 &n2)
+{
+ constexpr int dbg_level = 0;
+ mpq3 source;
+ mpq3 target;
+ mpq_class alpha;
+ bool ans_ok = false;
+
+ mpq3 v1 = q1 - p1;
+ mpq3 v2 = r2 - p1;
+ mpq3 n = mpq3::cross(v1, v2);
+ mpq3 v = p2 - p1;
+ if (dbg_level > 1) {
+ std::cout << "itt_canon2:\n";
+ std::cout << "p1=" << p1 << " q1=" << q1 << " r1=" << r1 << "\n";
+ std::cout << "p2=" << p2 << " q2=" << q2 << " r2=" << r2 << "\n";
+ std::cout << "v=" << v << " n=" << n << "\n";
+ }
+ if (mpq3::dot(v, n) > 0) {
+ v1 = r1 - p1;
+ n = mpq3::cross(v1, v2);
+ if (dbg_level > 1) {
+ std::cout << "case 1: v1=" << v1 << " v2=" << v2 << " n=" << n << "\n";
+ }
+ if (mpq3::dot(v, n) <= 0) {
+ v2 = q2 - p1;
+ n = mpq3::cross(v1, v2);
+ if (dbg_level > 1) {
+ std::cout << "case 1a: v2=" << v2 << " n=" << n << "\n";
+ }
+ if (mpq3::dot(v, n) > 0) {
+ v1 = p1 - p2;
+ v2 = p1 - r1;
+ alpha = mpq3::dot(v1, n2) / mpq3::dot(v2, n2);
+ v1 = v2 * alpha;
+ source = p1 - v1;
+ v1 = p2 - p1;
+ v2 = p2 - r2;
+ alpha = mpq3::dot(v1, n1) / mpq3::dot(v2, n1);
+ v1 = v2 * alpha;
+ target = p2 - v1;
+ ans_ok = true;
+ }
+ else {
+ v1 = p2 - p1;
+ v2 = p2 - q2;
+ alpha = mpq3::dot(v1, n1) / mpq3::dot(v2, n1);
+ v1 = v2 * alpha;
+ source = p2 - v1;
+ v1 = p2 - p1;
+ v2 = p2 - r2;
+ alpha = mpq3::dot(v1, n1) / mpq3::dot(v2, n1);
+ v1 = v2 * alpha;
+ target = p2 - v1;
+ ans_ok = true;
+ }
+ }
+ else {
+ if (dbg_level > 1) {
+ std::cout << "case 1b: ans=false\n";
+ }
+ ans_ok = false;
+ }
+ }
+ else {
+ v2 = q2 - p1;
+ n = mpq3::cross(v1, v2);
+ if (dbg_level > 1) {
+ std::cout << "case 2: v1=" << v1 << " v2=" << v2 << " n=" << n << "\n";
+ }
+ if (mpq3::dot(v, n) < 0) {
+ if (dbg_level > 1) {
+ std::cout << "case 2a: ans=false\n";
+ }
+ ans_ok = false;
+ }
+ else {
+ v1 = r1 - p1;
+ n = mpq3::cross(v1, v2);
+ if (dbg_level > 1) {
+ std::cout << "case 2b: v1=" << v1 << " v2=" << v2 << " n=" << n << "\n";
+ }
+ if (mpq3::dot(v, n) > 0) {
+ v1 = p1 - p2;
+ v2 = p1 - r1;
+ alpha = mpq3::dot(v1, n2) / mpq3::dot(v2, n2);
+ v1 = v2 * alpha;
+ source = p1 - v1;
+ v1 = p1 - p2;
+ v2 = p1 - q1;
+ alpha = mpq3::dot(v1, n2) / mpq3::dot(v2, n2);
+ v1 = v2 * alpha;
+ target = p1 - v1;
+ ans_ok = true;
+ }
+ else {
+ v1 = p2 - p1;
+ v2 = p2 - q2;
+ alpha = mpq3::dot(v1, n1) / mpq3::dot(v2, n1);
+ v1 = v2 * alpha;
+ source = p2 - v1;
+ v1 = p1 - p2;
+ v2 = p1 - q1;
+ alpha = mpq3::dot(v1, n2) / mpq3::dot(v2, n2);
+ v1 = v2 * alpha;
+ target = p1 - v1;
+ ans_ok = true;
+ }
+ }
+ }
+
+ if (dbg_level > 0) {
+ if (ans_ok) {
+ std::cout << "intersect: " << source << ", " << target << "\n";
+ }
+ else {
+ std::cout << "no intersect\n";
+ }
+ }
+ if (ans_ok) {
+ if (source == target) {
+ return ITT_value(IPOINT, source);
+ }
+ else {
+ return ITT_value(ISEGMENT, source, target);
+ }
+ }
+ else {
+ return ITT_value(INONE);
+ }
+}
+
+/* Helper function for intersect_tri_tri. Args have been canonicalized for triangle 1. */
+
+static ITT_value itt_canon1(const mpq3 &p1,
+ const mpq3 &q1,
+ const mpq3 &r1,
+ const mpq3 &p2,
+ const mpq3 &q2,
+ const mpq3 &r2,
+ const mpq3 &n1,
+ const mpq3 &n2,
+ int sp2,
+ int sq2,
+ int sr2)
+{
+ constexpr int dbg_level = 0;
+ if (sp2 > 0) {
+ if (sq2 > 0) {
+ return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2);
+ }
+ else if (sr2 > 0) {
+ return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2);
+ }
+ else {
+ return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2);
+ }
+ }
+ else if (sp2 < 0) {
+ if (sq2 < 0) {
+ return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2);
+ }
+ else if (sr2 < 0) {
+ return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2);
+ }
+ else {
+ return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2);
+ }
+ }
+ else {
+ if (sq2 < 0) {
+ if (sr2 >= 0) {
+ return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2);
+ }
+ else {
+ return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2);
+ }
+ }
+ else if (sq2 > 0) {
+ if (sr2 > 0) {
+ return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2);
+ }
+ else {
+ return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2);
+ }
+ }
+ else {
+ if (sr2 > 0) {
+ return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2);
+ }
+ else if (sr2 < 0) {
+ return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2);
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "triangles are coplanar\n";
+ }
+ return ITT_value(ICOPLANAR);
+ }
+ }
+ }
+ return ITT_value(INONE);
+}
+
+static ITT_value intersect_tri_tri(const TMesh &tm, int t1, int t2)
+{
+ constexpr int dbg_level = 0;
+ const IndexedTriangle &tri1 = tm.tri(t1);
+ const IndexedTriangle &tri2 = tm.tri(t2);
+ const mpq3 &p1 = tm.vert(tri1.v0());
+ const mpq3 &q1 = tm.vert(tri1.v1());
+ const mpq3 &r1 = tm.vert(tri1.v2());
+ const mpq3 &p2 = tm.vert(tri2.v0());
+ const mpq3 &q2 = tm.vert(tri2.v1());
+ const mpq3 &r2 = tm.vert(tri2.v2());
+
+ if (dbg_level > 0) {
+ std::cout << "\nINTERSECT_TRI_TRI t1=" << t1 << ", t2=" << t2 << "\n";
+ std::cout << " p1 = " << p1 << "\n";
+ std::cout << " q1 = " << q1 << "\n";
+ std::cout << " r1 = " << r1 << "\n";
+ std::cout << " p2 = " << p2 << "\n";
+ std::cout << " q2 = " << q2 << "\n";
+ std::cout << " r2 = " << r2 << "\n";
+ }
+
+ /* Get signs of t1's vertices' distances to plane of t2. */
+ /* If don't have normal, use mpq3 n2 = cross_v3v3(sub_v3v3(p2, r2), sub_v3v3(q2, r2)); */
+ const mpq3 &n2 = tm.tri_plane(t2).n;
+ int sp1 = sgn(mpq3::dot(p1 - r2, n2));
+ int sq1 = sgn(mpq3::dot(q1 - r2, n2));
+ int sr1 = sgn(mpq3::dot(r1 - r2, n2));
+
+ if (dbg_level > 1) {
+ std::cout << " sp1=" << sp1 << " sq1=" << sq1 << " sr1=" << sr1 << "\n";
+ }
+
+ if ((sp1 * sq1 > 0) && (sp1 * sr1 > 0)) {
+ if (dbg_level > 0) {
+ std::cout << "no intersection, all t1's verts above or below t2\n";
+ }
+ return ITT_value(INONE);
+ }
+
+ /* Repeat for signs of t2's vertices with respect to plane of t1. */
+ /* If don't have normal, use mpq3 n1 = cross_v3v3(sub_v3v3(q1, p1), sub_v3v3(r1, p1)); */
+ const mpq3 &n1 = tm.tri_plane(t1).n;
+ int sp2 = sgn(mpq3::dot(p2 - r1, n1));
+ int sq2 = sgn(mpq3::dot(q2 - r1, n1));
+ int sr2 = sgn(mpq3::dot(r2 - r1, n1));
+
+ if (dbg_level > 1) {
+ std::cout << " sp2=" << sp2 << " sq2=" << sq2 << " sr2=" << sr2 << "\n";
+ }
+
+ if ((sp2 * sq2 > 0) && (sp2 * sr2 > 0)) {
+ if (dbg_level > 0) {
+ std::cout << "no intersection, all t2's verts above or below t1\n";
+ }
+ return ITT_value(INONE);
+ }
+
+ /* Do rest of the work with vertices in a canonical order, where p1 is on
+ * postive side of plane and q1, r1 are not; similarly for p2.
+ */
+ ITT_value ans;
+ if (sp1 > 0) {
+ if (sq1 > 0) {
+ ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else if (sr1 > 0) {
+ ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ }
+ else if (sp1 < 0) {
+ if (sq1 < 0) {
+ ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ else if (sr1 < 0) {
+ ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ else {
+ ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ }
+ else {
+ if (sq1 < 0) {
+ if (sr1 >= 0) {
+ ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ }
+ else if (sq1 > 0) {
+ if (sr1 > 0) {
+ ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ }
+ else {
+ if (sr1 > 0) {
+ ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
+ }
+ else if (sr1 < 0) {
+ ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
+ }
+ else {
+ if (dbg_level > 0) {
+ std::cout << "triangles are coplanar\n";
+ }
+ ans = ITT_value(ICOPLANAR);
+ }
+ }
+ }
+ if (ans.kind == ICOPLANAR) {
+ ans.t_source = t2;
+ }
+ return ans;
+}
+
+struct CDT_data {
+ planeq t_plane;
+ Vector<mpq2> vert;
+ Vector<std::pair<int, int>> edge;
+ Vector<Vector<int>> face;
+ Vector<int> input_face; /* Parallels face, gives id from input TMesh of input face. */
+ Vector<bool> is_reversed; /* Parallels face, says if input face orientation is opposite. */
+ CDT_result<mpq_class> cdt_out; /* Result of running CDT on input with (vert, edge, face). */
+ int proj_axis;
+};
+
+/* Project a 3d vert to a 2d one by eliding proj_axis. This does not create
+ * degeneracies as long as the projection axis is one where the corresponding
+ * component of the originating plane normal is non-zero.
+ */
+static mpq2 project_3d_to_2d(const mpq3 &p3d, int proj_axis)
+{
+ mpq2 p2d;
+ switch (proj_axis) {
+ case (0): {
+ p2d[0] = p3d[1];
+ p2d[1] = p3d[2];
+ } break;
+ case (1): {
+ p2d[0] = p3d[0];
+ p2d[1] = p3d[2];
+ } break;
+ case (2): {
+ p2d[0] = p3d[0];
+ p2d[1] = p3d[1];
+ } break;
+ default:
+ BLI_assert(false);
+ }
+ return p2d;
+}
+
+/* We could dedup verts here, but CDT routine will do that anyway. */
+static int prepare_need_vert(CDT_data &cd, const mpq3 &p3d)
+{
+ mpq2 p2d = project_3d_to_2d(p3d, cd.proj_axis);
+ int v = cd.vert.append_and_get_index(p2d);
+ return v;
+}
+
+/* To unproject a 2d vert that was projected along cd.proj_axis, we copy the coordinates
+ * from the two axes not involved in the projection, and use the plane equation of the
+ * originating 3d plane, cd.t_plane, to derive the coordinate of the projected axis.
+ * The plane equation says a point p is on the plane if dot(p, plane.n()) + plane.d() == 0.
+ * Assume that the projection axis is such that plane.n()[proj_axis] != 0.
+ */
+static mpq3 unproject_cdt_vert(const CDT_data &cd, const mpq2 &p2d)
+{
+ mpq3 p3d;
+ BLI_assert(cd.t_plane.n[cd.proj_axis] != 0);
+ switch (cd.proj_axis) {
+ case (0): {
+ mpq_class num = cd.t_plane.n[1] * p2d[0] + cd.t_plane.n[2] * p2d[1] + cd.t_plane.d;
+ num = -num;
+ p3d[0] = num / cd.t_plane.n[0];
+ p3d[1] = p2d[0];
+ p3d[2] = p2d[1];
+ } break;
+ case (1): {
+ p3d[0] = p2d[0];
+ mpq_class num = cd.t_plane.n[0] * p2d[0] + cd.t_plane.n[2] * p2d[1] + cd.t_plane.d;
+ num = -num;
+ p3d[1] = num / cd.t_plane.n[1];
+ p3d[2] = p2d[1];
+ } break;
+ case (2): {
+ p3d[0] = p2d[0];
+ p3d[1] = p2d[1];
+ mpq_class num = cd.t_plane.n[0] * p2d[0] + cd.t_plane.n[1] * p2d[1] + cd.t_plane.d;
+ num = -num;
+ p3d[2] = num / cd.t_plane.n[2];
+ } break;
+ default:
+ BLI_assert(false);
+ }
+ return p3d;
+}
+
+static void prepare_need_edge(CDT_data &cd, const mpq3 &p1, const mpq3 &p2)
+{
+ int v1 = prepare_need_vert(cd, p1);
+ int v2 = prepare_need_vert(cd, p2);
+ cd.edge.append(std::pair<int, int>(v1, v2));
+}
+
+static void prepare_need_tri(CDT_data &cd, const TMesh &tm, int t)
+{
+ IndexedTriangle tri = tm.tri(t);
+ int v0 = prepare_need_vert(cd, tm.vert(tri.v0()));
+ int v1 = prepare_need_vert(cd, tm.vert(tri.v1()));
+ int v2 = prepare_need_vert(cd, tm.vert(tri.v2()));
+ bool rev;
+ /* How to get CCW orientation of projected tri? Note that when look down y axis
+ * as opposed to x or z, the orientation of the other two axes is not right-and-up.
+ */
+ if (cd.t_plane[cd.proj_axis] >= 0) {
+ rev = cd.proj_axis == 1;
+ }
+ else {
+ rev = cd.proj_axis != 1;
+ }
+ /* If t's plane is opposite to cd.t_plane, need to reverse again. */
+ if (sgn(tm.tri_plane(t).n[cd.proj_axis]) != sgn(cd.t_plane[cd.proj_axis])) {
+ rev = !rev;
+ }
+ int cd_t = cd.face.append_and_get_index(Vector<int>());
+ cd.face[cd_t].append(v0);
+ if (rev) {
+ cd.face[cd_t].append(v2);
+ cd.face[cd_t].append(v1);
+ }
+ else {
+ cd.face[cd_t].append(v1);
+ cd.face[cd_t].append(v2);
+ }
+ cd.input_face.append(t);
+ cd.is_reversed.append(rev);
+}
+
+static CDT_data prepare_cdt_input(const TMesh &tm, int t, const Vector<ITT_value> itts)
+{
+ CDT_data ans;
+ BLI_assert(tm.has_planes());
+ ans.t_plane = tm.tri_plane(t);
+ const planeq &pl = ans.t_plane;
+ ans.proj_axis = mpq3::dominant_axis(pl.n);
+ prepare_need_tri(ans, tm, t);
+ for (const ITT_value &itt : itts) {
+ switch (itt.kind) {
+ case INONE:
+ break;
+ case IPOINT: {
+ prepare_need_vert(ans, itt.p1);
+ } break;
+ case ISEGMENT: {
+ prepare_need_edge(ans, itt.p1, itt.p2);
+ } break;
+ case ICOPLANAR: {
+ prepare_need_tri(ans, tm, itt.t_source);
+ } break;
+ }
+ }
+ return ans;
+}
+
+static CDT_data prepare_cdt_input_for_cluster(const TMesh &tm,
+ const CoplanarClusterInfo &clinfo,
+ int c,
+ const Vector<ITT_value> itts)
+{
+ CDT_data ans;
+ BLI_assert(c >= 0 && c < clinfo.tot_cluster());
+ const CoplanarCluster &cl = clinfo.cluster(c);
+ BLI_assert(cl.tot_tri() > 0);
+ int t0 = cl.tri(0);
+ BLI_assert(tm.has_planes());
+ ans.t_plane = tm.tri_plane(t0);
+ const planeq &pl = ans.t_plane;
+ ans.proj_axis = mpq3::dominant_axis(pl.n);
+ for (const int t : cl) {
+ prepare_need_tri(ans, tm, t);
+ }
+ for (const ITT_value &itt : itts) {
+ switch (itt.kind) {
+ case IPOINT: {
+ prepare_need_vert(ans, itt.p1);
+ } break;
+ case ISEGMENT: {
+ prepare_need_edge(ans, itt.p1, itt.p2);
+ } break;
+ default:
+ break;
+ }
+ }
+ return ans;
+}
+
+/* Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face). */
+static void do_cdt(CDT_data &cd)
+{
+ constexpr int dbg_level = 0;
+ /* TODO: find a way to avoid the following copies. Maybe add a CDT_input variant (or just
+ * change its signature) to use Vectors instead of Arrays for the three inputs.
+ */
+ CDT_input<mpq_class> cdt_in;
+ cdt_in.vert = Array<mpq2>(cd.vert);
+ cdt_in.edge = Array<std::pair<int, int>>(cd.edge);
+ cdt_in.face = Array<Vector<int>>(cd.face);
+ if (dbg_level > 0) {
+ std::cout << "CDT input\nVerts:\n";
+ for (uint i = 0; i < cdt_in.vert.size(); ++i) {
+ std::cout << "v" << i << ": " << cdt_in.vert[i] << "\n";
+ }
+ std::cout << "Edges:\n";
+ for (uint i = 0; i < cdt_in.edge.size(); ++i) {
+ std::cout << "e" << i << ": (" << cdt_in.edge[i].first << ", " << cdt_in.edge[i].second
+ << ")\n";
+ }
+ std::cout << "Tris\n";
+ for (uint f = 0; f < cdt_in.face.size(); ++f) {
+ std::cout << "f" << f << ": ";
+ for (uint j = 0; j < cdt_in.face[f].size(); ++j) {
+ std::cout << cdt_in.face[f][j] << " ";
+ }
+ std::cout << "\n";
+ }
+ }
+ cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */
+ cd.cdt_out = BLI::delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ if (dbg_level > 0) {
+ std::cout << "\nCDT result\nVerts:\n";
+ for (uint i = 0; i < cd.cdt_out.vert.size(); ++i) {
+ std::cout << "v" << i << ": " << cd.cdt_out.vert[i] << "\n";
+ }
+ std::cout << "Tris\n";
+ for (uint f = 0; f < cd.cdt_out.face.size(); ++f) {
+ std::cout << "f" << f << ": ";
+ for (uint j = 0; j < cd.cdt_out.face[f].size(); ++j) {
+ std::cout << cd.cdt_out.face[f][j] << " ";
+ }
+ std::cout << "orig: ";
+ for (uint j = 0; j < cd.cdt_out.face_orig[f].size(); ++j) {
+ std::cout << cd.cdt_out.face_orig[f][j] << " ";
+ }
+ std::cout << "\n";
+ }
+ }
+}
+
+/* Using the result of CDT in cd.cdt_out, extract a TMesh representing the subdivision
+ * of input triangle t, which should be an element of cd.input_face.
+ */
+
+static TMesh extract_subdivided_tri(const CDT_data &cd, const TMesh &in_tm, int t)
+{
+ TMesh ans;
+
+ /* We want all triangles in cdt_out that had t (as indexed in the CDT_input) as an orig. */
+ /* Which output verts do we need in our answer? */
+ const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
+ Array<bool> needvert(cdt_out.vert.size()); /* Initialized to all false. */
+ Array<bool> needtri(cdt_out.face.size()); /* Initialized to all false. */
+ int t_in_cdt = -1;
+ for (int i = 0; i < static_cast<int>(cd.input_face.size()); ++i) {
+ if (cd.input_face[i] == t) {
+ t_in_cdt = i;
+ }
+ }
+ if (t_in_cdt == -1) {
+ std::cout << "Could not find " << t << " in cdt input tris\n";
+ BLI_assert(false);
+ return ans;
+ }
+ int num_needed_v = 0;
+ for (uint f = 0; f < cdt_out.face.size(); ++f) {
+ if (cdt_out.face_orig[f].contains(t_in_cdt)) {
+ needtri[f] = true;
+ for (int v : cdt_out.face[f]) {
+ if (!needvert[v]) {
+ needvert[v] = true;
+ ++num_needed_v;
+ }
+ }
+ }
+ }
+ Array<int> cdt_v_to_out_v(cdt_out.vert.size());
+
+ for (uint cdt_v = 0; cdt_v < cdt_out.vert.size(); ++cdt_v) {
+ if (needvert[cdt_v]) {
+ mpq3 v3co = unproject_cdt_vert(cd, cdt_out.vert[cdt_v]);
+ cdt_v_to_out_v[cdt_v] = ans.add_vert(v3co);
+ }
+ }
+ int orig = in_tm.tri(t).orig();
+ for (uint f = 0; f < cdt_out.face.size(); ++f) {
+ if (needtri[f]) {
+ BLI_assert(cdt_out.face[f].size() == 3);
+ int v0 = cdt_out.face[f][0];
+ int v1 = cdt_out.face[f][1];
+ int v2 = cdt_out.face[f][2];
+ BLI_assert(needvert[v0] && needvert[v1] && needvert[v2]);
+ int out_v0 = cdt_v_to_out_v[v0];
+ int out_v1 = cdt_v_to_out_v[v1];
+ int out_v2 = cdt_v_to_out_v[v2];
+ if (cd.is_reversed[t_in_cdt]) {
+ ans.add_tri(out_v0, out_v2, out_v1, orig);
+ }
+ else {
+ ans.add_tri(out_v0, out_v1, out_v2, orig);
+ }
+ }
+ }
+ return ans;
+}
+
+static TMesh calc_tri_subdivided(const TMesh &in_tm, int t)
+{
+ constexpr int dbg_level = 0;
+ TMesh ans;
+
+ if (dbg_level > 0) {
+ std::cout << "\ncalc_tri_subdivided for tri " << t << "\n\n";
+ }
+ int ntri = in_tm.tot_tri();
+ Vector<ITT_value> itts;
+ for (int t_other = 0; t_other < ntri; ++t_other) {
+ if (t_other == t) {
+ continue;
+ }
+ /* Intersect t with t_other. */
+ ITT_value itt = intersect_tri_tri(in_tm, t, t_other);
+ if (dbg_level > 1) {
+ std::cout << "intersect " << t << " with " << t_other << " result: " << itt << "\n";
+ }
+ if (itt.kind != INONE) {
+ itts.append(itt);
+ }
+ }
+ if (itts.size() == 0) {
+ /* No intersections: answer is just the original triangle t. */
+ ans = TMesh(in_tm, t);
+ }
+ else {
+ /* Use CDT to subdivide the triangle. */
+ CDT_data cd_data = prepare_cdt_input(in_tm, t, itts);
+ do_cdt(cd_data);
+ ans = extract_subdivided_tri(cd_data, in_tm, t);
+ }
+ if (dbg_level > 0) {
+ std::cout << "\ncalc_tri_subdivided " << t << " result:\n" << ans;
+ }
+ return ans;
+}
+
+static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo, int c, const TMesh &tm)
+{
+ constexpr int dbg_level = 0;
+ BLI_assert(0 <= c && c < clinfo.tot_cluster());
+ const CoplanarCluster &cl = clinfo.cluster(c);
+ /* Make a CDT input with triangles from C and intersects from other triangles in tm. */
+ if (dbg_level > 0) {
+ std::cout << "calc_cluster_subdivided for cluster " << c << " = " << cl << "\n";
+ }
+ /* Get vector itts of all intersections of a triangle of cl with any triangle of tm not
+ * in cl and not coplanar with it (for that latter, if there were an intersection,
+ * it should already be in cluster cl).
+ */
+ int ntri = tm.tot_tri();
+ Vector<ITT_value> itts;
+ for (int t_other = 0; t_other < ntri; ++t_other) {
+ if (clinfo.tri_cluster(t_other) != c) {
+ if (dbg_level > 0) {
+ std::cout << "intersect cluster " << c << " with tri " << t_other << "\n";
+ }
+ for (const int t : cl) {
+ ITT_value itt = intersect_tri_tri(tm, t, t_other);
+ if (dbg_level > 0) {
+ std::cout << "intersect tri " << t << " with tri " << t_other << " = " << itt << "\n";
+ }
+ if (itt.kind != INONE && itt.kind != ICOPLANAR) {
+ itts.append(itt);
+ }
+ }
+ }
+ }
+ /* Use CDT to subdivide the cluster triangles and the points and segs in itts. */
+ CDT_data cd_data = prepare_cdt_input_for_cluster(tm, clinfo, c, itts);
+ do_cdt(cd_data);
+ return cd_data;
+}
+
+static TMesh union_tri_subdivides(const BLI::Array<TMesh> &tri_subdivided)
+{
+ TMesh ans;
+ for (const TMesh &tmsub : tri_subdivided) {
+ Array<int> vtrans(tmsub.tot_vert());
+ for (int v = 0; v < tmsub.tot_vert(); ++v) {
+ vtrans[v] = ans.add_vert(tmsub.vert(v));
+ }
+ for (int t = 0; t < tmsub.tot_tri(); ++t) {
+ const IndexedTriangle &tri = tmsub.tri(t);
+ ans.add_tri(vtrans[tri.v0()], vtrans[tri.v1()], vtrans[tri.v2()], tri.orig());
+ }
+ }
+ return ans;
+}
+
+/* Need a canonical form of a plane so that can use as a key in a map and
+ * all coplanar triangles will have the same key.
+ * Make the first nonzero component of the normal be 1.
+ */
+static planeq canon_plane(const planeq &pl)
+{
+ if (pl.n[0] != 0) {
+ return planeq(mpq3(1, pl.n[1] / pl.n[0], pl.n[2] / pl.n[0]), pl.d / pl.n[0]);
+ }
+ else if (pl.n[1] != 0) {
+ return planeq(mpq3(0, 1, pl.n[2] / pl.n[1]), pl.d / pl.n[1]);
+ }
+ else {
+ return planeq(mpq3(0, 0, 1), pl.d / pl.n[2]);
+ }
+}
+
+/* Is a point in the interior of a 2d triangle or on one of its
+ * edges but not either endpoint of the edge?
+ * orient[pi][i] is the orientation test of the point pi against
+ * the side of the triangle starting at index i.
+ * Assume the triangele is non-degenerate and CCW-oriented.
+ * Then answer is true if p is left of or on all three of triangle a's edges,
+ * and strictly left of at least on of them.
+ */
+static bool non_trivially_2d_point_in_tri(const int orients[3][3], int pi)
+{
+ int p_left_01 = orients[pi][0];
+ int p_left_12 = orients[pi][1];
+ int p_left_20 = orients[pi][2];
+ return (p_left_01 >= 0 && p_left_12 >= 0 && p_left_20 >= 0 &&
+ (p_left_01 + p_left_12 + p_left_20) >= 2);
+}
+
+/* Given orients as defined in non_trivially_2d_intersect, do the triangles
+ * overlap in a "hex" pattern? That is, the overlap region is a hexagon, which
+ * one gets by having, each point of one triangle being strictly rightof one
+ * edge of the other and strictly left of the other two edges; and vice versa.
+ */
+static bool non_trivially_2d_hex_overlap(int orients[2][3][3])
+{
+ for (int ab = 0; ab < 2; ++ab) {
+ for (int i = 0; i < 3; ++i) {
+ bool ok = orients[ab][i][0] + orients[ab][i][1] + orients[ab][i][2] == 1 &&
+ orients[ab][i][0] != 0 && orients[ab][i][1] != 0 && orients[i][2] != 0;
+ if (!ok) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/* Given orients as defined in non_trivially_2d_intersect, do the triangles
+ * have one shared edge in a "folded-over" configuration?
+ * As well as a shared edge, the third vertex of one triangle needs to be
+ * rightof one and leftof the other two edges of the other triangle.
+ */
+
+static bool non_trivially_2d_shared_edge_overlap(int orients[2][3][3],
+ const mpq2 *a[3],
+ const mpq2 *b[3])
+{
+ for (int i = 0; i < 3; ++i) {
+ int in = (i + 1) % 3;
+ int inn = (i + 2) % 3;
+ for (int j = 0; j < 3; ++j) {
+ int jn = (j + 1) % 3;
+ int jnn = (j + 2) % 3;
+ if (*a[i] == *b[j] && *a[in] == *b[jn]) {
+ /* Edge from a[i] is shared with edge from b[j]. */
+ /* See if a[inn] is rightof or on one of the other edges of b.
+ * If it is on, then it has to be rightof or leftof the shared edge,
+ * depending on which edge it is.
+ */
+ if (orients[0][inn][jn] < 0 || orients[0][inn][jnn] < 0) {
+ return true;
+ }
+ if (orients[0][inn][jn] == 0 && orients[0][inn][j] == 1) {
+ return true;
+ }
+ if (orients[0][inn][jnn] == 0 && orients[0][inn][j] == -1) {
+ return true;
+ }
+ /* Similarly for b[jnn]. */
+ if (orients[1][jnn][in] < 0 || orients[1][jnn][inn] < 0) {
+ return true;
+ }
+ if (orients[1][jnn][in] == 0 && orients[1][jnn][i] == 1) {
+ return true;
+ }
+ if (orients[1][jnn][inn] == 0 && orients[1][jnn][i] == -1) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/* Are the triangles the same, perhaps with some permutation of vertices? */
+static bool same_triangles(const mpq2 *a[3], const mpq2 *b[3])
+{
+ for (int i = 0; i < 3; ++i) {
+ if (a[0] == b[i] && a[1] == b[(i + 1) % 3] && a[2] == b[(i + 2) % 3]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Do 2d triangles (a[0], a[1], a[2]) and (b[0], b[1], b2[2]) intersect at more than just shared
+ * vertices or a shared edge? This is true if any point of one tri is non-trivially inside the
+ * other. NO: that isn't quite sufficient: there is also the case where the verts are all mutually
+ * outside the other's triangle, but there is a hexagonal overlap region where they overlap.
+ */
+
+static bool non_trivially_2d_intersect(const mpq2 *a[3], const mpq2 *b[3])
+{
+ /* TODO: Could experiment with trying bounding box tests before these.
+ * TODO: Find a less expensive way than 18 orient tests to do this.
+ */
+ /* orients[0][ai][bi] is orient of point a[ai] compared to seg starting at b[bi].
+ * orients[1][bi][ai] is orient of point b[bi] compared to seg starting at a[ai].
+ */
+ int orients[2][3][3];
+ for (int ab = 0; ab < 2; ++ab) {
+ for (int ai = 0; ai < 3; ++ai) {
+ for (int bi = 0; bi < 3; ++bi) {
+ if (ab == 0) {
+ orients[0][ai][bi] = mpq2::orient2d(*b[bi], *b[(bi + 1) % 3], *a[ai]);
+ }
+ else {
+ orients[1][bi][ai] = mpq2::orient2d(*a[ai], *a[(ai + 1) % 3], *b[bi]);
+ }
+ }
+ }
+ }
+ return non_trivially_2d_point_in_tri(orients[0], 0) ||
+ non_trivially_2d_point_in_tri(orients[0], 1) ||
+ non_trivially_2d_point_in_tri(orients[0], 2) ||
+ non_trivially_2d_point_in_tri(orients[1], 0) ||
+ non_trivially_2d_point_in_tri(orients[1], 1) ||
+ non_trivially_2d_point_in_tri(orients[1], 2) || non_trivially_2d_hex_overlap(orients) ||
+ non_trivially_2d_shared_edge_overlap(orients, a, b) || same_triangles(a, b);
+ return true;
+}
+
+/* Does triangle t in tm non-trivially non-coplanar intersect any triangle
+ * in CoplanarCluster cl? Assume t is known to be in the same plane as all
+ * the triangles in cl, and that proj_axis is a good axis to project down
+ * to solve this problem in 2d.
+ */
+
+static bool non_trivially_coplanar_intersects(const TMesh &tm,
+ int t,
+ const CoplanarCluster &cl,
+ int proj_axis)
+{
+ const IndexedTriangle &tri = tm.tri(t);
+ mpq2 v0 = project_3d_to_2d(tm.vert(tri.v0()), proj_axis);
+ mpq2 v1 = project_3d_to_2d(tm.vert(tri.v1()), proj_axis);
+ mpq2 v2 = project_3d_to_2d(tm.vert(tri.v2()), proj_axis);
+ if (mpq2::orient2d(v0, v1, v2) != 1) {
+ mpq2 tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+ }
+ for (const int cl_t : cl) {
+ const IndexedTriangle &cl_tri = tm.tri(cl_t);
+ mpq2 ctv0 = project_3d_to_2d(tm.vert(cl_tri.v0()), proj_axis);
+ mpq2 ctv1 = project_3d_to_2d(tm.vert(cl_tri.v1()), proj_axis);
+ mpq2 ctv2 = project_3d_to_2d(tm.vert(cl_tri.v2()), proj_axis);
+ if (mpq2::orient2d(ctv0, ctv1, ctv2) != 1) {
+ mpq2 tmp = ctv1;
+ ctv1 = ctv2;
+ ctv2 = tmp;
+ }
+ const mpq2 *v[] = {&v0, &v1, &v2};
+ const mpq2 *ctv[] = {&ctv0, &ctv1, &ctv2};
+ if (non_trivially_2d_intersect(v, ctv)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static CoplanarClusterInfo find_clusters(const TMesh &tmesh)
+{
+ constexpr int dbg_level = 0;
+ if (dbg_level > 0) {
+ std::cout << "FIND_CLUSTERS\n";
+ }
+ CoplanarClusterInfo ans(tmesh.tot_tri());
+ /* There can be more than one CoplanarCluster per plane. Accumulate them in
+ * a Vector. We will have to merge some elements of the Vector as we discover
+ * triangles that form intersection bridges between two or more clusters.
+ */
+ Map<planeq, Vector<CoplanarCluster>> plane_cls;
+ for (int t = 0; t < tmesh.tot_tri(); ++t) {
+ const planeq &tplane = tmesh.tri_plane(t);
+ planeq canon_tplane = canon_plane(tplane);
+ if (plane_cls.contains(canon_tplane)) {
+ Vector<CoplanarCluster> &curcls = plane_cls.lookup(canon_tplane);
+ int proj_axis = mpq3::dominant_axis(canon_tplane.n);
+ /* Paritition curcls into those that intersect t non-trivially, and those that don't. */
+ Vector<CoplanarCluster *> int_cls;
+ Vector<CoplanarCluster *> no_int_cls;
+ for (CoplanarCluster &cl : curcls) {
+ if (non_trivially_coplanar_intersects(tmesh, t, cl, proj_axis)) {
+ int_cls.append(&cl);
+ }
+ else {
+ no_int_cls.append(&cl);
+ }
+ }
+ if (int_cls.size() == 0) {
+ /* t doesn't intersect any existing cluster in its plane, so make one just for it. */
+ curcls.append(CoplanarCluster(t));
+ }
+ else if (int_cls.size() == 1) {
+ /* t intersects exactly one existing cluster, so can add t to that cluster. */
+ int_cls[0]->add_tri(t);
+ }
+ else {
+ /* t intersections 2 or more existing clusters: need to merge them and replace all the
+ * originals with the merged one in curcls.
+ */
+ CoplanarCluster mergecl;
+ mergecl.add_tri(t);
+ for (CoplanarCluster *cl : int_cls) {
+ for (int t : *cl) {
+ mergecl.add_tri(t);
+ }
+ }
+ Vector<CoplanarCluster> newvec;
+ newvec.append(mergecl);
+ for (CoplanarCluster *cl_no_int : no_int_cls) {
+ newvec.append(*cl_no_int);
+ }
+ plane_cls.add_override(canon_tplane, newvec);
+ }
+ }
+ else {
+ plane_cls.add_new(canon_tplane, Vector<CoplanarCluster>{CoplanarCluster(t)});
+ }
+ }
+ /* Does this give deterministic order for cluster ids? I think so, since
+ * hash for planes is on their values, not their addresses.
+ */
+ for (auto item : plane_cls.items()) {
+ for (const CoplanarCluster &cl : item.value) {
+ if (cl.tot_tri() > 1) {
+ ans.add_cluster(cl);
+ }
+ }
+ }
+
+ return ans;
+}
+
+static TriMesh tmesh_to_trimesh(const TMesh &tm)
+{
+ TriMesh ans;
+ ans.vert = Array<mpq3>(tm.tot_vert());
+ ans.tri = Array<IndexedTriangle>(tm.tot_tri());
+ for (uint v = 0; v < tm.tot_vert(); ++v) {
+ ans.vert[v] = tm.vert(v);
+ }
+ for (uint t = 0; t < tm.tot_tri(); ++t) {
+ ans.tri[t] = tm.tri(t);
+ }
+ return ans;
+}
+
+/* This is the main routine for calculating the self_intersection of a TriMesh. */
+static TriMesh tpl_trimesh_self_intersect(const TriMesh &tm_in)
+{
+ constexpr int dbg_level = 1;
+ if (dbg_level > 0) {
+ std::cout << "\nTRIMESH_SELF_INTERSECT\n";
+ }
+ TMesh tmesh(tm_in, true);
+ int ntri = tmesh.tot_tri();
+ CoplanarClusterInfo clinfo = find_clusters(tmesh);
+ if (dbg_level > 1) {
+ std::cout << clinfo;
+ }
+ BLI::Array<CDT_data> cluster_subdivided(clinfo.tot_cluster());
+ for (int c = 0; c < clinfo.tot_cluster(); ++c) {
+ cluster_subdivided[c] = calc_cluster_subdivided(clinfo, c, tmesh);
+ }
+ BLI::Array<TMesh> tri_subdivided(ntri);
+ for (int t = 0; t < ntri; ++t) {
+ int c = clinfo.tri_cluster(t);
+ if (c == -1) {
+ tri_subdivided[t] = calc_tri_subdivided(tmesh, t);
+ }
+ else {
+ tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], tmesh, t);
+ }
+ }
+ TMesh combined = union_tri_subdivides(tri_subdivided);
+ return tmesh_to_trimesh(combined);
+}
+
+TriMesh trimesh_self_intersect(const TriMesh &tm_in)
+{
+ return tpl_trimesh_self_intersect(tm_in);
+}
+
+static std::ostream &operator<<(std::ostream &os, const TMesh &tm)
+{
+ os << "TMesh\nVerts:\n";
+ for (int v = 0; v < tm.tot_vert(); ++v) {
+ os << " " << v << ": " << tm.vert(v) << "\n";
+ }
+ os << "Tris:\n";
+ for (int t = 0; t < tm.tot_tri(); ++t) {
+ os << " " << t << ": " << tm.tri(t) << "\n";
+ if (tm.has_planes()) {
+ os << " plane: [" << tm.tri_plane(t).n << ";" << tm.tri_plane(t).d << "]\n";
+ }
+ }
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const IndexedTriangle &tri)
+{
+ os << "tri(" << tri.v0() << "," << tri.v1() << "," << tri.v2() << ")";
+ if (tri.orig() != -1) {
+ os << "(o" << tri.orig() << ")";
+ }
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const TriMesh &tm)
+{
+ os << "TriMesh input\nVerts:\n";
+ for (uint v = 0; v < tm.vert.size(); ++v) {
+ os << " " << v << ": " << tm.vert[v] << "\n";
+ }
+ os << "Tris:\n";
+ for (uint t = 0; t < tm.tri.size(); ++t) {
+ os << " " << t << ": " << tm.tri[t] << "\n";
+ }
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl)
+{
+ os << "cl(";
+ bool first = true;
+ for (const int t : cl) {
+ if (first) {
+ first = false;
+ }
+ else {
+ os << ",";
+ }
+ os << t;
+ }
+ os << ")";
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo)
+{
+ os << "Coplanar Cluster Info:\n";
+ for (int c = 0; c < clinfo.tot_cluster(); ++c) {
+ os << c << ": " << clinfo.cluster(c) << "\n";
+ }
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const ITT_value &itt)
+{
+ switch (itt.kind) {
+ case INONE:
+ os << "none";
+ break;
+ case IPOINT:
+ os << "point " << itt.p1;
+ break;
+ case ISEGMENT:
+ os << "segment " << itt.p1 << " " << itt.p2;
+ break;
+ case ICOPLANAR:
+ os << "coplanar t" << itt.t_source;
+ break;
+ }
+ return os;
+}
+
+/* Some contrasting colors to use for distinguishing triangles. */
+static const char *drawcolor[] = {
+ "0.67 0.14 0.14", /* red */
+ "0.16 0.29 0.84", /* blue */
+ "0.11 0.41 0.08", /* green */
+ "0.50 0.29 0.10", /* brown */
+ "0.50 0.15 0.75", /* purple */
+ "0.62 0.62 0.62", /* light grey */
+ "0.50 0.77 0.49", /* light green */
+ "0.61 0.68 1.00", /* light blue */
+ "0.16 0.82 0.82", /* cyan */
+ "1.00 0.57 0.20", /* orange */
+ "1.00 0.93 0.20", /* yellow */
+ "0.91 0.87 0.73", /* tan */
+ "1.00 0.80 0.95", /* pink */
+ "0.34 0.34 0.34" /* dark grey */
+};
+static constexpr int numcolors = sizeof(drawcolor) / sizeof(drawcolor[0]);
+
+/* See x3dom.org for an explanation of this way of embedding 3d objects in a web page. */
+
+static const char *htmlfileheader = R"(<head>
+<title>Mesh Intersection Tests</title>
+<script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script>
+<link rel='stylesheet' type='text/cs href='http://www.x3dom.org/download/x3dom.css'></link>
+</head>
+)";
+
+void write_html_trimesh(const Array<mpq3> &vert,
+ const Array<IndexedTriangle> &tri,
+ const std::string &fname,
+ const std::string &label)
+{
+ static bool draw_append = false;
+ constexpr const char *drawfiledir = "/tmp/";
+ constexpr int draw_width = 1400;
+ constexpr int draw_height = 1000;
+ constexpr bool draw_vert_labels = true;
+
+ const std::string fpath = std::string(drawfiledir) + fname;
+
+ std::ofstream f;
+ if (draw_append) {
+ f.open(fpath, std::ios_base::app);
+ }
+ else {
+ f.open(fpath);
+ }
+ if (!f) {
+ std::cout << "Could not open file " << fpath << "\n";
+ return;
+ }
+ if (!draw_append) {
+ f << htmlfileheader;
+ }
+
+ f << "<div>" << label << "</div>\n<div>\n"
+ << "<x3d width='" << draw_width << "px' "
+ << "height='" << draw_height << "px'>\n"
+ << "<scene>\n";
+
+ BLI::Array<bool> vused(vert.size());
+ int i = 0;
+ for (const IndexedTriangle &t : tri) {
+ double3 dv0, dv1, dv2;
+ for (int axis = 0; axis < 3; ++axis) {
+ dv0[axis] = vert[t.v0()][axis].get_d();
+ dv1[axis] = vert[t.v1()][axis].get_d();
+ dv2[axis] = vert[t.v2()][axis].get_d();
+ }
+ f << "<shape>\n";
+ f << " <appearance>\n";
+ f << " <twosidedmaterial diffuseColor='" << drawcolor[i % numcolors]
+ << "' separatebackcolor='false'/>\n";
+ f << " </appearance>\n";
+ f << " <triangleset>\n";
+ f << " <coordinate point='" << dv0[0] << " " << dv0[1] << " " << dv0[2] << " " << dv1[0]
+ << " " << dv1[1] << " " << dv1[2] << " " << dv2[0] << " " << dv2[1] << " " << dv2[2]
+ << "'/>\n";
+ f << " </triangleset>\n";
+ f << "</shape>\n";
+ vused[t.v0()] = true;
+ vused[t.v1()] = true;
+ vused[t.v2()] = true;
+ ++i;
+ }
+ if (draw_vert_labels) {
+ for (uint i = 0; i < vert.size(); ++i) {
+ if (!vused[i]) {
+ continue;
+ }
+ double3 dv(vert[i][0].get_d(), vert[i][1].get_d(), vert[i][2].get_d());
+ f << "<transform translation='" << dv[0] << " " << dv[1] << " " << dv[2] << "'>\n";
+ f << "<shape>\n <appearance>\n"
+ << " <twosidedmaterial diffuseColor='0 0 0'/>\n"
+ << " </appearance>\n"
+ << " <text string='" << i << "'><fontstyle size='0.25'/></text>\n"
+ << "</shape>\n</transform>\n";
+ }
+ }
+ f << "</scene>\n</x3d>\n</div>\n";
+ draw_append = true;
+}
+
+void write_obj_trimesh(const Array<mpq3> &vert,
+ const Array<IndexedTriangle> &tri,
+ const std::string &objname)
+{
+ constexpr const char *objdir = "/tmp/";
+ if (tri.size() == 0) {
+ return;
+ }
+
+ std::string fname = std::string(objdir) + objname + std::string(".obj");
+ std::string matfname = std::string(objdir) + std::string("dumpobj.mtl");
+ std::ofstream f;
+ f.open(fname);
+ if (!f) {
+ std::cout << "Could not open file " << fname << "\n";
+ return;
+ }
+
+ f << "mtllib dumpobj.mtl\n";
+
+ for (const mpq3 &vco : vert) {
+ double3 dv(vco[0].get_d(), vco[1].get_d(), vco[2].get_d());
+ f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n";
+ }
+ int i = 0;
+ for (const IndexedTriangle &t : tri) {
+ int matindex = i % numcolors;
+ f << "usemtl mat" + std::to_string(matindex) + "\n";
+ /* OBJ files use 1-indexing for vertices. */
+ f << "f " << t.v0() + 1 << " " << t.v1() + 1 << " " << t.v2() + 1 << "\n";
+ ++i;
+ }
+ f.close();
+
+ /* Could check if it already exists, but why bother. */
+ std::ofstream mf;
+ mf.open(matfname);
+ if (!mf) {
+ std::cout << "Could not open file " << matfname << "\n";
+ return;
+ }
+ for (int c = 0; c < numcolors; ++c) {
+ mf << "newmtl mat" + std::to_string(c) + "\n";
+ mf << "Kd " << drawcolor[c] << "\n";
+ }
+}
+
+}; // namespace MeshIntersect
+
+template<> struct DefaultHash<mpq_class> {
+ uint32_t operator()(const mpq_class &value) const
+ {
+ return DefaultHash<float>{}(static_cast<float>(value.get_d()));
+ }
+};
+
+template<> struct DefaultHash<mpq3> {
+ uint32_t operator()(const mpq3 &value) const
+ {
+ uint32_t hashx = DefaultHash<mpq_class>{}(value.x);
+ uint32_t hashy = DefaultHash<mpq_class>{}(value.y);
+ uint32_t hashz = DefaultHash<mpq_class>{}(value.z);
+ return hashx ^ (hashy * 33) ^ (hashz * 33 * 37);
+ }
+};
+
+template<> struct DefaultHash<MeshIntersect::planeq> {
+ uint32_t operator()(const MeshIntersect::planeq &value) const
+ {
+ uint32_t hashx = DefaultHash<mpq_class>{}(value.n.x);
+ uint32_t hashy = DefaultHash<mpq_class>{}(value.n.y);
+ uint32_t hashz = DefaultHash<mpq_class>{}(value.n.z);
+ uint32_t hashd = DefaultHash<mpq_class>{}(value.d);
+ return hashx ^ (hashy * 33) ^ (hashz * 33 * 37) ^ (hashd * 33 * 37 * 39);
+ }
+};
+
+} // namespace BLI
diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h
index d0e91d033fb..35f3f161e38 100644
--- a/source/blender/bmesh/bmesh_tools.h
+++ b/source/blender/bmesh/bmesh_tools.h
@@ -31,6 +31,7 @@ extern "C" {
#include "tools/bmesh_beautify.h"
#include "tools/bmesh_bevel.h"
#include "tools/bmesh_bisect_plane.h"
+#include "tools/bmesh_boolean.h"
#include "tools/bmesh_decimate.h"
#include "tools/bmesh_edgenet.h"
#include "tools/bmesh_edgesplit.h"
diff --git a/source/blender/bmesh/tools/bmesh_boolean.c b/source/blender/bmesh/tools/bmesh_boolean.c
index 74d390fcabe..af51f8235fe 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.c
+++ b/source/blender/bmesh/tools/bmesh_boolean.c
@@ -17,4633 +17,104 @@
/** \file
* \ingroup bmesh
*
- * Cut meshes along intersections and boolean operations on the intersections.
- *
- * Supported:
- * - Concave faces.
- * - Non-planar faces.
- * - Coplanar intersections
- * - Custom-data (UV's etc).
- *
+ * Main functions for boolean on a BMesh (used by the tool and modifier)
*/
#include "MEM_guardedalloc.h"
-#include "BLI_alloca.h"
-#include "BLI_bitmap.h"
-#include "BLI_delaunay_2d.h"
-#include "BLI_edgehash.h"
-#include "BLI_kdopbvh.h"
-#include "BLI_kdtree.h"
-#include "BLI_linklist.h"
+#include "BLI_boolean.h"
#include "BLI_math.h"
-#include "BLI_memarena.h"
-#include "BLI_utildefines.h"
#include "bmesh.h"
-#include "intern/bmesh_private.h"
-
-#include "bmesh_boolean.h" /* own include */
-
-#include "BLI_strict_flags.h"
-
-// #define BOOLDEBUG
-// #define PERFDEBUG
-
-/* A set of integers. TODO: faster structure. */
-typedef struct IntSet {
- LinkNode *list;
-} IntSet;
-
-typedef struct IntSetIterator {
- LinkNode *cur;
-} IntSetIterator;
-
-/* A set of integers, where each member gets an index
- * that can be used to access the member.
- * TODO: faster structure for lookup.
- */
-typedef struct IndexedIntSet {
- LinkNodePair listhead; /* links are ints */
- int size;
-} IndexedIntSet;
-
-/* A map from int -> int.
- * TODO: faster structure for lookup.
- */
-typedef struct IntIntMap {
- LinkNodePair listhead; /* Links are pointers to IntPair, allocated from arena. */
-} IntIntMap;
-
-typedef struct IntPair {
- int first;
- int second;
-} IntPair;
-
-typedef struct IntIntMapIterator {
- const IntIntMap *map;
- LinkNode *curlink;
- IntPair *keyvalue;
-} IntIntMapIterator;
-
-/* A Mesh Interface.
- * This would be an abstract interface in C++,
- * but similate that effect in C.
- * Idea is to write the rest of the code so that
- * it will work with either Mesh or BMesh as the
- * concrete representation.
- * Thus, editmesh and modifier can use the same
- * code but without need to convert to BMesh (or Mesh).
- *
- * Some data structures to make for efficient search
- * are also included in this structure.
- *
- * Exactly one of bm and me should be non-null.
- */
-typedef struct IMesh {
- BMesh *bm;
- struct Mesh *me;
- KDTree_3d *co_tree;
-} IMesh;
-
-/* Structs to hold verts, edges, and faces to be added to MeshAdd. */
-typedef struct NewVert {
- float co[3];
- int example; /* If not -1, example vert in IMesh. */
-} NewVert;
-
-typedef struct NewEdge {
- int v1;
- int v2;
- int example; /* If not -1, example edge in IMesh. */
-} NewEdge;
-
-typedef struct NewFace {
- IntPair *vert_edge_pairs; /* Array of len (vert, edge) pairs. */
- int len;
- int example; /* If not -1, example face in IMesh. */
- IntSet *other_examples; /* rest of faces in IMesh that are originals for this face */
-} NewFace;
-
-/* MeshAdd holds an incremental addition to an IMesh.
- * New verts, edges, and faces are given indices starting
- * beyond those of the underlying IMesh, and that new
- * geometry is stored here. For edges and faces, the
- * indices used can be either from the IMesh or from the
- * new geometry stored here.
- * Sometimes the new geometric elements are based on
- * an example element in the underlying IMesh (the example
- * will be used to copy attributes).
- * So we store examples here too.
- */
-typedef struct MeshAdd {
- NewVert **verts;
- NewEdge **edges;
- NewFace **faces;
- uint vert_reserved;
- uint edge_reserved;
- uint face_reserved;
- EdgeHash *edge_hash;
- int totvert;
- int totedge;
- int totface;
- int vindex_start; /* Add this to position in verts to get index of new vert. */
- int eindex_start; /* Add this to position in edges to get index of new edge. */
- int findex_start; /* Add this to position in faces to get index of new face. */
- IMesh *im; /* Underlying IMesh. */
-} MeshAdd;
-
-/* MeshDelete holds an incremental deletion to an IMesh.
- * It records the indices of the edges and faces that need
- * to be deleted. (As of now, we don't have to delete verts
- * except those that recorded separately as merges.)
- */
-typedef struct MeshDelete {
- BLI_bitmap *vert_bmap;
- BLI_bitmap *edge_bmap;
- BLI_bitmap *face_bmap;
- int totvert;
- int totedge;
- int totface;
-} MeshDelete;
-
-/* MeshChange holds all of the information needed to transform
- * an IMesh into the desired result: vertex merges, adds, deletes,
- * and which edges are to be tagged to mark intersection edges.
- */
-typedef struct MeshChange {
- MeshAdd add;
- MeshDelete delete;
- IntIntMap vert_merge_map;
- IntSet intersection_edges;
- IntSet face_flip;
- bool use_face_kill_loose;
-} MeshChange;
-
-/* A MeshPart is a subset of the geometry of an IndexMesh,
- * with some possible additional geometry.
- * The indices refer to vertex, edges, and faces in the IndexMesh
- * that this part is based on,
- * or, if the indices are larger than the total in the IndexMesh,
- * then it is in extra geometry incrementally added.
- * Unlike for IndexMesh, the edges implied by faces need not be explicitly
- * represented here.
- * Commonly a MeshPart will contain geometry that shares a plane,
- * and when that is so, the plane member says which plane,
- * TODO: faster structure for looking up verts, edges, faces.
- */
-typedef struct MeshPart {
- double plane[4]; /* First 3 are normal, 4th is signed distance to plane. */
- double bbmin[3]; /* Bounding box min, with eps padding. */
- double bbmax[3]; /* Bounding box max, with eps padding. */
- LinkNode *verts; /* Links are ints (vert indices). */
- LinkNode *edges; /* Links are ints (edge indices). */
- LinkNode *faces; /* Links are ints (face indices). */
-} MeshPart;
-
-/* A MeshPartSet set is a set of MeshParts.
- * For any two distinct elements of the set, either they are not
- * coplanar or if they are, they are known not to intersect.
- */
-typedef struct MeshPartSet {
- double bbmin[3];
- double bbmax[3];
- MeshPart **meshparts;
- int tot_part;
- int reserve; /* number of allocated slots in meshparts; may exceed tot_part */
- const char *label; /* for debugging */
-} MeshPartSet;
-
-/* An IMeshPlus is an IMesh plus a MeshAdd.
- * If the element indices are in range for the IMesh, then functions
- * access those, else they access the MeshAdd.
- */
-typedef struct IMeshPlus {
- IMesh *im;
- MeshAdd *meshadd;
-} IMeshPlus;
-
-/* Result of intersecting two MeshParts.
- * This only need identify the thngs that probably intersect,
- * as the actual intersections will be done later, when
- * parts are self-intersected. Dedup will handle any problems.
- * It is not necessary to include verts that are part of included
- * edges, nor edges that are part of included faces.
- */
-typedef struct PartPartIntersect {
- LinkNodePair verts; /* Links are vert indices. */
- LinkNodePair edges; /* Links are edge indices. */
- LinkNodePair faces; /* Links are face indices. */
- int a_index;
- int b_index;
-} PartPartIntersect;
-
-/* Bit to set in face_side per face flag inside BoolState. */
-#define SIDE_A 1
-#define SIDE_B 2
-#define BOTH_SIDES_OPP_NORMALS 4
-
-typedef struct BoolState {
- MemArena *mem_arena;
- IMesh im;
- double eps;
- uchar *face_side;
-} BoolState;
-
-/* Decoration to shut up gnu 'unused function' warning. */
-#ifdef __GNUC__
-# define ATTU __attribute__((unused))
-#else
-# define ATTU
-#endif
-
-#ifdef BOOLDEBUG
-/* For Debugging. */
-# define CO3(v) (v)->co[0], (v)->co[1], (v)->co[2]
-# define F2(v) (v)[0], (v)[1]
-# define F3(v) (v)[0], (v)[1], (v)[2]
-# define F4(v) (v)[0], (v)[1], (v)[2], (v)[3]
-# define BMI(e) BM_elem_index_get(e)
-
-ATTU static void dump_part(const MeshPart *part, const char *label);
-ATTU static void dump_partset(const MeshPartSet *pset);
-ATTU static void dump_partpartintersect(const PartPartIntersect *ppi, const char *label);
-ATTU static void dump_meshadd(const MeshAdd *ma, const char *label);
-ATTU static void dump_meshdelete(const MeshDelete *meshdelete, const char *label);
-ATTU static void dump_intintmap(const IntIntMap *map, const char *label, const char *prefix);
-ATTU static void dump_intset(const IntSet *set, const char *label, const char *prefix);
-ATTU static void dump_meshchange(const MeshChange *change, const char *label);
-ATTU static void dump_cdt_input(const CDT_input *cdt, const char *label);
-ATTU static void dump_cdt_result(const CDT_result *cdt, const char *label, const char *prefix);
-ATTU static void dump_bm(struct BMesh *bm, const char *msg);
-ATTU bool analyze_bmesh_for_boolean(BMesh *bm, bool verbose, int side, uchar *face_side);
-#endif
-
-#ifdef PERFDEBUG
-ATTU static void perfdata_init(void);
-ATTU static void incperfcount(int countnum);
-ATTU static void doperfmax(int maxnum, int val);
-ATTU static void dump_perfdata(void);
-#endif
-
-/* Forward declarations of some static functions. */
-static int min_int_in_array(int *array, int len);
-static LinkNode *linklist_shallow_copy_arena(LinkNode *list, struct MemArena *arena);
-static void calc_part_bb_eps(BoolState *bs, MeshPart *part, double eps);
-static bool find_in_intintmap(const IntIntMap *map, int key, int *r_val);
-static int meshadd_totvert(const MeshAdd *meshadd);
-static int meshadd_totedge(const MeshAdd *meshadd);
-static int meshadd_totface(const MeshAdd *meshadd);
-static NewVert *meshadd_get_newvert(const MeshAdd *meshadd, int v);
-static NewEdge *meshadd_get_newedge(const MeshAdd *meshadd, int e);
-static NewFace *meshadd_get_newface(const MeshAdd *meshadd, int f);
-static int meshadd_facelen(const MeshAdd *meshadd, int f);
-static void meshadd_get_face_no(const MeshAdd *meshadd, int f, double *r_no);
-static int meshadd_face_vert(const MeshAdd *meshadd, int f, int index);
-static void meshadd_get_vert_co(const MeshAdd *meshadd, int v, float *r_coords);
-static void meshadd_get_edge_verts(const MeshAdd *meshadd, int e, int *r_v1, int *r_v2);
-static bool meshdelete_find_vert(MeshDelete *meshdelete, int v);
-static bool meshdelete_find_edge(MeshDelete *meshdelete, int e);
-static bool meshdelete_find_face(MeshDelete *meshdelete, int f);
-static int find_edge_by_verts_in_meshadd(const MeshAdd *meshadd, int v1, int v2);
-
-/** Intset functions */
-
-static void init_intset(IntSet *inset)
-{
- inset->list = NULL;
-}
-
-static bool find_in_intset(const IntSet *set, int value)
-{
- LinkNode *ln;
-
- for (ln = set->list; ln; ln = ln->next) {
- if (POINTER_AS_INT(ln->link) == value) {
- return true;
- }
- }
- return false;
-}
-
-static void add_to_intset(BoolState *bs, IntSet *set, int value)
-{
- if (!find_in_intset(set, value)) {
- BLI_linklist_prepend_arena(&set->list, POINTER_FROM_INT(value), bs->mem_arena);
- }
-}
-
-/** IntSetIterator functions. */
-
-static void intset_iter_init(IntSetIterator *iter, const IntSet *set)
-{
- iter->cur = set->list;
-}
-
-static bool intset_iter_done(IntSetIterator *iter)
-{
- return iter->cur == NULL;
-}
-
-static void intset_iter_step(IntSetIterator *iter)
-{
- iter->cur = iter->cur->next;
-}
-
-static int intset_iter_value(IntSetIterator *iter)
-{
- return POINTER_AS_INT(iter->cur->link);
-}
-
-/** IndexedIntSet functions. */
-
-static void init_indexedintset(IndexedIntSet *intset)
-{
- intset->listhead.list = NULL;
- intset->listhead.last_node = NULL;
- intset->size = 0;
-}
-
-static int add_int_to_indexedintset(BoolState *bs, IndexedIntSet *intset, int value)
-{
- int index;
-
- index = BLI_linklist_index(intset->listhead.list, POINTER_FROM_INT(value));
- if (index == -1) {
- BLI_linklist_append_arena(&intset->listhead, POINTER_FROM_INT(value), bs->mem_arena);
- index = intset->size;
- intset->size++;
- }
- return index;
-}
-
-static bool find_in_indexedintset(const IndexedIntSet *intset, int value)
-{
- LinkNode *ln;
-
- for (ln = intset->listhead.list; ln; ln = ln->next) {
- if (POINTER_AS_INT(ln->link) == value) {
- return true;
- }
- }
- return false;
-}
-
-static int indexedintset_get_value_by_index(const IndexedIntSet *intset, int index)
-{
- LinkNode *ln;
- int i;
-
- if (index < 0 || index >= intset->size) {
- return -1;
- }
- ln = intset->listhead.list;
- for (i = 0; i < index; i++) {
- BLI_assert(ln != NULL);
- ln = ln->next;
- }
- BLI_assert(ln != NULL);
- return POINTER_AS_INT(ln->link);
-}
-
-static int indexedintset_get_index_for_value(const IndexedIntSet *intset, int value)
-{
- return BLI_linklist_index(intset->listhead.list, POINTER_FROM_INT(value));
-}
-
-/** IntIntMap functions. */
-
-static void init_intintmap(IntIntMap *intintmap)
-{
- intintmap->listhead.list = NULL;
- intintmap->listhead.last_node = NULL;
-}
-
-ATTU static int intintmap_size(const IntIntMap *intintmap)
-{
- return BLI_linklist_count(intintmap->listhead.list);
-}
-
-static void add_to_intintmap(BoolState *bs, IntIntMap *map, int key, int val)
-{
- IntPair *keyvalpair;
-
- keyvalpair = BLI_memarena_alloc(bs->mem_arena, sizeof(*keyvalpair));
- keyvalpair->first = key;
- keyvalpair->second = val;
- BLI_linklist_append_arena(&map->listhead, keyvalpair, bs->mem_arena);
-}
-
-static bool find_in_intintmap(const IntIntMap *map, int key, int *r_val)
-{
- LinkNode *ln;
-
- for (ln = map->listhead.list; ln; ln = ln->next) {
- IntPair *pair = (IntPair *)ln->link;
- if (pair->first == key) {
- *r_val = pair->second;
- return true;
- }
- }
- return false;
-}
-
-/* Note: this is a shallow copy: afterwards, dst and src will
- * share underlying list
- */
-ATTU static void copy_intintmap_intintmap(IntIntMap *dst, IntIntMap *src)
-{
- dst->listhead.list = src->listhead.list;
- dst->listhead.last_node = src->listhead.last_node;
-}
-
-ATTU static void set_intintmap_entry(BoolState *bs, IntIntMap *map, int key, int value)
-{
- LinkNode *ln;
-
- for (ln = map->listhead.list; ln; ln = ln->next) {
- IntPair *pair = (IntPair *)ln->link;
- if (pair->first == key) {
- pair->second = value;
- return;
- }
- }
- add_to_intintmap(bs, map, key, value);
-}
-
-/* Abstract the IntIntMap iteration to allow for changes to
- * implementation later.
- * We allow the value of a map to be changed during
- * iteration, but not the key.
- */
-
-ATTU static void intintmap_iter_init(IntIntMapIterator *iter, const IntIntMap *map)
-{
- iter->map = map;
- iter->curlink = map->listhead.list;
- if (iter->curlink) {
- iter->keyvalue = (IntPair *)iter->curlink->link;
- }
- else {
- iter->keyvalue = NULL;
- }
-}
-
-static inline bool intintmap_iter_done(IntIntMapIterator *iter)
-{
- return iter->curlink == NULL;
-}
-
-ATTU static void intintmap_iter_step(IntIntMapIterator *iter)
-{
- iter->curlink = iter->curlink->next;
- if (iter->curlink) {
- iter->keyvalue = (IntPair *)iter->curlink->link;
- }
- else {
- iter->keyvalue = NULL;
- }
-}
-
-ATTU static inline int intintmap_iter_key(IntIntMapIterator *iter)
-{
- return iter->keyvalue->first;
-}
-
-ATTU static inline int *intintmap_iter_valuep(IntIntMapIterator *iter)
-{
- return &iter->keyvalue->second;
-}
-
-ATTU static inline int intintmap_iter_value(IntIntMapIterator *iter)
-{
- return iter->keyvalue->second;
-}
-
-/** Miscellaneous utility functions. */
-
-static int min_int_in_array(int *array, int len)
-{
- int min = INT_MAX;
- int i;
-
- for (i = 0; i < len; i++) {
- min = min_ii(min, array[i]);
- }
- return min;
-}
-
-/* Shallow copy. Result will share link pointers with original. */
-static LinkNode *linklist_shallow_copy_arena(LinkNode *list, struct MemArena *arena)
-{
- LinkNodePair listhead = {NULL, NULL};
- LinkNode *ln;
-
- for (ln = list; ln; ln = ln->next) {
- BLI_linklist_append_arena(&listhead, ln->link, arena);
- }
-
- return listhead.list;
-}
-
-/* Get to last node of a liskned list. Also return list size in r_count. */
-ATTU static LinkNode *linklist_last(LinkNode *ln, int *r_count)
-{
- int i;
-
- if (ln) {
- i = 1;
- for (; ln->next; ln = ln->next) {
- i++;
- }
- *r_count = i;
- return ln;
- }
- *r_count = 0;
- return NULL;
-}
-
-/** Functions to move to Blenlib's math_geom when stable. */
-
-#if 0
-/* This general tri-tri intersect routine may be useful as an optimization later, but is unused for now. */
-
-/*
- * Return -1, 0, or 1 as (px,py) is right of, collinear (within epsilon) with, or left of line
- * (l1,l2)
- */
-static int line_point_side_v2_array_db(
- double l1x, double l1y, double l2x, double l2y, double px, double py, double epsilon)
-{
- double det, dx, dy, lnorm;
-
- dx = l2x - l1x;
- dy = l2y - l1y;
- det = dx * (py - l1y) - dy * (px - l1x);
- lnorm = sqrt(dx * dx + dy * dy);
- if (fabs(det) < epsilon * lnorm)
- return 0;
- else if (det < 0)
- return -1;
- else
- return 1;
-}
-
-/**
- * Intersect two triangles, allowing for coplanar intersections too.
- *
- * The result can be any of: a single point, a segment, or an n-gon (n <= 6),
- * so can indicate all of these with a return array of coords max size 6 and a returned
- * length: if length is 1, then intersection is a point; if 2, then a segment;
- * if 3 or more, a (closed) ngon.
- * Use double arithmetic, and use consistent ordering of vertices and
- * segments in tests to make some precision problems less likely.
- * Algorithm: Tomas Möller's "A Fast Triangle-Triangle Intersection Test"
- *
- * \param r_pts, r_npts: Optional arguments to retrieve the intersection points between the 2
- * triangles. \return true when the triangles intersect.
- *
- */
-static bool isect_tri_tri_epsilon_v3_db_ex(const double t_a0[3],
- const double t_a1[3],
- const double t_a2[3],
- const double t_b0[3],
- const double t_b1[3],
- const double t_b2[3],
- double r_pts[6][3],
- int *r_npts,
- const double epsilon)
-{
- const double *tri_pair[2][3] = {{t_a0, t_a1, t_a2}, {t_b0, t_b1, t_b2}};
- double co[2][3][3];
- double plane_a[4], plane_b[4];
- double plane_co[3], plane_no[3];
- int verti;
-
- BLI_assert((r_pts != NULL) == (r_npts != NULL));
-
- /* This is remnant from when input args were floats. TODO: remove this copying. */
- for (verti = 0; verti < 3; verti++) {
- copy_v3_v3_db(co[0][verti], tri_pair[0][verti]);
- copy_v3_v3_db(co[1][verti], tri_pair[1][verti]);
- }
- /* normalizing is needed for small triangles T46007 */
- normal_tri_v3_db(plane_a, UNPACK3(co[0]));
- normal_tri_v3_db(plane_b, UNPACK3(co[1]));
-
- plane_a[3] = -dot_v3v3_db(plane_a, co[0][0]);
- plane_b[3] = -dot_v3v3_db(plane_b, co[1][0]);
-
- if (isect_plane_plane_v3_db(plane_a, plane_b, plane_co, plane_no) &&
- (normalize_v3_d(plane_no) > epsilon)) {
- struct {
- double min, max;
- } range[2] = {{DBL_MAX, -DBL_MAX}, {DBL_MAX, -DBL_MAX}};
- int t;
- double co_proj[3];
-
- closest_to_plane3_normalized_v3_db(co_proj, plane_no, plane_co);
-
- /* For both triangles, find the overlap with the line defined by the ray [co_proj, plane_no].
- * When the ranges overlap we know the triangles do too. */
- for (t = 0; t < 2; t++) {
- int j, j_prev;
- double tri_proj[3][3];
-
- closest_to_plane3_normalized_v3_db(tri_proj[0], plane_no, co[t][0]);
- closest_to_plane3_normalized_v3_db(tri_proj[1], plane_no, co[t][1]);
- closest_to_plane3_normalized_v3_db(tri_proj[2], plane_no, co[t][2]);
-
- for (j = 0, j_prev = 2; j < 3; j_prev = j++) {
- /* note that its important to have a very small nonzero epsilon here
- * otherwise this fails for very small faces.
- * However if its too small, large adjacent faces will count as intersecting */
- const double edge_fac = line_point_factor_v3_ex_db(
- co_proj, tri_proj[j_prev], tri_proj[j], 1e-10, -1.0);
- if (UNLIKELY(edge_fac == -1.0)) {
- /* pass */
- }
- else if (edge_fac > -epsilon && edge_fac < 1.0 + epsilon) {
- double ix_tri[3];
- double span_fac;
-
- interp_v3_v3v3_db(ix_tri, co[t][j_prev], co[t][j], edge_fac);
- /* the actual distance, since 'plane_no' is normalized */
- span_fac = dot_v3v3_db(plane_no, ix_tri);
-
- range[t].min = min_dd(range[t].min, span_fac);
- range[t].max = max_dd(range[t].max, span_fac);
- }
- }
-
- if (range[t].min == DBL_MAX) {
- return false;
- }
- }
-
- if (((range[0].min > range[1].max + epsilon) || (range[0].max < range[1].min - epsilon)) ==
- 0) {
- if (r_pts && r_npts) {
- double pt1[3], pt2[3];
- project_plane_normalized_v3_v3v3_db(plane_co, plane_co, plane_no);
- madd_v3_v3v3db_db(pt1, plane_co, plane_no, max_dd(range[0].min, range[1].min));
- madd_v3_v3v3db_db(pt2, plane_co, plane_no, min_dd(range[0].max, range[1].max));
- copy_v3_v3_db(r_pts[0], pt1);
- copy_v3_v3_db(r_pts[1], pt2);
- if (len_v3v3_db(pt1, pt2) <= epsilon)
- *r_npts = 1;
- else
- *r_npts = 2;
- }
-
- return true;
- }
- }
- else if (fabs(plane_a[3] - plane_b[3]) <= epsilon) {
- double pts[9][3];
- int ia, ip, ia_n, ia_nn, ip_prev, npts, same_side[6], ss, ss_prev, j;
-
- for (ip = 0; ip < 3; ip++)
- copy_v3_v3_db(pts[ip], co[1][ip]);
- npts = 3;
-
- /* a convex polygon vs convex polygon clipping algorithm */
- for (ia = 0; ia < 3; ia++) {
- ia_n = (ia + 1) % 3;
- ia_nn = (ia_n + 1) % 3;
-
- /* set same_side[i] = 0 if A[ia], A[ia_n], pts[ip] are collinear.
- * else same_side[i] =1 if A[ia_nn] and pts[ip] are on same side of A[ia], A[ia_n].
- * else same_side[i] = -1
- */
- for (ip = 0; ip < npts; ip++) {
- double t;
- const double *l1 = co[0][ia];
- const double *l2 = co[0][ia_n];
- const double *p1 = co[0][ia_nn];
- const double *p2 = pts[ip];
-
- /* rather than projecting onto plane, do same-side tests projecting onto 3 ortho planes */
- t = line_point_side_v2_array_db(l1[0], l1[1], l2[0], l2[1], p1[0], p1[1], epsilon);
- if (t != 0.0) {
- same_side[ip] = t * line_point_side_v2_array_db(
- l1[0], l1[1], l2[0], l2[1], p2[0], p2[1], epsilon);
- }
- else {
- t = line_point_side_v2_array_db(l1[1], l1[2], l2[1], l2[2], p1[1], p1[2], epsilon);
- if (t != 0.0) {
- same_side[ip] = t * line_point_side_v2_array_db(
- l1[1], l1[2], l2[1], l2[2], p2[1], p2[2], epsilon);
- }
- else {
- t = line_point_side_v2_array_db(l1[0], l1[2], l2[0], l2[2], p1[0], p1[2], epsilon);
- same_side[ip] = t * line_point_side_v2_array_db(
- l1[0], l1[2], l2[0], l2[2], p2[0], p2[2], epsilon);
- }
- }
- }
- ip_prev = npts - 1;
- for (ip = 0; ip < (npts > 2 ? npts : npts - 1); ip++) {
- ss = same_side[ip];
- ss_prev = same_side[ip_prev];
- if ((ss_prev == 1 && ss == -1) || (ss_prev == -1 && ss == 1)) {
- /* do coplanar line-line intersect, specialized verison of isect_line_line_epsilon_v3_db
- */
- double *v1 = co[0][ia], *v2 = co[0][ia_n], *v3 = pts[ip_prev], *v4 = pts[ip];
- double a[3], b[3], c[3], ab[3], cb[3], isect[3], div;
-
- sub_v3_v3v3_db(c, v3, v1);
- sub_v3_v3v3_db(a, v2, v1);
- sub_v3_v3v3_db(b, v4, v3);
-
- cross_v3_v3v3_db(ab, a, b);
- div = dot_v3v3_db(ab, ab);
- if (div == 0.0) {
- /* shouldn't happen! */
- printf("div == 0, shouldn't happen\n");
- continue;
- }
- cross_v3_v3v3_db(cb, c, b);
- mul_v3db_db(a, dot_v3v3_db(cb, ab) / div);
- add_v3_v3v3_db(isect, v1, a);
-
- /* insert isect at current location */
- BLI_assert(npts < 9);
- for (j = npts; j > ip; j--) {
- copy_v3_v3_db(pts[j], pts[j - 1]);
- same_side[j] = same_side[j - 1];
- }
- copy_v3_v3_db(pts[ip], isect);
- same_side[ip] = 0;
- npts++;
- }
- ip_prev = ip;
- }
- /* cut out some pts that are no longer in intersection set */
- for (ip = 0; ip < npts;) {
- if (same_side[ip] == -1) {
- /* remove point at ip */
- for (j = ip; j < npts - 1; j++) {
- copy_v3_v3_db(pts[j], pts[j + 1]);
- same_side[j] = same_side[j + 1];
- }
- npts--;
- }
- else {
- ip++;
- }
- }
- }
-
- *r_npts = npts;
- /* This copy is remant of old signature that returned floats. TODO: remove this copy. */
- for (ip = 0; ip < npts; ip++) {
- copy_v3_v3_db(r_pts[ip], pts[ip]);
- }
- return npts > 0;
- }
- if (r_npts)
- *r_npts = 0;
- return false;
-}
-#endif
-
-/* TODO: move these into math_geom.c. */
-/* What is interpolation factor that gives closest point on line to a given point? */
-static double line_interp_factor_v3_db(const double point[3],
- const double line_co1[3],
- const double line_co2[3])
-{
- double h[3], seg_dir[3], seg_len_squared;
-
- sub_v3_v3v3_db(h, point, line_co1);
- seg_len_squared = len_squared_v3v3_db(line_co2, line_co1);
- if (UNLIKELY(seg_len_squared) == 0.0) {
- return 0.0;
- }
- sub_v3_v3v3_db(seg_dir, line_co2, line_co1);
- return dot_v3v3_db(h, seg_dir) / seg_len_squared;
-}
-
-/* Does the segment intersect the plane, within epsilon?
- * Return value is 0 if no intersect, 1 if one intersect, 2 if the whole segment is in the plane.
- * In case 1, r_isect gets the intersection point, possibly snapped to an endpoint (if outside
- * segment but within epsilon) and r_lambda gets the factor from seg_co1 to seg_co2 of
- * the intersection point.
- * \note Similar logic to isect_ray_plane_v3.
- */
-static int isect_seg_plane_normalized_epsilon_v3_db(const double seg_co1[3],
- const double seg_co2[3],
- const double plane[4],
- double epsilon,
- double r_isect[3],
- double *r_lambda)
-{
- double h[3], plane_co[3], seg_dir[3], side1, side2;
- double dot, lambda;
-
- BLI_ASSERT_UNIT_V3_DB(plane);
- sub_v3_v3v3_db(seg_dir, seg_co2, seg_co1);
- dot = dot_v3v3_db(plane, seg_dir);
- if (dot == 0.0) {
- /* plane_point_side_v3_db gets signed distance of point to plane. */
- side1 = plane_point_side_v3_db(plane, seg_co1);
- side2 = plane_point_side_v3_db(plane, seg_co2);
- if (fabs(side1) <= epsilon || fabs(side2) <= epsilon) {
- return 2;
- }
- else {
- return 0;
- }
- }
- mul_v3db_v3dbdb(plane_co, plane, -plane[3]);
- sub_v3_v3v3_db(h, seg_co1, plane_co);
- lambda = -dot_v3v3_db(plane, h) / dot;
- if (lambda < -epsilon || lambda > 1.0 + epsilon) {
- return 0;
- }
- if (lambda < 0.0) {
- lambda = 0.0;
- copy_v3_v3_db(r_isect, seg_co1);
- }
- else if (lambda > 1.0) {
- lambda = 1.0;
- copy_v3_v3_db(r_isect, seg_co2);
- }
- else {
- madd_v3_v3v3db_db(r_isect, seg_co1, seg_dir, lambda);
- }
- *r_lambda = lambda;
- return 1;
-}
-
-/** IMesh functions. */
-
-static KDTree_3d *make_im_co_tree(IMesh *im);
-
-static void init_imesh_from_bmesh(IMesh *im, BMesh *bm)
-{
- im->bm = bm;
- im->me = NULL;
- BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
- BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE | BM_LOOP);
- im->co_tree = make_im_co_tree(im);
-}
-
-static void imesh_free_aux_data(IMesh *im)
-{
- BLI_kdtree_3d_free(im->co_tree);
-}
-
-static int imesh_totvert(const IMesh *im)
-{
- if (im->bm) {
- return im->bm->totvert;
- }
- else {
- return 0; /* TODO */
- }
-}
-
-static int imesh_totedge(const IMesh *im)
-{
- if (im->bm) {
- return im->bm->totedge;
- }
- else {
- return 0; /* TODO */
- }
-}
-
-static int imesh_totface(const IMesh *im)
-{
- if (im->bm) {
- return im->bm->totface;
- }
- else {
- return 0; /* TODO */
- }
-}
-
-static int imesh_facelen(const IMesh *im, int f)
-{
- int ans = 0;
-
- if (im->bm) {
- BMFace *bmf = BM_face_at_index(im->bm, f);
- if (bmf) {
- ans = bmf->len;
- }
- }
- else {
- ; /* TODO */
- }
- return ans;
-}
-
-static void imesh_get_face_no(const IMesh *im, int f, double *r_no)
-{
- if (im->bm) {
- BMFace *bmf = BM_face_at_index(im->bm, f);
- copy_v3db_v3fl(r_no, bmf->no);
- }
- else {
- ; /* TODO */
- }
-}
-
-static int imesh_face_vert(const IMesh *im, int f, int index)
-{
- int i;
- int ans = -1;
-
- if (im->bm) {
- BMFace *bmf = BM_face_at_index(im->bm, f);
- if (bmf) {
- BMLoop *l = bmf->l_first;
- for (i = 0; i < index; i++) {
- l = l->next;
- }
- BMVert *bmv = l->v;
- ans = BM_elem_index_get(bmv);
- }
- }
- else {
- ; /* TODO */
- }
- return ans;
-}
-
-static void imesh_get_vert_co(const IMesh *im, int v, float *r_coords)
-{
- if (im->bm) {
- BMVert *bmv = BM_vert_at_index(im->bm, v);
- if (bmv) {
- copy_v3_v3(r_coords, bmv->co);
- return;
- }
- else {
- zero_v3(r_coords);
- }
- }
- else {
- ; /* TODO */
- }
-}
-
-static void imesh_get_vert_co_db(const IMesh *im, int v, double *r_coords)
-{
- if (im->bm) {
- BMVert *bmv = BM_vert_at_index(im->bm, v);
- if (bmv) {
- copy_v3db_v3fl(r_coords, bmv->co);
- return;
- }
- else {
- zero_v3_db(r_coords);
- }
- }
- else {
- ; /* TODO */
- }
-}
-
-static KDTree_3d *make_im_co_tree(IMesh *im)
-{
- KDTree_3d *tree;
- int v, nv;
- float co[3];
-
- nv = imesh_totvert(im);
- tree = BLI_kdtree_3d_new((uint)nv);
- for (v = 0; v < nv; v++) {
- imesh_get_vert_co(im, v, co);
- BLI_kdtree_3d_insert(tree, v, co);
- }
- BLI_kdtree_3d_balance(tree);
- return tree;
-}
-
-/* Find a vertex in im eps-close to co, if it exists.
- * If there are multiple, return the one with the lowest vertex index.
- * Else return -1.
- */
-
-/* Callback to find min index vertex within eps range. */
-static bool find_co_cb(void *user_data, int index, const float co[3], float dist_sq)
-{
- int *v = (int *)user_data;
- if (*v == -1) {
- *v = index;
- }
- else {
- *v = min_ii(*v, index);
- }
- UNUSED_VARS(co, dist_sq);
- return true;
-}
-
-static int imesh_find_co_db(const IMesh *im, const double co[3], double eps)
-{
- int v;
- float feps = (float)eps;
- float fco[3];
-
- copy_v3fl_v3db(fco, co);
- v = -1;
- BLI_kdtree_3d_range_search_cb(im->co_tree, fco, feps, find_co_cb, &v);
- return v;
-}
-
-/* Find an edge in im between given two verts (either order ok), if it exists.
- * Else return -1.
- * TODO: speed this up.
- */
-static int imesh_find_edge(const IMesh *im, int v1, int v2)
-{
- BMesh *bm = im->bm;
-
- if (bm) {
- BMEdge *bme;
- BMVert *bmv1, *bmv2;
- BMIter iter;
-
- if (v1 >= bm->totvert || v2 >= bm->totvert) {
- return -1;
- }
- bmv1 = BM_vert_at_index(bm, v1);
- bmv2 = BM_vert_at_index(bm, v2);
- BM_ITER_ELEM (bme, &iter, bmv1, BM_EDGES_OF_VERT) {
- if (BM_edge_other_vert(bme, bmv1) == bmv2) {
- return BM_elem_index_get(bme);
- }
- }
- return -1;
- }
- else {
- return -1; /* TODO */
- }
-}
-
-static void imesh_get_edge_cos_db(const IMesh *im, int e, double *r_coords1, double *r_coords2)
-{
- if (im->bm) {
- BMEdge *bme = BM_edge_at_index(im->bm, e);
- if (bme) {
- copy_v3db_v3fl(r_coords1, bme->v1->co);
- copy_v3db_v3fl(r_coords2, bme->v2->co);
- }
- else {
- zero_v3_db(r_coords1);
- zero_v3_db(r_coords2);
- }
- }
- else {
- ; /* TODO */
- }
-}
-
-static void imesh_get_edge_verts(const IMesh *im, int e, int *r_v1, int *r_v2)
-{
- if (im->bm) {
- BMEdge *bme = BM_edge_at_index(im->bm, e);
- if (bme) {
- *r_v1 = BM_elem_index_get(bme->v1);
- *r_v2 = BM_elem_index_get(bme->v2);
- }
- else {
- *r_v1 = -1;
- *r_v2 = -1;
- }
- }
- else {
- ; /* TODO */
- }
-}
-
-ATTU static void imesh_get_face_plane_db(const IMesh *im, int f, double r_plane[4])
-{
- double plane_co[3];
-
- zero_v4_db(r_plane);
- if (im->bm) {
- BMFace *bmf = BM_face_at_index(im->bm, f);
- if (bmf) {
- /* plane_from_point_normal_v3 with mixed arithmetic */
- copy_v3db_v3fl(r_plane, bmf->no);
- copy_v3db_v3fl(plane_co, bmf->l_first->v->co);
- r_plane[3] = -dot_v3v3_db(r_plane, plane_co);
- }
- }
-}
-
-static void imesh_get_face_plane(const IMesh *im, int f, float r_plane[4])
-{
- float plane_co[3];
-
- zero_v4(r_plane);
- if (im->bm) {
- BMFace *bmf = BM_face_at_index(im->bm, f);
- if (bmf) {
- /* plane_from_point_normal_v3 with mixed arithmetic */
- copy_v3_v3(r_plane, bmf->no);
- copy_v3_v3(plane_co, bmf->l_first->v->co);
- r_plane[3] = -dot_v3v3(r_plane, plane_co);
- }
- }
-}
-
-static void imesh_calc_point_in_face(IMesh *im, int f, double co[3])
-{
- if (im->bm) {
- float fco[3];
- BMFace *bmf = BM_face_at_index(im->bm, f);
- BM_face_calc_point_in_face(bmf, fco);
- copy_v3db_v3fl(co, fco);
- }
- else {
- ; /* TODO */
- }
-}
-
-/* Return a tesselation of f into triangles.
- * There will always be flen - 2 triangles where f is f's face length.
- * Caller must supply array of size (flen - 2) * 3 ints.
- * Return will be triples of indices of the vertices around f.
- */
-static void imesh_face_calc_tesselation(IMesh *im, int f, int (*r_index)[3])
-{
- if (im->bm) {
- BMFace *bmf = BM_face_at_index(im->bm, f);
- BMLoop **loops = BLI_array_alloca(loops, (size_t)bmf->len);
- /* OK to use argument use_fixed_quad == true: don't need convex quads. */
- BM_face_calc_tessellation(bmf, true, loops, (uint(*)[3])r_index);
- /* Need orientation of triangles to match that of face. Because of using
- * use_fix_quads == true, we know that we only might have a problem here
- * for polygons with more than 4 sides. */
- if (bmf->len > 4) {
- float tri0_no[3];
- BMVert *v0, *v1, *v2;
- v0 = loops[r_index[0][0]]->v;
- v1 = loops[r_index[0][1]]->v;
- v2 = loops[r_index[0][2]]->v;
- normal_tri_v3(tri0_no, v0->co, v1->co, v2->co);
- if (dot_v3v3(tri0_no, bmf->no) < 0.0f) {
- /* Need to reverse winding order for all triangles in tesselation. */
- int i, tmp;
- for (i = 0; i < bmf->len - 2; i++) {
- tmp = r_index[i][1];
- r_index[i][1] = r_index[i][2];
- r_index[i][2] = tmp;
- }
- }
- }
- }
- else {
- ; /* TODO */
- }
-}
-
-static int resolve_merge(int v, const IntIntMap *vert_merge_map)
-{
- int vmapped = v;
- int target;
-
- while (find_in_intintmap(vert_merge_map, vmapped, &target)) {
- vmapped = target;
- }
- return vmapped;
-}
-
-/* Trying this instead of trying to keep the tables up to date.
- * I keep having this bug where strange BMVerts are being used
- * and I hope this will fix it.
- */
-#define USE_BM_ELEM_COPY
-
-/* To store state of side (side a / side b / opp normals) we will
- * use these hflag tags in BMFaces. Note that the modifier currently
- * uses BM_ELEM_DRAW for side a / side b; we'll overwrite that as
- * modifier code doesn't use it again after this routine returns.
- */
-#define SIDE_A_TAG BM_ELEM_TAG
-#define SIDE_B_TAG BM_ELEM_DRAW
-#define BOTH_SIDES_OPP_NORMALS_TAG (1 << 6)
-#define ALL_SIDE_TAGS (SIDE_A_TAG | SIDE_B_TAG | BOTH_SIDES_OPP_NORMALS_TAG)
-
-/* Apply the change to the BMesh. Ensure that indices are valid afterwards.
- * Also reallocate bs->face_side and set it appropriately,
- * including marking those faces that have examples on both sides but have opposite
- * normals with the flag that says that.
- */
-static void apply_meshchange_to_bmesh(BoolState *bs, BMesh *bm, MeshChange *change)
-{
- int bm_tot_v, bm_tot_e, bm_tot_f, tot_new_v, tot_new_e, tot_new_f;
- int i, v, e, f, v1, v2;
- int facelen, max_facelen;
- NewVert *newvert;
- NewEdge *newedge;
- NewFace *newface;
- BMVert **new_bmvs, **face_bmvs;
- BMVert *bmv, *bmv1, *bmv2;
- BMEdge **new_bmes, **face_bmes;
- BMEdge *bme, *bme_eg;
- BMFace *bmf, *bmf_eg;
-#ifdef USE_BM_ELEM_COPY
- BMFace **new_bmfs;
-#endif
- int fside;
-
- MeshAdd *meshadd = &change->add;
- MeshDelete *meshdelete = &change->delete;
- IntIntMap *vert_merge_map = &change->vert_merge_map;
- IntSet *intersection_edges = &change->intersection_edges;
- IntSetIterator is_iter;
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\n\nAPPLY_MESHCHANGE_TO_BMESH\n\n");
- if (dbg_level > 1) {
- dump_meshchange(change, "change to apply");
- }
- }
-#endif
-
- /* Create new BMVerts. */
- BM_mesh_elem_table_ensure(bm, BM_VERT);
- bm_tot_v = bm->totvert;
- tot_new_v = meshadd_totvert(meshadd);
-#ifdef USE_BM_ELEM_COPY
- new_bmvs = MEM_mallocN((size_t)(bm_tot_v + tot_new_v) * sizeof(BMVert *), __func__);
- BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)new_bmvs, bm_tot_v);
-#endif
- if (tot_new_v > 0) {
-#ifndef USE_BM_ELEM_COPY
- new_bmvs = BLI_array_alloca(new_bmvs, (size_t)tot_new_v);
-#endif
- BLI_assert(meshadd->vindex_start == bm_tot_v);
- for (v = meshadd->vindex_start; v < meshadd->vindex_start + tot_new_v; v++) {
- newvert = meshadd_get_newvert(meshadd, v);
- BLI_assert(newvert != NULL);
- bmv = BM_vert_create(bm, newvert->co, NULL, 0);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("created new BMVert for new vert %d at (%f,%f,%f)\n", v, F3(newvert->co));
- printf(" -> bmv=%p\n", bmv);
- /* BM_mesh_validate(bm); */
- }
-#endif
-#ifdef USE_BM_ELEM_COPY
- new_bmvs[v] = bmv;
-#else
- new_bmvs[v - meshadd->vindex_start] = bmv;
-#endif
- }
- }
-
-#ifndef USE_BM_ELEM_COPY
- /* Adding verts has made the vertex table dirty.
- * It is probably still ok, but just in case...
- * TODO: find a way to avoid regenerating this table, maybe.
- */
- BM_mesh_elem_table_ensure(bm, BM_VERT);
-#endif
-
- /* Now the edges. */
- bm_tot_e = bm->totedge;
- tot_new_e = meshadd_totedge(meshadd);
-#ifdef USE_BM_ELEM_COPY
- new_bmes = MEM_mallocN((size_t)(bm_tot_e + tot_new_e) * sizeof(BMEdge *), __func__);
- BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)new_bmes, bm_tot_e);
-#endif
- if (tot_new_e > 0) {
-#ifndef USE_BM_ELEM_COPY
- new_bmes = BLI_array_alloca(new_bmes, (size_t)tot_new_e);
-#endif
- BLI_assert(meshadd->eindex_start == bm_tot_e);
- for (e = meshadd->eindex_start; e < meshadd->eindex_start + tot_new_e; e++) {
- newedge = meshadd_get_newedge(meshadd, e);
- BLI_assert(newedge != NULL);
- if (newedge->example != -1) {
- /* Not really supposed to access bm->etable directly but even
- * though it may be technically dirty, all of the example indices
- * will still be OK since they should be from original edges. */
- BLI_assert(newedge->example < meshadd->eindex_start);
-#ifdef USE_BM_ELEM_COPY
- bme_eg = new_bmes[newedge->example];
-#else
- bme_eg = bm->etable[newedge->example];
-#endif
- BLI_assert(bme_eg != NULL && bme_eg->head.htype == BM_EDGE);
- }
- else {
- bme_eg = NULL;
- }
- v1 = newedge->v1;
- v2 = newedge->v2;
-#ifdef USE_BM_ELEM_COPY
- if (v1 < bm_tot_v) {
- v1 = resolve_merge(v1, vert_merge_map);
- }
- bmv1 = new_bmvs[v1];
-#else
- if (v1 < bm_tot_v) {
- v1 = resolve_merge(v1, vert_merge_map);
- bmv1 = BM_vert_at_index(bm, v1);
- }
- else {
- bmv1 = new_bmvs[v1 - meshadd->vindex_start];
- }
-#endif
- BLI_assert(bmv1 != NULL);
-#ifdef USE_BM_ELEM_COPY
- if (v2 < bm_tot_v) {
- v2 = resolve_merge(v2, vert_merge_map);
- }
- bmv2 = new_bmvs[v2];
-#else
- if (v2 < bm_tot_v) {
- v2 = resolve_merge(v2, vert_merge_map);
- bmv2 = BM_vert_at_index(bm, v2);
- }
- else {
- bmv2 = new_bmvs[v2 - meshadd->vindex_start];
- }
-#endif
- BLI_assert(bmv2 != NULL);
- BLI_assert(v1 != v2 && bmv1 != bmv2);
- bme = BM_edge_create(bm, bmv1, bmv2, bme_eg, BM_CREATE_NO_DOUBLE);
- if (bme_eg) {
- BM_elem_select_copy(bm, bme, bme_eg);
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("created BMEdge for new edge %d, v1=%d, v2=%d, bmv1=%p, bmv2=%p\n",
- e,
- v1,
- v2,
- bmv1,
- bmv2);
- printf(" -> bme=%p\n", bme);
- /* BM_mesh_validate(bm); */
- }
-#endif
-#ifdef USE_BM_ELEM_COPY
- new_bmes[e] = bme;
-#else
- new_bmes[e - meshadd->eindex_start] = bme;
-#endif
- }
- }
-
- /* Now the faces. */
- bm_tot_f = bm->totface;
- tot_new_f = meshadd_totface(meshadd);
-#ifdef USE_BM_ELEM_COPY
- new_bmfs = MEM_mallocN((size_t)(bm_tot_f + tot_new_f) * sizeof(BMFace *), __func__);
- BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)new_bmfs, bm_tot_f);
-#endif
- /* When we kill faces later, the faces will get new indices, destroying
- * the correspondence with the bs->face_side table, so use tags for these
- * so we can retrieve them from BMFaces later and create a new face_side table.
- */
- for (f = 0; f < bm_tot_f; f++) {
- bmf = new_bmfs[f];
- fside = bs->face_side[f];
- BM_elem_flag_disable(bmf, ALL_SIDE_TAGS);
- if (fside & SIDE_A) {
- BM_elem_flag_enable(bmf, SIDE_A_TAG);
- }
- if (fside & SIDE_B) {
- BM_elem_flag_enable(bmf, SIDE_B_TAG);
- }
- if (fside & BOTH_SIDES_OPP_NORMALS) {
- BM_elem_flag_enable(bmf, BOTH_SIDES_OPP_NORMALS_TAG);
- }
- }
- if (tot_new_f > 0) {
- /* Find max face length so can allocate buffers just once. */
- max_facelen = 0;
- for (f = meshadd->findex_start; f < meshadd->findex_start + tot_new_f; f++) {
- newface = meshadd_get_newface(meshadd, f);
- BLI_assert(newface != NULL);
- max_facelen = max_ii(max_facelen, newface->len);
- }
- face_bmvs = BLI_array_alloca(face_bmvs, (size_t)max_facelen);
- face_bmes = BLI_array_alloca(face_bmes, (size_t)max_facelen);
- for (f = meshadd->findex_start; f < meshadd->findex_start + tot_new_f; f++) {
- newface = meshadd_get_newface(meshadd, f);
- BLI_assert(newface != NULL);
- fside = 0;
- if (newface->example != -1) {
- BLI_assert(newface->example < meshadd->findex_start);
-#ifdef USE_BM_ELEM_COPY
- bmf_eg = new_bmfs[newface->example];
-#else
- bmf_eg = bm->ftable[newface->example];
-#endif
- fside = bs->face_side[newface->example];
-
- /* See if newface has examples on both sides of the boolean operation.
- * Add its BMFace to both_sides_faces if so. */
- if (newface->other_examples) {
- int f_o;
- BMFace *bmf_eg_o;
-
- intset_iter_init(&is_iter, newface->other_examples);
- for (; !intset_iter_done(&is_iter); intset_iter_step(&is_iter)) {
- f_o = intset_iter_value(&is_iter);
-#ifdef USE_BM_ELEM_COPY
- bmf_eg_o = new_bmfs[f_o];
-#else
- bmf_eg_o = bm->ftable[f_o];
-#endif
- fside |= bs->face_side[f_o];
- if (dot_v3v3(bmf_eg->no, bmf_eg_o->no) < 0.0f) {
- fside |= BOTH_SIDES_OPP_NORMALS;
- }
- }
- }
- }
- else {
- bmf_eg = NULL;
- }
- facelen = newface->len;
- for (i = 0; i < facelen; i++) {
- v = newface->vert_edge_pairs[i].first;
-#ifdef USE_BM_ELEM_COPY
- if (v < bm_tot_v) {
- v = resolve_merge(v, vert_merge_map);
- }
- bmv = new_bmvs[v];
-#else
- if (v < bm_tot_v) {
- v = resolve_merge(v, vert_merge_map);
- bmv = BM_vert_at_index(bm, v);
- }
- else {
- bmv = new_bmvs[v - meshadd->vindex_start];
- }
-#endif
- BLI_assert(bmv != NULL);
- face_bmvs[i] = bmv;
- e = newface->vert_edge_pairs[i].second;
-#ifdef USE_BM_ELEM_COPY
- bme = new_bmes[e];
-#else
- if (e < bm_tot_e) {
- bme = bm->etable[e];
- }
- else {
- bme = new_bmes[e - meshadd->eindex_start];
- }
-#endif
- BLI_assert(bme != NULL);
- face_bmes[i] = bme;
- }
- bmf = BM_face_create(bm, face_bmvs, face_bmes, facelen, bmf_eg, 0);
- if (bmf_eg) {
- BM_elem_select_copy(bm, bmf, bmf_eg);
- }
- if (find_in_intset(&change->face_flip, f)) {
- BM_face_normal_flip(bm, bmf);
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("created BMFace for new face %d\n", f);
- for (int j = 0; j < facelen; j++) {
- printf(" v=%d=>%d, bmv=%p, vco=(%f,%f,%f), e=%d=>%d, bme=%p\n",
- newface->vert_edge_pairs[j].first,
- BM_elem_index_get(face_bmvs[j]),
- face_bmvs[j],
- F3(face_bmvs[j]->co),
- newface->vert_edge_pairs[j].second,
- BM_elem_index_get(face_bmes[j]),
- face_bmes[j]);
- }
- printf(" -> bmf = %p\n", bmf);
- /* BM_mesh_validate(bm); */
- }
-#endif
- new_bmfs[f] = bmf;
- if (fside & SIDE_A) {
- BM_elem_flag_enable(bmf, SIDE_A_TAG);
- }
- if (fside & SIDE_B) {
- BM_elem_flag_enable(bmf, SIDE_B_TAG);
- }
- if (fside & BOTH_SIDES_OPP_NORMALS) {
- BM_elem_flag_enable(bmf, BOTH_SIDES_OPP_NORMALS_TAG);
- }
- }
- }
-
- /* Some original faces need their normals flipped. */
- intset_iter_init(&is_iter, &change->face_flip);
- for (; !intset_iter_done(&is_iter); intset_iter_step(&is_iter)) {
- f = intset_iter_value(&is_iter);
- if (f < bm_tot_f) {
- bmf = bm->ftable[f];
- BM_face_normal_flip(bm, bmf);
- }
- }
-
- /* Need to tag the intersection edges. */
- intset_iter_init(&is_iter, intersection_edges);
- for (; !intset_iter_done(&is_iter); intset_iter_step(&is_iter)) {
- e = intset_iter_value(&is_iter);
-#ifdef USE_BM_ELEM_COPY
- bme = new_bmes[e];
-#else
- if (e < bm_tot_e) {
- bme = bm->etable[e];
- }
- else {
- bme = new_bmes[e - meshadd->eindex_start];
- }
-#endif
- BM_elem_flag_enable(bme, BM_ELEM_TAG);
- }
-
- /* Delete the geometry we are supposed to delete now. */
- for (f = 0; f < bm_tot_f; f++) {
- if (meshdelete_find_face(meshdelete, f)) {
- bmf = bm->ftable[f];
- if (change->use_face_kill_loose) {
- BM_face_kill_loose(bm, bmf);
- }
- else {
- BM_face_kill(bm, bmf);
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("killed bmf=%p for ftable[%d]\n", bmf, f);
- /* BM_mesh_validate(bm); */
- }
-#endif
- }
- }
- for (e = 0; e < bm_tot_e; e++) {
- if (meshdelete_find_edge(meshdelete, e)) {
- bme = bm->etable[e];
- BM_edge_kill(bm, bme);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("killed bme=%p for etable[%d]\n", bme, e);
- /* BM_mesh_validate(bm); */
- }
-#endif
- }
- }
- for (v = 0; v < bm_tot_v; v++) {
- if (meshdelete_find_vert(meshdelete, v)) {
- bmv = bm->vtable[v];
- BM_vert_kill(bm, bmv);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("killed bmv=%p for vtable[%d]\n", bmv, v);
- /* BM_mesh_validate(bm); */
- }
-#endif
- }
- }
- BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
- BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
-
- /* Make a new face_side table. */
- bs->face_side = BLI_memarena_alloc(bs->mem_arena, (size_t)bm->totface * sizeof(uchar));
- for (f = 0; f < bm->totface; f++) {
- bmf = BM_face_at_index(bm, f);
- fside = 0;
- if (BM_elem_flag_test(bmf, SIDE_A_TAG)) {
- fside |= SIDE_A;
- }
- if (BM_elem_flag_test(bmf, SIDE_B_TAG)) {
- fside |= SIDE_B;
- }
- if (BM_elem_flag_test(bmf, BOTH_SIDES_OPP_NORMALS_TAG)) {
- fside |= BOTH_SIDES_OPP_NORMALS;
- }
- bs->face_side[f] = (uchar)fside;
- BM_elem_flag_disable(bmf, ALL_SIDE_TAGS);
- }
-
-#ifdef USE_BM_ELEM_COPY
- MEM_freeN(new_bmvs);
- MEM_freeN(new_bmes);
- MEM_freeN(new_bmfs);
-#endif
-}
-
-static void apply_meshchange_to_imesh(BoolState *bs, IMesh *im, MeshChange *change)
-{
- if (im->bm) {
- apply_meshchange_to_bmesh(bs, im->bm, change);
- }
- else {
- /* TODO */
- }
-}
-
-static void bb_update(double bbmin[3], double bbmax[3], int v, const IMesh *im)
-{
- int i;
- float vco[3];
- double vcod[3];
-
- imesh_get_vert_co(im, v, vco);
- copy_v3db_v3fl(vcod, vco);
- for (i = 0; i < 3; i++) {
- bbmin[i] = min_dd(vcod[i], bbmin[i]);
- bbmax[i] = max_dd(vcod[i], bbmax[i]);
- }
-}
-
-/* Function used for imesh_calc_face_groups to return true
- * when we should cross this loop l to new faces to accumulate
- * faces in the same group.
- * This allows such traversal if there is no other loop in the
- * loop radial that has a face on the opposite 'side' of the boolean operation.
- */
-static bool boolfilterfn(const BMLoop *l, void *user_data)
-{
- int fside, fside_other;
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("boolfilterfun: l = loop from v%d to v%d in face f%d\n",
- BMI(l->v),
- BMI(l->next->v),
- BMI(l->f));
- }
-#endif
- if (l->radial_next != l) {
- uchar *face_side = user_data;
- BMLoop *l_iter = l->radial_next;
- fside = face_side[BM_elem_index_get(l->f)];
- do {
- fside_other = face_side[BM_elem_index_get(l_iter->f)];
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf(" l_iter = loop from v%d to v%d in face f%d\n",
- BMI(l_iter->v),
- BMI(l_iter->next->v),
- BMI(l_iter->f));
- printf(" fside=%d fside_other=%d\n", fside, fside_other);
- }
-#endif
- if ((fside ^ fside_other) != 0) {
- return false;
- }
- } while ((l_iter = l_iter->radial_next) != l);
- return true;
- }
- return false;
-}
-
-/* Calculate groups of faces.
- * In this context, a 'group' is a set of maximal set of faces in the same
- * boolean 'side', where side is determined by bs->test_fn.
- * Maximal in the sense that the faces are connected across edges that
- * are only attached to faces in the same side.
- *
- * The r_groups_array ahould be an array of length = # of faces in the IMesh.
- * It will be filled with face indices, partitioned into groups.
- * The r_group_index pointer will be set to point at an array of pairs of ints
- * (which must be MEM_freeN'd when done), of length # of groups = returned value.
- * Each pair has a start index in r_groups_array and a length, specifying
- * the part of r_groups_array that has the face indices for the group.
- */
-static int imesh_calc_face_groups(BoolState *bs, int *r_groups_array, int (**r_group_index)[2])
-{
- int ngroups;
- IMesh *im = &bs->im;
-
- if (im->bm) {
- BM_mesh_elem_table_ensure(im->bm, BM_FACE);
- BM_mesh_elem_index_ensure(im->bm, BM_FACE);
- ngroups = BM_mesh_calc_face_groups(
- im->bm, r_groups_array, r_group_index, boolfilterfn, bs->face_side, 0, BM_EDGE);
- }
- else {
- /* TODO */
- ngroups = 0;
- }
- return ngroups;
-}
-
-/** MeshAdd functions. */
-
-static void init_meshadd(BoolState *bs, MeshAdd *meshadd)
-{
- IMesh *im = &bs->im;
- uint guess_added_verts, guess_added_edges, guess_added_faces;
- MemArena *arena = bs->mem_arena;
-
- memset(meshadd, 0, sizeof(MeshAdd));
- meshadd->im = im;
- meshadd->vindex_start = imesh_totvert(im);
- meshadd->eindex_start = imesh_totedge(im);
- meshadd->findex_start = imesh_totface(im);
-
- /* A typical intersection of two shells has O(sqrt(# of faces in bigger part)) intersection
- * edges. */
- guess_added_verts = (uint)min_ii(20 * (int)sqrtf((float)imesh_totvert(im)), 100);
- guess_added_edges = guess_added_verts;
- guess_added_faces = 2 * guess_added_edges;
- meshadd->vert_reserved = guess_added_verts;
- meshadd->edge_reserved = guess_added_edges;
- meshadd->face_reserved = guess_added_faces;
- meshadd->verts = BLI_memarena_alloc(arena, meshadd->vert_reserved * sizeof(NewVert *));
- meshadd->edges = BLI_memarena_alloc(arena, meshadd->edge_reserved * sizeof(NewEdge *));
- meshadd->faces = BLI_memarena_alloc(arena, meshadd->face_reserved * sizeof(NewFace *));
- meshadd->edge_hash = BLI_edgehash_new_ex("bool meshadd", guess_added_edges);
-}
-
-static void meshadd_free_aux_data(MeshAdd *meshadd)
-{
- BLI_edgehash_free(meshadd->edge_hash, NULL);
-}
-
-static inline int meshadd_totvert(const MeshAdd *meshadd)
-{
- return meshadd->totvert;
-}
-
-static inline int meshadd_totedge(const MeshAdd *meshadd)
-{
- return meshadd->totedge;
-}
-
-static inline int meshadd_totface(const MeshAdd *meshadd)
-{
- return meshadd->totface;
-}
-
-static int meshadd_add_vert(
- BoolState *bs, MeshAdd *meshadd, const float co[3], int example, bool checkdup)
-{
- NewVert *newv;
- MemArena *arena = bs->mem_arena;
- int i;
-
- if (checkdup) {
- for (i = 0; i < meshadd->totvert; i++) {
- newv = meshadd->verts[i];
- if (compare_v3v3(newv->co, co, (float)bs->eps)) {
- return meshadd->vindex_start + i;
- }
- }
- }
- newv = BLI_memarena_alloc(arena, sizeof(*newv));
- copy_v3_v3(newv->co, co);
- newv->example = example;
- if ((uint)meshadd->totvert == meshadd->vert_reserved) {
- uint new_reserved = 2 * meshadd->vert_reserved;
- NewVert **new_buf = BLI_memarena_alloc(arena, new_reserved * sizeof(NewVert *));
- memcpy(new_buf, meshadd->verts, meshadd->vert_reserved * sizeof(NewVert *));
- meshadd->verts = new_buf;
- meshadd->vert_reserved = new_reserved;
- }
- meshadd->verts[meshadd->totvert] = newv;
- meshadd->totvert++;
- return meshadd->vindex_start + meshadd->totvert - 1;
-}
-
-static int meshadd_add_vert_db(
- BoolState *bs, MeshAdd *meshadd, const double co[3], int example, bool checkdup)
-{
- float fco[3];
- copy_v3fl_v3db(fco, co);
- return meshadd_add_vert(bs, meshadd, fco, example, checkdup);
-}
-
-static int meshadd_add_edge(
- BoolState *bs, MeshAdd *meshadd, int v1, int v2, int example, bool checkdup)
-{
- NewEdge *newe;
- MemArena *arena = bs->mem_arena;
- int i;
-
- if (checkdup) {
- i = POINTER_AS_INT(
- BLI_edgehash_lookup_default(meshadd->edge_hash, (uint)v1, (uint)v2, POINTER_FROM_INT(-1)));
- if (i != -1) {
- return i;
- }
- }
- newe = BLI_memarena_alloc(arena, sizeof(*newe));
- newe->v1 = v1;
- newe->v2 = v2;
- newe->example = example;
- BLI_assert(example == -1 || example < meshadd->eindex_start);
- if ((uint)meshadd->totedge == meshadd->edge_reserved) {
- uint new_reserved = 2 * meshadd->edge_reserved;
- NewEdge **new_buf = BLI_memarena_alloc(arena, new_reserved * sizeof(NewEdge *));
- memcpy(new_buf, meshadd->edges, meshadd->edge_reserved * sizeof(NewEdge *));
- meshadd->edges = new_buf;
- meshadd->edge_reserved = new_reserved;
- }
- meshadd->edges[meshadd->totedge] = newe;
- BLI_edgehash_insert(meshadd->edge_hash, (uint)v1, (uint)v2, POINTER_FROM_INT(meshadd->totedge));
- meshadd->totedge++;
- return meshadd->eindex_start + meshadd->totedge - 1;
-}
-
-/* This assumes that vert_edge is an arena-allocated array that will persist. */
-static int meshadd_add_face(BoolState *bs,
- MeshAdd *meshadd,
- IntPair *vert_edge,
- int len,
- int example,
- IntSet *other_examples)
-{
- NewFace *newf;
- MemArena *arena = bs->mem_arena;
-
- newf = BLI_memarena_alloc(arena, sizeof(*newf));
- newf->vert_edge_pairs = vert_edge;
- newf->len = len;
- newf->example = example;
- newf->other_examples = other_examples;
- if ((uint)meshadd->totface == meshadd->face_reserved) {
- uint new_reserved = 2 * meshadd->face_reserved;
- NewFace **new_buf = BLI_memarena_alloc(arena, new_reserved * sizeof(NewFace *));
- memcpy(new_buf, meshadd->faces, meshadd->face_reserved * sizeof(NewFace *));
- meshadd->faces = new_buf;
- meshadd->face_reserved = new_reserved;
- }
- meshadd->faces[meshadd->totface] = newf;
- meshadd->totface++;
- return meshadd->findex_start + meshadd->totface - 1;
-}
-
-static int meshadd_facelen(const MeshAdd *meshadd, int f)
-{
- NewFace *nf;
- int i;
-
- i = f - meshadd->findex_start;
- if (i >= 0 && i < meshadd->totface) {
- nf = meshadd->faces[i];
- return nf->len;
- }
- return 0;
-}
-
-static void meshadd_get_face_no(const MeshAdd *meshadd, int f, double *r_no)
-{
- NewFace *nf;
- int i;
-
- i = f - meshadd->findex_start;
- if (i >= 0 && i < meshadd->totface) {
- nf = meshadd->faces[i];
- if (nf->example) {
- imesh_get_face_no(meshadd->im, nf->example, r_no);
- }
- else {
- printf("unexpected meshadd_get_face_no on face without example\n");
- BLI_assert(false);
- }
- }
-}
-
-static int meshadd_face_vert(const MeshAdd *meshadd, int f, int index)
-{
- NewFace *nf;
- int i;
-
- i = f - meshadd->findex_start;
- if (i >= 0 && i < meshadd->totface) {
- nf = meshadd->faces[i];
- if (index >= 0 && index < nf->len) {
- return nf->vert_edge_pairs[index].first;
- }
- }
- return -1;
-}
-
-static NewVert *meshadd_get_newvert(const MeshAdd *meshadd, int v)
-{
- int i;
-
- i = v - meshadd->vindex_start;
- if (i >= 0 && i < meshadd->totvert) {
- return meshadd->verts[i];
- }
- else {
- return NULL;
- }
-}
-
-static NewEdge *meshadd_get_newedge(const MeshAdd *meshadd, int e)
-{
- int i;
-
- i = e - meshadd->eindex_start;
- if (i >= 0 && i < meshadd->totedge) {
- return meshadd->edges[i];
- }
- else {
- return NULL;
- }
-}
-
-static NewFace *meshadd_get_newface(const MeshAdd *meshadd, int f)
-{
- int i;
-
- i = f - meshadd->findex_start;
- if (i >= 0 && i < meshadd->totface) {
- return meshadd->faces[i];
- }
- else {
- return NULL;
- }
-}
-
-static void meshadd_get_vert_co(const MeshAdd *meshadd, int v, float *r_coords)
-{
- NewVert *nv;
-
- nv = meshadd_get_newvert(meshadd, v);
- if (nv) {
- copy_v3_v3(r_coords, nv->co);
- }
- else {
- zero_v3(r_coords);
- }
-}
-
-static void meshadd_get_vert_co_db(const MeshAdd *meshadd, int v, double *r_coords)
-{
- float fco[3];
-
- meshadd_get_vert_co(meshadd, v, fco);
- copy_v3db_v3fl(r_coords, fco);
-}
-
-static void meshadd_get_edge_verts(const MeshAdd *meshadd, int e, int *r_v1, int *r_v2)
-{
- NewEdge *ne;
-
- ne = meshadd_get_newedge(meshadd, e);
- if (ne) {
- *r_v1 = ne->v1;
- *r_v2 = ne->v2;
- }
- else {
- *r_v1 = -1;
- *r_v2 = -1;
- }
-}
-
-static int find_edge_by_verts_in_meshadd(const MeshAdd *meshadd, int v1, int v2)
-{
- int i;
-
- i = POINTER_AS_INT(
- BLI_edgehash_lookup_default(meshadd->edge_hash, (uint)v1, (uint)v2, POINTER_FROM_INT(-1)));
- if (i != -1) {
- return meshadd->eindex_start + i;
- }
- return -1;
-}
-
-/** MeshDelete functions. */
-
-static void init_meshdelete(BoolState *bs, MeshDelete *meshdelete)
-{
- IMesh *im = &bs->im;
- MemArena *arena = bs->mem_arena;
-
- meshdelete->totvert = imesh_totvert(im);
- meshdelete->totedge = imesh_totedge(im);
- meshdelete->totface = imesh_totface(im);
- /* The BLI_BITMAP_NEW... functions clear the bitmaps too. */
- meshdelete->vert_bmap = BLI_BITMAP_NEW_MEMARENA(arena, meshdelete->totvert);
- meshdelete->edge_bmap = BLI_BITMAP_NEW_MEMARENA(arena, meshdelete->totedge);
- meshdelete->face_bmap = BLI_BITMAP_NEW_MEMARENA(arena, meshdelete->totface);
-}
-
-ATTU static void meshdelete_add_vert(MeshDelete *meshdelete, int v)
-{
- BLI_assert(0 <= v && v < meshdelete->totvert);
- BLI_BITMAP_ENABLE(meshdelete->vert_bmap, v);
-}
-
-static void meshdelete_add_edge(MeshDelete *meshdelete, int e)
-{
- BLI_assert(0 <= e && e < meshdelete->totedge);
- BLI_BITMAP_ENABLE(meshdelete->edge_bmap, e);
-}
-
-static void meshdelete_add_face(MeshDelete *meshdelete, int f)
-{
- BLI_assert(0 <= f && f < meshdelete->totface);
- BLI_BITMAP_ENABLE(meshdelete->face_bmap, f);
-}
-
-ATTU static void meshdelete_remove_vert(MeshDelete *meshdelete, int v)
-{
- BLI_assert(0 <= v && v < meshdelete->totvert);
- BLI_BITMAP_DISABLE(meshdelete->vert_bmap, v);
-}
-
-static void meshdelete_remove_edge(MeshDelete *meshdelete, int e)
-{
- BLI_assert(0 <= e && e < meshdelete->totedge);
- BLI_BITMAP_DISABLE(meshdelete->edge_bmap, e);
-}
-
-ATTU static void meshdelete_remove_face(MeshDelete *meshdelete, int f)
-{
- BLI_assert(0 <= f && f < meshdelete->totface);
- BLI_BITMAP_DISABLE(meshdelete->face_bmap, f);
-}
-
-ATTU static bool meshdelete_find_vert(MeshDelete *meshdelete, int v)
-{
- BLI_assert(0 <= v && v < meshdelete->totvert);
- return BLI_BITMAP_TEST_BOOL(meshdelete->vert_bmap, v);
-}
-
-static bool meshdelete_find_edge(MeshDelete *meshdelete, int e)
-{
- BLI_assert(0 <= e && e < meshdelete->totedge);
- return BLI_BITMAP_TEST_BOOL(meshdelete->edge_bmap, e);
-}
-
-static bool meshdelete_find_face(MeshDelete *meshdelete, int f)
-{
- BLI_assert(0 <= f && f < meshdelete->totface);
- return BLI_BITMAP_TEST_BOOL(meshdelete->face_bmap, f);
-}
-
-/** MeshChange functions. */
-
-static void init_meshchange(BoolState *bs, MeshChange *meshchange)
-{
- init_intintmap(&meshchange->vert_merge_map);
- init_meshadd(bs, &meshchange->add);
- init_meshdelete(bs, &meshchange->delete);
- init_intset(&meshchange->intersection_edges);
- init_intset(&meshchange->face_flip);
- meshchange->use_face_kill_loose = false;
-}
-
-static void meshchange_free_aux_data(MeshChange *meshchange)
-{
- meshadd_free_aux_data(&meshchange->add);
-}
-
-/** MeshPartSet functions. */
-
-static void init_meshpartset(BoolState *bs, MeshPartSet *partset, int reserve, const char *label)
-{
- partset->meshparts = NULL;
- partset->meshparts = BLI_memarena_alloc(bs->mem_arena, (size_t)reserve * sizeof(MeshPart *));
- partset->tot_part = 0;
- partset->reserve = reserve;
- zero_v3_db(partset->bbmin);
- zero_v3_db(partset->bbmax);
- partset->label = label;
-}
-
-static inline void add_part_to_partset(MeshPartSet *partset, MeshPart *part)
-{
- BLI_assert(partset->tot_part < partset->reserve);
- partset->meshparts[partset->tot_part++] = part;
-}
-
-static inline MeshPart *partset_part(const MeshPartSet *partset, int index)
-{
- BLI_assert(index < partset->tot_part);
- return partset->meshparts[index];
-}
-
-/* Fill in partset->bbmin and partset->bbmax with axis aligned bounding box
- * for the partset.
- * Also calculates bbmin and bbmax for each part.
- * Add epsilon buffer on all sides.
- */
-static void calc_partset_bb_eps(BoolState *bs, MeshPartSet *partset, double eps)
-{
- MeshPart *part;
- int i, p;
-
- if (partset->meshparts == NULL) {
- zero_v3_db(partset->bbmin);
- zero_v3_db(partset->bbmax);
- return;
- }
- copy_v3_db(partset->bbmin, DBL_MAX);
- copy_v3_db(partset->bbmax, -DBL_MAX);
- for (p = 0; p < partset->tot_part; p++) {
- part = partset->meshparts[p];
- calc_part_bb_eps(bs, part, eps);
- for (i = 0; i < 3; i++) {
- partset->bbmin[i] = min_dd(partset->bbmin[i], part->bbmin[i]);
- partset->bbmax[i] = max_dd(partset->bbmax[i], part->bbmax[i]);
- }
- }
- /* eps padding was already added in calc_part_bb_eps. */
-}
-
-/** MeshPart functions. */
-
-static void init_meshpart(BoolState *UNUSED(bs), MeshPart *part)
-{
- memset(part, 0, sizeof(*part));
-}
-
-ATTU static MeshPart *copy_part(BoolState *bs, const MeshPart *part)
-{
- MeshPart *copy;
- MemArena *arena = bs->mem_arena;
-
- copy = BLI_memarena_alloc(bs->mem_arena, sizeof(*copy));
- copy_v4_v4_db(copy->plane, part->plane);
- copy_v3_v3_db(copy->bbmin, part->bbmin);
- copy_v3_v3_db(copy->bbmax, part->bbmax);
-
- /* All links in lists are ints, so can use shallow copy. */
- copy->verts = linklist_shallow_copy_arena(part->verts, arena);
- copy->edges = linklist_shallow_copy_arena(part->edges, arena);
- copy->faces = linklist_shallow_copy_arena(part->faces, arena);
- return copy;
-}
-
-static int part_totvert(const MeshPart *part)
-{
- return BLI_linklist_count(part->verts);
-}
-
-static int part_totedge(const MeshPart *part)
-{
- return BLI_linklist_count(part->edges);
-}
-
-static int part_totface(const MeshPart *part)
-{
- return BLI_linklist_count(part->faces);
-}
-
-/*
- * Return the index in MeshPart space of the index'th face in part.
- * "MeshPart space" means that if the f returned is in the range of
- * face indices in the underlying IMesh, then it represents the face
- * in the IMesh. If f is greater than or equal to that, then it represents
- * the face that is (f - im's totf)th in the new_faces list.
- */
-static int part_face(const MeshPart *part, int index)
-{
- LinkNode *ln;
-
- ln = BLI_linklist_find(part->faces, index);
- if (ln) {
- return POINTER_AS_INT(ln->link);
- }
- return -1;
-}
-
-/* Like part_face, but for vertices. */
-static int part_vert(const MeshPart *part, int index)
-{
- LinkNode *ln;
-
- ln = BLI_linklist_find(part->verts, index);
- if (ln) {
- return POINTER_AS_INT(ln->link);
- }
- return -1;
-}
-
-/* Like part_face, but for edges. */
-static int part_edge(const MeshPart *part, int index)
-{
- LinkNode *ln;
-
- ln = BLI_linklist_find(part->edges, index);
- if (ln) {
- return POINTER_AS_INT(ln->link);
- }
- return -1;
-}
-
-/* Fill part->bbmin and part->bbmax with the axis-aligned bounding box
- * for the part.
- * Add an epsilon buffer on all sides.
- */
-static void calc_part_bb_eps(BoolState *bs, MeshPart *part, double eps)
-{
- IMesh *im = &bs->im;
- LinkNode *ln;
- int v, e, f, i, flen, j;
-
- copy_v3_db(part->bbmin, DBL_MAX);
- copy_v3_db(part->bbmax, -DBL_MAX);
- for (ln = part->verts; ln; ln = ln->next) {
- v = POINTER_AS_INT(ln->link);
- bb_update(part->bbmin, part->bbmax, v, im);
- }
- for (ln = part->edges; ln; ln = ln->next) {
- e = POINTER_AS_INT(ln->link);
- /* TODO: handle edge verts */
- printf("calc_part_bb_eps please implement edge (%d)\n", e);
- }
- for (ln = part->faces; ln; ln = ln->next) {
- f = POINTER_AS_INT(ln->link);
- flen = imesh_facelen(im, f);
- for (j = 0; j < flen; j++) {
- v = imesh_face_vert(im, f, j);
- bb_update(part->bbmin, part->bbmax, v, im);
- }
- }
- if (part->bbmin[0] == DBL_MAX) {
- zero_v3_db(part->bbmin);
- zero_v3_db(part->bbmax);
- return;
- }
- for (i = 0; i < 3; i++) {
- part->bbmin[i] -= eps;
- part->bbmax[i] += eps;
- }
-}
-
-static bool parts_may_intersect(const MeshPart *part1, const MeshPart *part2)
-{
- return isect_aabb_aabb_v3_db(part1->bbmin, part1->bbmax, part2->bbmin, part2->bbmax);
-}
-
-/* Return true if a_plane and b_plane are the same plane, to within eps.
- * Assume normal part of plane is normalized. */
-static bool planes_are_coplanar(const double a_plane[4], const double b_plane[4], double eps)
-{
- double norms_dot;
-
- /* They are the same plane even if they have opposite-facing normals,
- * in which case the 4th constants will also be opposite. */
- norms_dot = dot_v3v3_db(a_plane, b_plane);
- if (norms_dot > 0.0) {
- return fabs(norms_dot - 1.0) <= eps && fabs(a_plane[3] - b_plane[3]) <= eps;
- }
- else {
- return fabs(norms_dot + 1.0) <= eps && fabs(a_plane[3] + b_plane[3]) <= eps;
- }
-}
-
-/* Return the MeshPart in partset for plane.
- * If none exists, make a new one for the plane and add
- * it to partset.
- * TODO: perhaps have hash set of plane normal -> part.
- */
-ATTU static MeshPart *find_part_for_plane(BoolState *bs,
- MeshPartSet *partset,
- const double plane[4])
-{
- MeshPart *new_part;
- int i;
-
- for (i = 0; i < partset->tot_part; i++) {
- MeshPart *p = partset->meshparts[i];
- if (planes_are_coplanar(plane, p->plane, bs->eps)) {
- return p;
- }
- }
- new_part = BLI_memarena_alloc(bs->mem_arena, sizeof(MeshPart));
- init_meshpart(bs, new_part);
- copy_v4_v4_db(new_part->plane, plane);
- add_part_to_partset(partset, new_part);
- return new_part;
-}
-
-ATTU static void add_vert_to_part(BoolState *bs, MeshPart *part, int v)
-{
- BLI_linklist_prepend_arena(&part->verts, POINTER_FROM_INT(v), bs->mem_arena);
-}
-
-ATTU static void add_edge_to_part(BoolState *bs, MeshPart *part, int e)
-{
- BLI_linklist_prepend_arena(&part->verts, POINTER_FROM_INT(e), bs->mem_arena);
-}
-
-static void add_face_to_part(BoolState *bs, MeshPart *part, int f)
-{
- BLI_linklist_prepend_arena(&part->faces, POINTER_FROM_INT(f), bs->mem_arena);
-}
-
-/* If part consists of only one face from IMesh, return the number of vertices
- * in the face. Else return 0.
- */
-ATTU static int part_is_one_im_face(BoolState *bs, const MeshPart *part)
-{
- int f;
-
- if (part->verts == NULL && part->edges == NULL && part->faces != NULL &&
- BLI_linklist_count(part->faces) == 1) {
- f = POINTER_AS_INT(part->faces->link);
- return imesh_facelen(&bs->im, f);
- }
- return 0;
-}
-
-/** IMeshPlus functions. */
-
-static void init_imeshplus(IMeshPlus *imp, IMesh *im, MeshAdd *meshadd)
-{
- imp->im = im;
- imp->meshadd = meshadd;
-}
-
-static int imeshplus_facelen(const IMeshPlus *imp, int f)
-{
- IMesh *im = imp->im;
- return (f < imesh_totface(im)) ? imesh_facelen(im, f) : meshadd_facelen(imp->meshadd, f);
-}
-
-static void imeshplus_get_face_no(const IMeshPlus *imp, int f, double *r_no)
-{
- IMesh *im = imp->im;
- if (f < imesh_totface(im)) {
- imesh_get_face_no(im, f, r_no);
- }
- else {
- meshadd_get_face_no(imp->meshadd, f, r_no);
- }
-}
-
-static int imeshplus_face_vert(const IMeshPlus *imp, int f, int index)
-{
- IMesh *im = imp->im;
- return (f < imesh_totface(im)) ? imesh_face_vert(im, f, index) :
- meshadd_face_vert(imp->meshadd, f, index);
-}
-
-ATTU static void imeshplus_get_vert_co(const IMeshPlus *imp, int v, float *r_coords)
-{
- IMesh *im = imp->im;
- if (v < imesh_totvert(im)) {
- imesh_get_vert_co(im, v, r_coords);
- }
- else {
- meshadd_get_vert_co(imp->meshadd, v, r_coords);
- }
-}
-
-static void imeshplus_get_vert_co_db(const IMeshPlus *imp, int v, double *r_coords)
-{
- IMesh *im = imp->im;
- if (v < imesh_totvert(im)) {
- imesh_get_vert_co_db(im, v, r_coords);
- }
- else {
- meshadd_get_vert_co_db(imp->meshadd, v, r_coords);
- }
-}
-
-static void imeshplus_get_edge_verts(const IMeshPlus *imp, int e, int *r_v1, int *r_v2)
-{
- IMesh *im = imp->im;
- if (e < imesh_totedge(im)) {
- imesh_get_edge_verts(im, e, r_v1, r_v2);
- }
- else {
- meshadd_get_edge_verts(imp->meshadd, e, r_v1, r_v2);
- }
-}
-
-/** PartPartIntersect functions. */
-
-static void init_partpartintersect(PartPartIntersect *ppi)
-{
- memset(ppi, 0, sizeof(*ppi));
-}
-
-static void add_vert_to_partpartintersect(BoolState *bs, PartPartIntersect *ppi, int v)
-{
- BLI_linklist_append_arena(&ppi->verts, POINTER_FROM_INT(v), bs->mem_arena);
-}
-
-static void add_edge_to_partpartintersect(BoolState *bs, PartPartIntersect *ppi, int e)
-{
- BLI_linklist_append_arena(&ppi->edges, POINTER_FROM_INT(e), bs->mem_arena);
-}
-
-static void add_face_to_partpartintersect(BoolState *bs, PartPartIntersect *ppi, int f)
-{
- BLI_linklist_append_arena(&ppi->faces, POINTER_FROM_INT(f), bs->mem_arena);
-}
-
-/* Pick one of the two possible plane representations with unit normal as canonical. */
-static void canonicalize_plane(float plane[4])
-{
- bool do_negate = false;
- if (plane[3] != 0.0f) {
- do_negate = plane[3] > 0.0f;
- }
- else if (plane[2] != 0.0f) {
- do_negate = plane[2] > 0.0f;
- }
- else if (plane[1] != 0.0f) {
- do_negate = plane[1] > 0.0f;
- }
- else {
- do_negate = plane[0] > 0.0f;
- }
- if (do_negate) {
- plane[0] = -plane[0];
- plane[1] = -plane[1];
- plane[2] = -plane[2];
- plane[3] = -plane[3];
- }
-}
-
-/** Intersection Algorithm functions. */
-
-struct FindCoplanarCBData {
- int near_f_with_part;
- float eps;
- float test_plane[4];
- MeshPart **face_part;
-};
-
-/* See if co is a plane that is eps-close to test_plane. If there is already
- * a MeshPart for such a plane, store the lowest such index in near_f_with_part.
- */
-static bool find_coplanar_cb(void *user_data, int index, const float co[3], float UNUSED(dist_sq))
-{
- struct FindCoplanarCBData *cb_data = user_data;
- if (cb_data->face_part[index] != NULL) {
- if (fabsf(cb_data->test_plane[3] - co[3]) <= cb_data->eps &&
- fabsf(dot_v3v3(cb_data->test_plane, co) - 1.0f) <= cb_data->eps * M_2_PI) {
- if (cb_data->near_f_with_part == -1 || index < cb_data->near_f_with_part) {
- cb_data->near_f_with_part = index;
- }
- }
- }
- return true;
-}
-
-/* Fill partset with parts for each plane for which there is a face
- * in bs->im.
- * Use bs->test_fn to check elements against test_val, to see whether or not it should be in the
- * result.
- */
-static void find_coplanar_parts(BoolState *bs, MeshPartSet *partset, int sides, const char *label)
-{
- IMesh *im = &bs->im;
- MeshPart *part;
- MeshPart **face_part;
- int im_nf, f;
- int near_f;
- float plane[4];
- KDTree_4d *tree;
- struct FindCoplanarCBData cb_data;
- float feps = (float)bs->eps;
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nFIND_COPLANAR_PARTS %s, sides=%d\n", label, sides);
- }
-#endif
- im_nf = imesh_totface(im);
- init_meshpartset(bs, partset, im_nf, label);
- tree = BLI_kdtree_4d_new((unsigned int)im_nf);
- face_part = MEM_calloc_arrayN((size_t)im_nf, sizeof(MeshPart *), __func__);
- for (f = 0; f < im_nf; f++) {
- if (!(bs->face_side[f] & sides)) {
- continue;
- }
- imesh_get_face_plane(im, f, plane);
- canonicalize_plane(plane);
- BLI_kdtree_4d_insert(tree, f, plane);
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf("%d: (%f,%f,%f),%f\n", f, F4(plane));
- }
-#endif
- }
- BLI_kdtree_4d_balance(tree);
- cb_data.eps = feps;
- cb_data.face_part = face_part;
- for (f = 0; f < im_nf; f++) {
- if (!(bs->face_side[f] & sides)) {
- continue;
- }
- imesh_get_face_plane(im, f, plane);
- canonicalize_plane(plane);
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf("find part for face %d, plane=(%f,%f,%f),%f\n", f, F4(plane));
- }
-#endif
- // near_f = BLI_kdtree_4d_find_nearest_cb(tree, plane, find_coplanar_cb, face_part, &kdnear);
- cb_data.near_f_with_part = -1;
- copy_v4_v4(cb_data.test_plane, plane);
- /* Use bigger epsilon for range search because comparison function we want is a bit different
- * from 4d distance. */
- BLI_kdtree_4d_range_search_cb(tree, plane, feps * 10.0f, find_coplanar_cb, &cb_data);
- near_f = cb_data.near_f_with_part;
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf(" near_f = %d\n", near_f);
- }
-#endif
- if (near_f == -1) {
- part = BLI_memarena_alloc(bs->mem_arena, sizeof(*part));
- init_meshpart(bs, part);
- copy_v4db_v4fl(part->plane, plane);
- add_face_to_part(bs, part, f);
- add_part_to_partset(partset, part);
- face_part[f] = part;
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf(" near_f = -1, so new part made for f=%d\n", f);
- }
-#endif
- }
- else {
- add_face_to_part(bs, face_part[near_f], f);
- face_part[f] = face_part[near_f];
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf(" add to existing part %d\n", near_f);
- }
-#endif
- }
- }
- /* TODO: look for loose verts and wire edges to add to each partset */
- calc_partset_bb_eps(bs, partset, bs->eps);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- dump_partset(partset);
- }
-#endif
- BLI_kdtree_4d_free(tree);
- MEM_freeN(face_part);
-}
-
-/*
- * Intersect all the geometry in part, assumed to be in one plane, together with other
- * geometry as given in the ppis list (each link of which is a PartPartIntersect *).
- * Return a PartPartIntersect that gives the new geometry that should
- * replace the geometry in part. May also add new elements in meshadd,
- * and may also add vert merges in vert_merge_map.
- * If no output is needed, return NULL.
- */
-static PartPartIntersect *self_intersect_part_and_ppis(BoolState *bs,
- MeshPart *part,
- LinkNodePair *ppis,
- MeshChange *change)
-{
- CDT_input in;
- CDT_result *out;
- LinkNode *ln, *lnv, *lne, *lnf;
- PartPartIntersect *ppi, *ppi_out;
- MemArena *arena = bs->mem_arena;
- IMeshPlus imp;
- int i, j, part_nf, part_ne, part_nv, tot_ne, face_len, v, e, f, v1, v2;
- int nfaceverts, v_index, e_index, f_index, faces_index;
- int in_v, out_v, out_v2, start, in_e, out_e, in_f, out_f, e_eg, f_eg, f_eg_o, eg_len;
- int *imp_v, *imp_e;
- IntPair *new_face_data;
- IMesh *im = &bs->im;
- IndexedIntSet verts_needed;
- IndexedIntSet edges_needed;
- IndexedIntSet faces_needed;
- IntIntMap in_to_vmap;
- IntIntMap in_to_emap;
- IntIntMap in_to_fmap;
- IntSet *f_other_egs;
- double mat_2d[3][3];
- double mat_2d_inv[3][3];
- double xyz[3], save_z, p[3], q[3], fno[3];
- bool ok, reverse_face;
- MeshAdd *meshadd = &change->add;
- MeshDelete *meshdelete = &change->delete;
- IntIntMap *vert_merge_map = &change->vert_merge_map;
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nSELF_INTERSECT_PART_AND_PPIS\n\n");
- if (dbg_level > 1) {
- dump_part(part, "self_intersect_part");
- printf("ppis\n");
- for (ln = ppis->list; ln; ln = ln->next) {
- ppi = (PartPartIntersect *)ln->link;
- dump_partpartintersect(ppi, "");
- }
- }
- }
-#endif
- /* Find which vertices are needed for CDT input */
- part_nf = part_totface(part);
- part_ne = part_totedge(part);
- part_nv = part_totvert(part);
- if (part_nf <= 1 && part_ne == 0 && part_nv == 0 && ppis->list == NULL) {
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("trivial 1 face case\n");
- }
-#endif
- return NULL;
- }
- init_indexedintset(&verts_needed);
- init_indexedintset(&edges_needed);
- init_indexedintset(&faces_needed);
- init_intintmap(&in_to_vmap);
- init_intintmap(&in_to_emap);
- init_intintmap(&in_to_fmap);
- init_imeshplus(&imp, &bs->im, meshadd);
-
- /* nfaceverts will accumulate the total lengths of all faces added. */
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nself_intersect_part_and_ppis: gathering needed edges and verts\n\n");
- }
-#endif
- nfaceverts = 0;
- for (i = 0; i < part_nf; i++) {
- f = part_face(part, i);
- BLI_assert(f != -1);
- face_len = imesh_facelen(im, f);
- nfaceverts += face_len;
- for (j = 0; j < face_len; j++) {
- v = imesh_face_vert(im, f, j);
- BLI_assert(v != -1);
- v_index = add_int_to_indexedintset(bs, &verts_needed, v);
- add_to_intintmap(bs, &in_to_vmap, v_index, v);
- }
- f_index = add_int_to_indexedintset(bs, &faces_needed, f);
- add_to_intintmap(bs, &in_to_fmap, f_index, f);
- }
- for (i = 0; i < part_ne; i++) {
- e = part_edge(part, i);
- BLI_assert(e != -1);
- imeshplus_get_edge_verts(&imp, e, &v1, &v2);
- BLI_assert(v1 != -1 && v2 != -1);
- v_index = add_int_to_indexedintset(bs, &verts_needed, v1);
- add_to_intintmap(bs, &in_to_vmap, v_index, v1);
- v_index = add_int_to_indexedintset(bs, &verts_needed, v2);
- add_to_intintmap(bs, &in_to_vmap, v_index, v2);
- e_index = add_int_to_indexedintset(bs, &edges_needed, e);
- add_to_intintmap(bs, &in_to_emap, e_index, e);
- }
- for (i = 0; i < part_nv; i++) {
- v = part_vert(part, i);
- BLI_assert(v != -1);
- v_index = add_int_to_indexedintset(bs, &verts_needed, v);
- add_to_intintmap(bs, &in_to_vmap, v_index, v);
- }
- for (ln = ppis->list; ln; ln = ln->next) {
- ppi = (PartPartIntersect *)ln->link;
- for (lnv = ppi->verts.list; lnv; lnv = lnv->next) {
- v = POINTER_AS_INT(lnv->link);
- if (!find_in_indexedintset(&verts_needed, v)) {
- v_index = add_int_to_indexedintset(bs, &verts_needed, v);
- add_to_intintmap(bs, &in_to_vmap, v_index, v);
- }
- }
- for (lne = ppi->edges.list; lne; lne = lne->next) {
- e = POINTER_AS_INT(lne->link);
- if (!find_in_indexedintset(&edges_needed, e)) {
- imeshplus_get_edge_verts(&imp, e, &v1, &v2);
- BLI_assert(v1 != -1 && v2 != -1);
- v_index = add_int_to_indexedintset(bs, &verts_needed, v1);
- add_to_intintmap(bs, &in_to_vmap, v_index, v1);
- v_index = add_int_to_indexedintset(bs, &verts_needed, v2);
- add_to_intintmap(bs, &in_to_vmap, v_index, v2);
- e_index = add_int_to_indexedintset(bs, &edges_needed, e);
- add_to_intintmap(bs, &in_to_emap, e_index, e);
- }
- }
- for (lnf = ppi->faces.list; lnf; lnf = lnf->next) {
- f = POINTER_AS_INT(lnf->link);
- if (!find_in_indexedintset(&faces_needed, f)) {
- face_len = imeshplus_facelen(&imp, f);
- nfaceverts += face_len;
- for (j = 0; j < face_len; j++) {
- v = imesh_face_vert(im, f, j);
- BLI_assert(v != -1);
- if (!find_in_indexedintset(&verts_needed, v)) {
- v_index = add_int_to_indexedintset(bs, &verts_needed, v);
- add_to_intintmap(bs, &in_to_vmap, v_index, v);
- }
- }
- f_index = add_int_to_indexedintset(bs, &faces_needed, f);
- add_to_intintmap(bs, &in_to_fmap, f_index, f);
- }
- }
- }
- /* Edges implicit in faces will come back as orig edges, so handle those. */
- tot_ne = edges_needed.size;
- for (i = 0; i < faces_needed.size; i++) {
- f = indexedintset_get_value_by_index(&faces_needed, i);
- imeshplus_get_face_no(&imp, f, fno);
- reverse_face = (dot_v3v3_db(part->plane, fno) < 0.0);
- face_len = imesh_facelen(im, f);
- for (j = 0; j < face_len; j++) {
- v1 = imesh_face_vert(im, f, reverse_face ? (face_len - j - 1) % face_len : j);
- v2 = imesh_face_vert(
- im, f, reverse_face ? (2 * face_len - j - 2) % face_len : (j + 1) % face_len);
- e = imesh_find_edge(im, v1, v2);
- BLI_assert(e != -1);
- add_to_intintmap(bs, &in_to_emap, j + tot_ne, e);
- }
- tot_ne += face_len;
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("self_intersect_part_and_ppis: cdt input maps\n\n");
- dump_intintmap(&in_to_vmap, "cdt v -> mesh v", " ");
- printf("\n");
- dump_intintmap(&in_to_emap, "cdt e -> mesh e", " ");
- printf("\n");
- dump_intintmap(&in_to_fmap, "cdt f -> mesh f", " ");
- printf("\n");
- }
-#endif
-
- in.verts_len = verts_needed.size;
- in.edges_len = edges_needed.size;
- in.faces_len = faces_needed.size;
- in.vert_coords = BLI_array_alloca(in.vert_coords, (size_t)in.verts_len);
- if (in.edges_len != 0) {
- in.edges = BLI_array_alloca(in.edges, (size_t)in.edges_len);
- }
- else {
- in.edges = NULL;
- }
- in.faces = BLI_array_alloca(in.faces, (size_t)nfaceverts);
- in.faces_start_table = BLI_array_alloca(in.faces_start_table, (size_t)in.faces_len);
- in.faces_len_table = BLI_array_alloca(in.faces_len_table, (size_t)in.faces_len);
- in.epsilon = (float)bs->eps;
- in.skip_input_modify = false;
-
- /* Fill in the vert_coords of CDT input */
-
- /* Find mat_2d: matrix to rotate so that plane normal moves to z axis */
- axis_dominant_v3_to_m3_db(mat_2d, part->plane);
- ok = invert_m3_m3_db(mat_2d_inv, mat_2d);
- BLI_assert(ok);
-
- for (i = 0; i < in.verts_len; i++) {
- v = indexedintset_get_value_by_index(&verts_needed, i);
- BLI_assert(v != -1);
- imeshplus_get_vert_co_db(&imp, v, p);
- mul_v3_m3v3_db(xyz, mat_2d, p);
- copy_v2fl_v2db(in.vert_coords[i], xyz);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("in vert %d (needed vert %d) was (%g,%g,%g), rotated (%g,%g,%g)\n",
- i,
- v,
- F3(p),
- F3(xyz));
- }
-#endif
- if (i == 0) {
- /* If part is truly coplanar, all z components of rotated v should be the same.
- * Save it so that can rotate back to correct place when done.
- */
- save_z = xyz[2];
- }
- }
-
- /* Fill in the face data of CDT input. */
- /* faces_index is next place in flattened faces table to put a vert index. */
- faces_index = 0;
- for (i = 0; i < in.faces_len; i++) {
- f = indexedintset_get_value_by_index(&faces_needed, i);
- face_len = imeshplus_facelen(&imp, f);
- in.faces_start_table[i] = faces_index;
- imeshplus_get_face_no(&imp, f, fno);
- reverse_face = (dot_v3v3_db(part->plane, fno) < 0.0);
- for (j = 0; j < face_len; j++) {
- v = imeshplus_face_vert(&imp, f, reverse_face ? face_len - j - 1 : j);
- BLI_assert(v != -1);
- v_index = indexedintset_get_index_for_value(&verts_needed, v);
- in.faces[faces_index++] = v_index;
- }
- in.faces_len_table[i] = faces_index - in.faces_start_table[i];
- }
-
- /* Fill in edge data of CDT input. */
- for (i = 0; i < in.edges_len; i++) {
- e = indexedintset_get_value_by_index(&edges_needed, i);
- imeshplus_get_edge_verts(&imp, e, &v1, &v2);
- BLI_assert(v1 != -1 && v2 != -1);
- in.edges[i][0] = indexedintset_get_index_for_value(&verts_needed, v1);
- in.edges[i][1] = indexedintset_get_index_for_value(&verts_needed, v2);
- }
-
- /* TODO: fill in loose vert data of CDT input. */
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\n");
- dump_cdt_input(&in, "");
- printf("\n");
- }
-#endif
-
- out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH);
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\n");
- dump_cdt_result(out, "", "");
- printf("\n");
- printf("self_intersect_part_and_ppis: make ppi result\n");
- }
-#endif
-
- /* Make the PartPartIntersect that represents the output of the CDT. */
- ppi_out = BLI_memarena_alloc(bs->mem_arena, sizeof(*ppi_out));
- init_partpartintersect(ppi_out);
-
- /* imp_v will map an output vert index to an IMesh + MeshAdd space vertex. */
- imp_v = BLI_array_alloca(imp_v, (size_t)out->verts_len);
- for (out_v = 0; out_v < out->verts_len; out_v++) {
- if (out->verts_orig_len_table[out_v] > 0) {
- int try_v;
- /* out_v maps to a vertex we fed in from verts_needed. */
- start = out->verts_orig_start_table[out_v];
- /* Choose orig that maps to lowest imesh vert, to make for a stable algorithm. */
- in_v = -1;
- v = INT_MAX;
- for (i = 0; i < out->verts_orig_len_table[out_v]; i++) {
- int try_in_v = out->verts_orig[start + i];
- if (!find_in_intintmap(&in_to_vmap, try_in_v, &try_v)) {
- printf("shouldn't happen, %d not in in_to_vmap\n", try_in_v);
- BLI_assert(false);
- }
- if (try_v < v) {
- v = try_v;
- in_v = try_in_v;
- }
- }
- BLI_assert(v != INT_MAX);
- /* If v is in IMesh then any other orig's that are in IMesh need to
- * go into the vert_merge_map. */
- if (v < imesh_totvert(im) && out->verts_orig_len_table[out_v] > 1) {
- for (i = 0; i < out->verts_orig_len_table[out_v]; i++) {
- j = out->verts_orig[start + i];
- if (j != in_v) {
- if (!find_in_intintmap(&in_to_vmap, j, &v1)) {
- printf("shouldn't happen, %d not in in_to_vmap\n", j);
- BLI_assert(false);
- }
- if (v1 < imesh_totvert(im)) {
- add_to_intintmap(bs, vert_merge_map, v1, v);
- meshdelete_add_vert(meshdelete, v1);
- }
- }
- }
- }
- }
- else {
- /* Need a new imp vertex for out_v. */
- copy_v2db_v2fl(q, out->vert_coords[out_v]);
- q[2] = save_z;
- mul_v3_m3v3_db(p, mat_2d_inv, q);
- /* p should not already be in the IMesh because such verts should have been added to the
- * input. However, it is possible that the vert might already be in meshadd. */
- v = meshadd_add_vert_db(bs, meshadd, p, -1, true);
- }
- imp_v[out_v] = v;
- add_vert_to_partpartintersect(bs, ppi_out, v);
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nimp_v, the map from output vert to imesh/meshadd vert\n");
- for (out_v = 0; out_v < out->verts_len; out_v++) {
- printf(" outv %d => imeshv %d\n", out_v, imp_v[out_v]);
- }
- printf("\n");
- }
-#endif
-
- /* Similar to above code, but for edges. */
- imp_e = BLI_array_alloca(imp_e, (size_t)out->edges_len);
- for (out_e = 0; out_e < out->edges_len; out_e++) {
- e_eg = -1;
- if (out->edges_orig_len_table[out_e] > 0) {
- start = out->edges_orig_start_table[out_e];
- in_e = min_int_in_array(&out->edges_orig[start], out->edges_orig_len_table[out_e]);
- if (!find_in_intintmap(&in_to_emap, in_e, &e_eg)) {
- printf("shouldn't happen, %d not in in_to_emap\n", in_e);
- BLI_assert(false);
- }
- /* If e_eg is in IMesh then need to record e_eg and any other edges
- * in the orig for out_e as deleted unless the output edge is
- * the same as the input one. We'll discover the "same as"
- * condition below, so delete here and add back there if so.
- */
- if (e_eg < imesh_totedge(im)) {
- for (i = 0; i < out->edges_orig_len_table[out_e]; i++) {
- j = out->edges_orig[start + i];
- if (!find_in_intintmap(&in_to_emap, j, &e)) {
- printf("shouldn't happen, %d not in in_to_emap\n", j);
- BLI_assert(false);
- }
- if (j < imesh_totedge(im)) {
- meshdelete_add_edge(meshdelete, e);
- }
- }
- }
- }
- /* If e_eg != -1 now, out_e may be only a part of e_eg; if so, make a new e but use e_eg as
- * example.
- */
- v1 = imp_v[out->edges[out_e][0]];
- v2 = imp_v[out->edges[out_e][1]];
- v1 = resolve_merge(v1, vert_merge_map);
- v2 = resolve_merge(v2, vert_merge_map);
- if (e_eg != -1) {
- int ev1, ev2;
- imeshplus_get_edge_verts(&imp, e_eg, &ev1, &ev2);
- if (!((v1 == ev1 && v2 == ev2) || (v1 == ev2 && v2 == ev1))) {
- if (e_eg >= imesh_totedge(im)) {
- e_eg = -1;
- }
- e = meshadd_add_edge(bs, meshadd, v1, v2, e_eg, true);
- }
- else {
- e = e_eg;
- if (e < imesh_totedge(im)) {
- /* Don't want to delete e after all. */
- meshdelete_remove_edge(meshdelete, e);
- }
- }
- }
- else {
- e = meshadd_add_edge(bs, meshadd, v1, v2, e_eg, true);
- }
- imp_e[out_e] = e;
- add_edge_to_partpartintersect(bs, ppi_out, e);
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nimp_e, the map from output edge to imesh/meshadd edge\n");
- for (out_e = 0; out_e < out->edges_len; out_e++) {
- printf(" oute %d => imeshe %d\n", out_e, imp_e[out_e]);
- }
- printf("\n");
- }
-#endif
-
- /* Now for the faces. */
- for (out_f = 0; out_f < out->faces_len; out_f++) {
- in_f = -1;
- f_eg = -1;
- f_other_egs = NULL;
- reverse_face = false;
- if (out->faces_orig_len_table[out_f] > 0) {
- start = out->faces_orig_start_table[out_f];
- eg_len = out->faces_orig_len_table[out_f];
- in_f = min_int_in_array(&out->faces_orig[start], eg_len);
- if (!find_in_intintmap(&in_to_fmap, in_f, &f_eg)) {
- printf("shouldn't happen, %d not in in_to_fmap\n", in_f);
- BLI_assert(false);
- }
- if (eg_len > 1) {
- /* Record the other examples too. They may be needed for boolean operations. */
- f_other_egs = BLI_memarena_alloc(arena, sizeof(*f_other_egs));
- init_intset(f_other_egs);
- for (i = start; i < start + eg_len; i++) {
- if (!find_in_intintmap(&in_to_fmap, out->faces_orig[i], &f_eg_o)) {
- printf("shouldn't happen, %d not in in_to_fmap\n", out->faces_orig[i]);
- }
- if (f_eg_o != f_eg) {
- add_to_intset(bs, f_other_egs, f_eg_o);
- }
- }
- }
- /* If f_eg is in IMesh then need to record f_eg and any other faces
- * in the orig for out_f as deleted. */
- if (f_eg < imesh_totface(im)) {
- for (i = 0; i < out->faces_orig_len_table[out_f]; i++) {
- j = out->faces_orig[start + i];
- if (!find_in_intintmap(&in_to_fmap, j, &f)) {
- printf("shouldn't happen, %d not in in_to_fmap\n", j);
- BLI_assert(false);
- }
- if (j < imesh_totface(im)) {
- meshdelete_add_face(meshdelete, f);
- }
- }
- imeshplus_get_face_no(&imp, f_eg, fno);
- reverse_face = (dot_v3v3_db(part->plane, fno) < 0.0);
- }
- }
- /* Even if f is same as an existing face, we make a new one, to simplify "what to delete"
- * bookkeeping later. */
- face_len = out->faces_len_table[out_f];
- start = out->faces_start_table[out_f];
- new_face_data = (IntPair *)BLI_memarena_alloc(arena,
- (size_t)face_len * sizeof(new_face_data[0]));
- for (i = 0; i < face_len; i++) {
- if (reverse_face) {
- out_v = out->faces[start + ((-i + face_len) % face_len)];
- out_v2 = out->faces[start + ((-i - 1 + face_len) % face_len)];
- }
- else {
- out_v = out->faces[start + i];
- out_v2 = out->faces[start + ((i + 1) % face_len)];
- }
- v = imp_v[out_v];
- v2 = imp_v[out_v2];
- new_face_data[i].first = v;
- /* Edge (v, v2) should be an edge already added to ppi_out. Also e is either in im or
- * meshadd. */
- e = find_edge_by_verts_in_meshadd(meshadd, v, v2);
- if (e == -1) {
- e = imesh_find_edge(&bs->im, v, v2);
- }
- if (e == -1) {
- printf("shouldn't happen: couldn't find e=(%d,%d)\n", v, v2);
- BLI_assert(false);
- }
- new_face_data[i].second = e;
- }
- f = meshadd_add_face(bs, meshadd, new_face_data, face_len, f_eg, f_other_egs);
- add_face_to_partpartintersect(bs, ppi_out, f);
- }
-
- BLI_delaunay_2d_cdt_free(out);
- return ppi_out;
-}
-
-/* Find geometry that in the coplanar parts which may intersect.
- * For now, just assume all can intersect.
- */
-static PartPartIntersect *coplanar_part_part_intersect(
- BoolState *bs, MeshPart *part_a, int a_index, MeshPart *part_b, int b_index)
-{
- MemArena *arena = bs->mem_arena;
- PartPartIntersect *ppi;
- int totv_a, tote_a, totf_a, totv_b, tote_b, totf_b;
- int i, v, e, f;
-
- ppi = BLI_memarena_alloc(arena, sizeof(*ppi));
- ppi->a_index = a_index;
- ppi->b_index = b_index;
- init_partpartintersect(ppi);
- totv_a = part_totvert(part_a);
- tote_a = part_totedge(part_a);
- totf_a = part_totface(part_a);
- totv_b = part_totvert(part_b);
- tote_b = part_totedge(part_b);
- totf_b = part_totface(part_b);
-
- for (i = 0; i < totv_a; i++) {
- v = part_vert(part_a, i);
- add_vert_to_partpartintersect(bs, ppi, v);
- }
- for (i = 0; i < totv_b; i++) {
- v = part_vert(part_b, i);
- add_vert_to_partpartintersect(bs, ppi, v);
- }
- for (i = 0; i < tote_a; i++) {
- e = part_edge(part_a, i);
- add_edge_to_partpartintersect(bs, ppi, e);
- }
- for (i = 0; i < tote_b; i++) {
- e = part_edge(part_b, i);
- add_edge_to_partpartintersect(bs, ppi, e);
- }
- for (i = 0; i < totf_a; i++) {
- f = part_face(part_a, i);
- add_face_to_partpartintersect(bs, ppi, f);
- }
- for (i = 0; i < totf_b; i++) {
- f = part_face(part_b, i);
- add_face_to_partpartintersect(bs, ppi, f);
- }
- return ppi;
-}
-
-typedef struct FaceEdgeInfo {
- double co[3]; /* coord of this face vertex */
- double isect[3]; /* intersection, if any, of this edge segment (starts at v) with line */
- double factor; /* co = line_co1 + factor * line_dir */
- int v; /* vertex index of this face coord */
- bool v_on; /* is co on the line (within epsilon)? */
- bool isect_ok; /* does this edge segment (excluding end vertex) intersect line? */
-} FaceEdgeInfo;
-
-typedef struct IntervalInfo {
- double fac[2];
- double co[2][3];
-} IntervalInfo;
-
-/* Find intersection of a face with a line and return the intervals on line.
- * It is known that the line is in the same plane as the face.
- * The other_plane argument is a plane (double[4]), and is the plane
- * that we intersected f's part's plane with to get the line.
- * If there is any intersection, it should be a series of points
- * and line segments on the line.
- * A point on the line can be represented as fractions of the distance from
- * line_co1 to line_co2. (The fractions can be negative or greater than 1.)
- * The intervals will be returned as a linked list of (start, end) fractions,
- * where single points will be represented by a pair (start, start).
- * The returned list will be in increasing order of the start fraction, and
- * the intervals will not overlap, though they may touch.
- * [TODO: handle case where face has several
- * edges on the line, or where faces fold back on themselves.]
- *
- * To avoid problems where different topological judgements are made by
- * different calls in the code, we want to make sure that we always do exactly
- * the same calculation whenver seeing whether an edge intersects the line
- * or a vertex is on the line. A common case is where an edge in f that the other_plane
- * intersects is also in another part, g, and we want the same intersection point when
- * intersecting that edge in the g / other_plane intersect case. The line itself can
- * be different, but the other_plane will be the same, so make sure to do the calculation
- * as an edge / plane intersection, and only use the line to figure out the factors.
- */
-static void find_face_line_intersects(BoolState *bs,
- LinkNodePair *intervals,
- int f,
- const double *other_plane,
- const double line_co1[3],
- const double line_co2[3])
-{
- IMesh *im = &bs->im;
- int flen, i, is;
- double eps = bs->eps;
- FaceEdgeInfo *finfo, *fi, *finext;
- double co_close[3], line_co1_to_co[3], line_dir[3];
- IntervalInfo *interval;
- double l_no[3], lambda;
- double line_dir_len;
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nFIND_FACE_LINE_INTERSECTS, face %d\n", f);
- printf("along line (%f,%f,%f)(%f,%f,%f)\n", F3(line_co1), F3(line_co2));
- printf("other_plane (%f,%f,%f,%f)\n", F4(other_plane));
- }
-#endif
- intervals[0].list = NULL;
- intervals[0].last_node = NULL;
- flen = imesh_facelen(im, f);
- finfo = BLI_array_alloca(finfo, (size_t)flen);
- sub_v3_v3v3_db(line_dir, line_co2, line_co1);
- line_dir_len = len_v3_db(line_dir);
- for (i = 0; i < flen; i++) {
- fi = &finfo[i];
- fi->v = imesh_face_vert(im, f, i);
- imesh_get_vert_co_db(im, fi->v, fi->co);
- closest_to_line_v3_db(co_close, fi->co, line_co1, line_co2);
- fi->v_on = compare_v3v3_db(fi->co, co_close, eps);
- fi->isect_ok = fi->v_on;
- if (fi->v_on) {
- copy_v3_v3_db(fi->isect, co_close);
- sub_v3_v3v3_db(line_co1_to_co, co_close, line_co1);
- fi->factor = len_v3_db(line_co1_to_co) / line_dir_len;
- if (dot_v3v3_db(line_co1_to_co, line_dir) < 0.0) {
- fi->factor = -fi->factor;
- }
- }
- else {
- zero_v3_db(fi->isect);
- fi->factor = 0.0f;
- }
- }
- sub_v3_v3v3_db(l_no, line_co2, line_co1);
- normalize_v3_d(l_no);
- for (i = 0; i < flen; i++) {
- fi = &finfo[i];
- if (fi->isect_ok) {
+#include "bmesh_boolean.h"
+
+/* Caller must call free_trimesh_input when done with the returned value. */
+static Boolean_trimesh_input *trimesh_input_from_bm(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*(test_fn))(BMFace *f, void *user_data),
+ void *user_data)
+{
+ int v, t, i;
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_FACE);
+ Boolean_trimesh_input *trimesh = MEM_mallocN(sizeof(*trimesh), __func__);
+ trimesh->vert_len = bm->totvert;
+ trimesh->tri_len = looptris_tot;
+ trimesh->vert_coord = MEM_malloc_arrayN(bm->totvert, sizeof(trimesh->vert_coord[0]), __func__);
+ trimesh->tri = MEM_malloc_arrayN(looptris_tot, sizeof(trimesh->tri[0]), __func__);
+ for (v = 0; v < bm->totvert; v++) {
+ BMVert *bmv = BM_vert_at_index(bm, v);
+ copy_v3_v3(trimesh->vert_coord[v], bmv->co);
+ }
+ for (t = 0; t < looptris_tot; t++) {
+ BMFace *f = looptris[t][0]->f;
+ if (test_fn(f, user_data) == -1) {
continue;
}
- finext = &finfo[(i + 1) % flen];
- /* For consistent calculations, order the ends of the segment consistently.
- * Also, use segment original coordinates, not any snapped version.
- */
- int v1, v2;
- double seg_co1[3], seg_co2[3];
- v1 = fi->v;
- v2 = finext->v;
- if (v1 > v2) {
- SWAP(int, v1, v2);
- }
- imesh_get_vert_co_db(im, v1, seg_co1);
- imesh_get_vert_co_db(im, v2, seg_co2);
- is = isect_seg_plane_normalized_epsilon_v3_db(
- seg_co1, seg_co2, other_plane, eps, fi->isect, &lambda);
- if (is > 0) {
- fi->isect_ok = true;
- fi->factor = line_interp_factor_v3_db(is == 1 ? fi->isect : fi->co, line_co1, line_co2);
- if (finext->v_on && is != 2) {
- /* Don't count intersections of only the end of the line segment. */
- fi->isect_ok = false;
- }
- }
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- for (i = 0; i < flen; i++) {
- fi = &finfo[i];
- printf("finfo[%d]: v=%d v_on=%d isect_ok=%d factor=%g isect=(%f,%f,%f)\n",
- i,
- fi->v,
- fi->v_on,
- fi->isect_ok,
- fi->factor,
- F3(fi->isect));
- }
- }
-#endif
- /* For now just handle case of convex faces, which should be one of the following
- * cases: (1) no intersects; (2) 1 intersect (a vertex); (2) 2 intersects on two
- * edges; (3) line coincindes with one edge.
- * TODO: handle general case. Needs ray shooting to test inside/outside
- * or division into convex pieces or something.
- */
- if (true) { /* TODO: replace this with "is face convex?" test */
- int startpos = -1;
- int endpos = -1;
- for (i = 0; i < flen; i++) {
- if (finfo[i].isect_ok) {
- startpos = i;
- break;
- }
- }
- if (startpos == -1) {
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("no intersections\n");
- }
-#endif
- return;
- }
- endpos = startpos;
- for (i = (startpos + 1) % flen; i != startpos; i = (i + 1) % flen) {
- if (finfo[i].isect_ok) {
- endpos = i;
- }
+ for (i = 0; i < 3; ++i) {
+ BMLoop *l = looptris[t][i];
+ trimesh->tri[t][i] = BM_elem_index_get(l->v);
}
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("startpos=%d, endpos=%d\n", startpos, endpos);
- }
-#endif
- interval = BLI_memarena_alloc(bs->mem_arena, sizeof(IntervalInfo));
- if (finfo[startpos].factor <= finfo[endpos].factor) {
- interval->fac[0] = finfo[startpos].factor;
- interval->fac[1] = finfo[endpos].factor;
- copy_v3_v3_db(interval->co[0], finfo[startpos].isect);
- copy_v3_v3_db(interval->co[1], finfo[endpos].isect);
- }
- else {
- interval->fac[0] = finfo[endpos].factor;
- interval->fac[1] = finfo[startpos].factor;
- copy_v3_v3_db(interval->co[0], finfo[endpos].isect);
- copy_v3_v3_db(interval->co[1], finfo[startpos].isect);
- }
- if (interval->fac[1] - interval->fac[0] <= eps) {
- interval->fac[1] = interval->fac[0];
- copy_v3_v3_db(interval->co[1], interval->co[0]);
- }
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("interval factors = (%f,%f), coords = (%f,%f,%f)(%f,%f,%f)\n",
- interval->fac[0],
- interval->fac[1],
- F3(interval->co[0]),
- F3(interval->co[1]));
}
-#endif
- BLI_linklist_append_arena(intervals, interval, bs->mem_arena);
+ return trimesh;
}
-/* Find geometry that in the non-coplanar parts which may intersect.
- * Needs to be the part of the geometry that is on the common line
- * of intersection, so that it is in the plane of both parts.
- */
-static PartPartIntersect *non_coplanar_part_part_intersect(BoolState *bs,
- MeshPart *part_a,
- int a_index,
- MeshPart *part_b,
- int b_index,
- MeshChange *change)
+static void free_trimesh_input(Boolean_trimesh_input *in)
{
- MemArena *arena = bs->mem_arena;
- IMesh *im = &bs->im;
- PartPartIntersect *ppi;
- MeshPart *part;
- int totv[2], tote[2], totf[2];
- int i, v, e, f, pi, v1, v2;
- int index_a, index_b;
- LinkNodePair *intervals_a;
- LinkNodePair *intervals_b;
- LinkNodePair *intervals;
- LinkNode *lna, *lnb;
- IntervalInfo *iinfoa, *iinfob;
- int is;
- double co[3], co1[3], co2[3], co_close[3], line_co1[3], line_co2[3], line_dir[3];
- double co_close1[3], co_close2[3];
- double *other_plane;
- double elen_squared;
- double faca1, faca2, facb1, facb2, facstart, facend;
- double eps = bs->eps;
- double eps_squared = eps * eps;
- bool on1, on2;
- MeshAdd *meshadd = &change->add;
- IntSet *intersection_edges = &change->intersection_edges;
-#ifdef BOOLDEBUG
- LinkNode *ln;
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nNON_COPLANAR_PART_PART_INTERSECT a%d b%d\n\n", a_index, b_index);
- }
-#endif
- if (!isect_plane_plane_v3_db(part_a->plane, part_b->plane, line_co1, line_dir)) {
- /* Presumably the planes are parallel if they are not coplanar and don't intersect. */
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("planes don't intersect\n");
- }
-#endif
- return NULL;
- }
- add_v3_v3v3_db(line_co2, line_co1, line_dir);
-
- ppi = BLI_memarena_alloc(arena, sizeof(*ppi));
- init_partpartintersect(ppi);
- ppi->a_index = a_index;
- ppi->b_index = b_index;
-
- /* Handle loose vertices of parts. */
- for (pi = 0; pi < 2; pi++) {
- part = pi == 0 ? part_a : part_b;
- totv[pi] = part_totvert(part);
- for (i = 0; i < totv[pi]; i++) {
- v = part_vert(part, i);
- imesh_get_vert_co_db(im, v, co);
- closest_to_line_v3_db(co_close, co, line_co1, line_co2);
- if (compare_v3v3_db(co, co_close, eps)) {
- add_vert_to_partpartintersect(bs, ppi, v);
- }
- }
- }
-
- /* Handle loose edges of parts */
- for (pi = 0; pi < 2; pi++) {
- part = pi == 0 ? part_a : part_b;
- tote[pi] = part_totedge(part);
- for (i = 0; i < tote[pi]; i++) {
- e = part_edge(part, i);
- imesh_get_edge_cos_db(im, e, co1, co2);
- /* First check if co1 and/or co2 are on line, within eps. */
- closest_to_line_v3_db(co_close1, co1, line_co1, line_co2);
- closest_to_line_v3_db(co_close2, co2, line_co1, line_co2);
- on1 = compare_v3v3_db(co1, co_close1, eps);
- on2 = compare_v3v3_db(co2, co_close2, eps);
- if (on1 || on2) {
- if (on1 && on2) {
- add_edge_to_partpartintersect(bs, ppi, e);
- }
- else {
- imesh_get_edge_verts(im, e, &v1, &v2);
- add_vert_to_partpartintersect(bs, ppi, on1 ? v1 : v2);
- }
- }
- else {
- is = isect_line_line_epsilon_v3_db(
- line_co1, line_co2, co1, co2, co_close1, co_close2, eps);
- if (is > 0) {
- /* co_close1 is closest on line to segment (co1,co2). */
- if (is == 1 || compare_v3v3_db(co_close1, co_close2, eps)) {
- /* Intersection is on line or within eps. Is it on e's segment? */
- elen_squared = len_squared_v3v3_db(co1, co2) + eps_squared;
- if (len_squared_v3v3_db(co_close1, co1) <= elen_squared &&
- len_squared_v3v3_db(co_close1, co2) <= elen_squared) {
- /* Maybe intersection point is some other point in mesh. */
- v = imesh_find_co_db(im, co_close1, eps);
- if (v == -1) {
- /* A new point. Need to add to meshadd. */
- v = meshadd_add_vert_db(bs, meshadd, co, -1, true);
- }
- add_vert_to_partpartintersect(bs, ppi, v);
- }
- }
- }
- }
- }
- }
-
- /* Handle faces of parts. */
- totf[0] = part_totface(part_a);
- totf[1] = part_totface(part_b);
- intervals_a = BLI_array_alloca(intervals_a, (size_t)totf[0]);
- intervals_b = BLI_array_alloca(intervals_b, (size_t)totf[1]);
- for (pi = 0; pi < 2; pi++) {
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("non_coplanar_part_part_intersect: doing faces from part %s\n", pi == 0 ? "a" : "b");
- }
-#endif
- part = pi == 0 ? part_a : part_b;
- totf[pi] = part_totface(part);
- for (i = 0; i < totf[pi]; i++) {
- f = part_face(part, i);
- intervals = pi == 0 ? intervals_a : intervals_b;
- other_plane = pi == 0 ? part_b->plane : part_a->plane;
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- if (pi == 0) {
- printf("doing %dth face of part a%d, f%d\nother_plane=(%f,%f,%f,%f) from b%d\n",
- i,
- a_index,
- f,
- F4(other_plane),
- b_index);
- }
- else {
- printf("doing %dth face of part b%d, f%d\nother_plane=(%f,%f,%f,%f) from a%d\n",
- i,
- b_index,
- f,
- F4(other_plane),
- a_index);
- }
- }
-#endif
- find_face_line_intersects(bs, &intervals[i], f, other_plane, line_co1, line_co2);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- if (intervals[i].list == NULL) {
- printf("no intersections\n");
- }
- else {
- for (ln = intervals[i].list; ln; ln = ln->next) {
- iinfoa = (IntervalInfo *)ln->link;
- printf(" (%f,%f) -> (%f,%f,%f)(%f,%f,%f)\n",
- iinfoa->fac[0],
- iinfoa->fac[1],
- F3(iinfoa->co[0]),
- F3(iinfoa->co[1]));
- }
- }
- }
-#endif
- }
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("non_coplanar_part_part_intersect: intersecting face pair intervals\n");
- }
-#endif
- /* Need to intersect the intervals of each face pair's intervals. */
- for (index_a = 0; index_a < totf[0]; index_a++) {
- lna = intervals_a[index_a].list;
- if (lna == NULL) {
- continue;
- }
- for (index_b = 0; index_b < totf[1]; index_b++) {
- lnb = intervals_b[index_b].list;
- if (lnb == NULL) {
- continue;
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("intersect intervals for faces %d and %d\n",
- part_face(part_a, index_a),
- part_face(part_b, index_b));
- }
-#endif
- if (BLI_linklist_count(lna) == 1 && BLI_linklist_count(lnb) == 1) {
- /* Common special case of two single intervals to intersect. */
- iinfoa = (IntervalInfo *)lna->link;
- iinfob = (IntervalInfo *)lnb->link;
- faca1 = iinfoa->fac[0];
- faca2 = iinfoa->fac[1];
- facb1 = iinfob->fac[0];
- facb2 = iinfob->fac[1];
- facstart = max_dd(faca1, facb1);
- facend = min_dd(faca2, facb2);
- if (facend < facstart - eps) {
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf(" no intersection\n");
- }
-#endif
- }
- else {
- if (facstart == faca1) {
- copy_v3_v3_db(co, iinfoa->co[0]);
- }
- else {
- copy_v3_v3_db(co, iinfob->co[0]);
- }
- if (facend == faca2) {
- copy_v3_v3_db(co2, iinfoa->co[1]);
- }
- else {
- copy_v3_v3_db(co2, iinfob->co[1]);
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf(
- " interval result: factors (%f,%f) = coords (%.5f,%.5f,%.5f)(%.5f,%.5f,%.5f)\n",
- facstart,
- facend,
- F3(co),
- F3(co2));
- }
-#endif
- if (compare_v3v3_db(co, co2, eps)) {
- /* Add a single vertex. */
- v = imesh_find_co_db(im, co, eps);
- if (v == -1) {
- /* A new point. Need to add to meshadd. */
- v = meshadd_add_vert_db(bs, meshadd, co, -1, true);
- }
- add_vert_to_partpartintersect(bs, ppi, v);
- }
- else {
- /* Add an edge. */
- v1 = imesh_find_co_db(im, co, eps);
- if (v1 == -1) {
- /* A new point. Need to add to meshadd. */
- v1 = meshadd_add_vert_db(bs, meshadd, co, -1, true);
- }
- v2 = imesh_find_co_db(im, co2, eps);
- if (v2 == -1) {
- v2 = meshadd_add_vert_db(bs, meshadd, co2, -1, true);
- }
- if (v1 == v2) {
- /* Even though coords are far enough apart with double
- * test, maybe they are close enough with float test.
- * Just add a single vert if this happens.
- */
- add_vert_to_partpartintersect(bs, ppi, v1);
- }
- else {
- e = imesh_find_edge(im, v1, v2);
- if (e == -1) {
- /* TODO: if overlaps an existing edge, use as example. */
- e = meshadd_add_edge(bs, meshadd, v1, v2, -1, true);
- }
- add_edge_to_partpartintersect(bs, ppi, e);
- add_to_intset(bs, intersection_edges, e);
- }
- }
- }
- }
- else {
- printf("implement the multi-interval intersect case\n");
- }
- }
- }
- return ppi;
-}
-
-static PartPartIntersect *part_part_intersect(BoolState *bs,
- MeshPart *part_a,
- int a_index,
- MeshPart *part_b,
- int b_index,
- MeshChange *change)
-{
- PartPartIntersect *ans;
- if (!parts_may_intersect(part_a, part_b)) {
- ans = NULL;
- }
- else if (planes_are_coplanar(part_a->plane, part_b->plane, bs->eps)) {
- ans = coplanar_part_part_intersect(bs, part_a, a_index, part_b, b_index);
- }
- else {
- ans = non_coplanar_part_part_intersect(bs, part_a, a_index, part_b, b_index, change);
- }
- return ans;
-}
-
-/* Lexicographic sort of two BVHTreeOverlap. */
-static int bool_overlap_sort_fn(const void *v_1, const void *v_2)
-{
- const BVHTreeOverlap *o1 = v_1;
- const BVHTreeOverlap *o2 = v_2;
-
- if (o1->indexA < o2->indexA) {
- return -1;
- }
- else if (o1->indexA > o2->indexA) {
- return 1;
- }
- else {
- if (o1->indexB < o2->indexB) {
- return -1;
- }
- else if (o1->indexB > o2->indexB) {
- return 1;
- }
- else {
- return 0;
- }
- }
+ MEM_freeN(in->vert_coord);
+ MEM_freeN(in->tri);
+ MEM_freeN(in);
}
-/* Intersect all parts of a_partset with all parts of b_partset.
- */
-static void intersect_partset_pair(BoolState *bs,
- MeshPartSet *a_partset,
- MeshPartSet *b_partset,
- MeshChange *meshchange)
-{
- int a_index, b_index, tot_part_a, tot_part_b;
- uint overlap_index;
- MeshPart *part_a, *part_b;
- MemArena *arena = bs->mem_arena;
- bool same_partsets = (a_partset == b_partset);
- LinkNodePair *a_isects; /* Array of List of PairPartIntersect. */
- LinkNodePair *b_isects; /* Array of List of PairPartIntersect. */
- PartPartIntersect *isect;
- BLI_bitmap *bpart_coplanar_with_apart;
- BVHTree *tree_a, *tree_b;
- uint tree_overlap_tot;
- BVHTreeOverlap *overlap;
- float feps_margin = 20.0f * ((float)bs->eps);
- float bbpts[6];
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-#endif
-
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf("\nINTERSECT_PARTSET_PAIR\n\n");
- if (dbg_level > 0) {
- dump_partset(a_partset);
- dump_partset(b_partset);
- }
- }
-#endif
- tot_part_a = a_partset->tot_part;
- tot_part_b = b_partset->tot_part;
- a_isects = BLI_memarena_calloc(arena, (size_t)tot_part_a * sizeof(a_isects[0]));
- b_isects = BLI_memarena_calloc(arena, (size_t)tot_part_b * sizeof(b_isects[0]));
- bpart_coplanar_with_apart = BLI_BITMAP_NEW_MEMARENA(arena, tot_part_b);
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf(
- "\nIntersect_partset_pair: do all part - part preliminary intersections (using bvh)\n\n");
- }
-#endif
- /* Tree type is 8 => octtree; axis = 6 => using XYZ axes only. */
- tree_a = BLI_bvhtree_new(tot_part_a, feps_margin, 8, 6);
- for (a_index = 0; a_index < tot_part_a; a_index++) {
- part_a = partset_part(a_partset, a_index);
- copy_v3fl_v3db(bbpts, part_a->bbmin);
- copy_v3fl_v3db(bbpts + 3, part_a->bbmax);
- BLI_bvhtree_insert(tree_a, a_index, bbpts, 2);
- }
- BLI_bvhtree_balance(tree_a);
- if (!same_partsets) {
- tree_b = BLI_bvhtree_new(tot_part_b, feps_margin, 8, 6);
- for (b_index = 0; b_index < tot_part_b; b_index++) {
- part_b = partset_part(b_partset, b_index);
- copy_v3fl_v3db(bbpts, part_b->bbmin);
- copy_v3fl_v3db(bbpts + 3, part_b->bbmax);
- BLI_bvhtree_insert(tree_b, b_index, bbpts, 2);
- }
- BLI_bvhtree_balance(tree_b);
- }
- else {
- tree_b = tree_a;
- }
-
- overlap = BLI_bvhtree_overlap(tree_a, tree_b, &tree_overlap_tot, NULL, NULL);
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("process %u overlaps\n\n", tree_overlap_tot);
- }
-#endif
-
- if (overlap) {
- /* For stable results in the face of, especially, multithreaded bvhtree overlap, sort the
- * overlaps. */
- qsort(overlap, tree_overlap_tot, sizeof(overlap[0]), bool_overlap_sort_fn);
- for (overlap_index = 0; overlap_index < tree_overlap_tot; overlap_index++) {
- a_index = overlap[overlap_index].indexA;
- b_index = overlap[overlap_index].indexB;
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf("overlap: a%d and b%d\n", a_index, b_index);
- }
-#endif
- part_a = partset_part(a_partset, a_index);
- part_b = partset_part(b_partset, b_index);
- if (same_partsets) {
- if (b_index <= a_index) {
- continue;
- }
- }
- else {
- if (planes_are_coplanar(part_a->plane, part_b->plane, bs->eps)) {
- BLI_BITMAP_ENABLE(bpart_coplanar_with_apart, b_index);
- }
- }
- isect = part_part_intersect(bs, part_a, a_index, part_b, b_index, meshchange);
- if (isect != NULL) {
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("Part a%d intersects part b%d\n", a_index, b_index);
- dump_partpartintersect(isect, "");
- printf("\n");
- dump_meshchange(meshchange, "incremental");
- }
-#endif
- BLI_linklist_append_arena(&a_isects[a_index], isect, arena);
- BLI_linklist_append_arena(&b_isects[b_index], isect, arena);
- if (same_partsets) {
- BLI_linklist_append_arena(&a_isects[b_index], isect, arena);
- BLI_linklist_append_arena(&b_isects[a_index], isect, arena);
- }
- }
- }
- MEM_freeN(overlap);
- }
- BLI_bvhtree_free(tree_a);
- if (tree_b != tree_a) {
- BLI_bvhtree_free(tree_b);
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nintersect_partset_pair: do self intersections\n\n");
- }
-#endif
- /* Now self-intersect the parts with their lists of isects. */
- for (a_index = 0; a_index < tot_part_a; a_index++) {
- part_a = partset_part(a_partset, a_index);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nSELF INTERSECT part a%d with its ppis\n", a_index);
- }
-#endif
- isect = self_intersect_part_and_ppis(bs, part_a, &a_isects[a_index], meshchange);
-#ifdef BOOLDEBUG
- if (isect && dbg_level > 0) {
- dump_partpartintersect(isect, "after self intersect");
- dump_meshchange(meshchange, "after self intersect");
- }
-#endif
- }
- if (!same_partsets) {
- for (b_index = 0; b_index < tot_part_b; b_index++) {
- part_b = partset_part(b_partset, b_index);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\nSELF INTERSECT part b%d with its ppis\n", b_index);
- }
-#endif
- if (BLI_BITMAP_TEST_BOOL(bpart_coplanar_with_apart, b_index)) {
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("skipping self_intersect because coplanar with some a part\n");
- }
-#endif
- continue;
- }
- isect = self_intersect_part_and_ppis(bs, part_b, &b_isects[b_index], meshchange);
-#ifdef BOOLDEBUG
- if (isect && dbg_level > 0) {
- dump_partpartintersect(isect, "after self intersect b");
- dump_meshchange(meshchange, "after self intersect b");
- }
-#endif
- }
- }
-}
-
-static void do_boolean_op(BoolState *bm, const int boolean_mode);
-
-/**
- * Intersect faces
- * leaving the resulting edges tagged.
- *
- * \param test_fn: Return value: -1: skip, 0: tree_a, 1: tree_b (use_self == false)
- * \param boolean_mode: -1: no-boolean, 0: intersection... see #BMESH_ISECT_BOOLEAN_ISECT.
- * \return true if the mesh is changed (intersections cut or faces removed from boolean).
- */
bool BM_mesh_boolean(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
int (*test_fn)(BMFace *f, void *user_data),
void *user_data,
const bool use_self,
- const bool UNUSED(use_separate),
- const int boolean_mode,
- const float eps)
-{
- BoolState bs = {NULL};
- MeshPartSet all_parts, a_parts, b_parts;
- MeshChange meshchange;
- IntSet both_side_faces;
- BMFace *bmf;
- int f, test_val;
-#ifdef BOOLDEBUG
- bool side_a_ok, side_b_ok;
- int dbg_level = 0;
-#endif
-
-#ifdef PERFDEBUG
- perfdata_init();
-#endif
-
- init_imesh_from_bmesh(&bs.im, bm);
- bs.eps = eps;
- bs.mem_arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), __func__);
- bs.face_side = BLI_memarena_calloc(bs.mem_arena, (size_t)bm->totface * sizeof(uchar));
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("\n\nBOOLEAN, use_self=%d, boolean_mode=%d, eps=%g\n", use_self, boolean_mode, eps);
- }
- if (dbg_level > 1) {
- side_a_ok = analyze_bmesh_for_boolean(bm, false, SIDE_A, bs.face_side);
- side_b_ok = analyze_bmesh_for_boolean(bm, false, SIDE_B, bs.face_side);
- }
-#endif
-
- for (f = 0; f < bm->totface; f++) {
- if (use_self) {
- bs.face_side[f] = SIDE_A | SIDE_B;
- }
- else {
- bmf = BM_face_at_index(bm, f);
- test_val = test_fn(bmf, user_data);
- if (test_val != -1) {
- bs.face_side[f] = (test_val == 0) ? SIDE_A : SIDE_B;
- }
- }
- }
-
- init_meshchange(&bs, &meshchange);
- init_intset(&both_side_faces);
-
- if (use_self) {
- find_coplanar_parts(&bs, &all_parts, SIDE_A | SIDE_B, "all");
- intersect_partset_pair(&bs, &all_parts, &all_parts, &meshchange);
- }
- else {
- find_coplanar_parts(&bs, &a_parts, SIDE_A, "A");
- find_coplanar_parts(&bs, &b_parts, SIDE_B, "B");
- intersect_partset_pair(&bs, &a_parts, &b_parts, &meshchange);
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- dump_meshchange(&meshchange, "change for intersection");
- dump_intset(&both_side_faces, "both side faces", "");
- }
-#endif
-
- apply_meshchange_to_imesh(&bs, &bs.im, &meshchange);
-
- if (boolean_mode != -1) {
- do_boolean_op(&bs, boolean_mode);
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- if (!use_self && side_a_ok && side_b_ok) {
- bool ok;
- ok = analyze_bmesh_for_boolean(bm, false, 0, NULL);
- BLI_assert(ok);
- }
- }
-#endif
-
- imesh_free_aux_data(&bs.im);
- meshchange_free_aux_data(&meshchange);
- BLI_memarena_free(bs.mem_arena);
-#ifdef PERFDEBUG
- dump_perfdata();
-#endif
- return true;
-}
-
-/** Boolean functions. */
-
-/* Return the Generalized Winding Number of point co wih respect to the
- * volume implied by the faces for which bs->test_fn returns the value side.
- * See "Robust Inside-Outside Segmentation using Generalized Winding Numbers"
- * by Jacobson, Kavan, and Sorkine-Hornung.
- * This is like a winding number in that if it is positive, the point
- * is inside the volume. But it is tolerant of not-completely-watertight
- * volumes, still doing a passable job of classifying inside/outside
- * as we intuitively understand that to mean.
- * TOOD: speed up this calculation using the heirarchical algorithm in
- * that paper.
- */
-static double generalized_winding_number(BoolState *bs, int side, const double co[3])
-{
- double gwn, fgwn;
- int i, v1, v2, v3, f, totf, flen, tottri, fside;
- IMesh *im = &bs->im;
- int(*index)[3];
- int index_buffer_len;
- bool negate;
- double p1[3], p2[3], p3[3], a[3], b[3], c[3], bxc[3];
- double alen, blen, clen, num, denom, x;
-#ifdef BOOLDEBUG
- int dbg_level = 0;
-
- if (dbg_level > 0) {
- printf("generalized_winding_number, side=%d, co=(%f,%f,%f)\n", side, F3(co));
- }
-#endif
-
- /* Use the same buffer for all tesselations. Will increase size if necessary. */
-#ifdef BOOLDEBUG
- index_buffer_len = 3;
-#else
- index_buffer_len = 64;
-#endif
- index = MEM_mallocN((size_t)index_buffer_len * sizeof(index[0]), __func__);
- totf = imesh_totface(im);
- gwn = 0.0;
- for (f = 0; f < totf; f++) {
- fside = bs->face_side[f];
- if (!(fside & side)) {
- continue;
- }
- negate = (fside | BOTH_SIDES_OPP_NORMALS);
- flen = imesh_facelen(im, f);
- tottri = flen - 2;
- if (tottri > index_buffer_len) {
- index_buffer_len = tottri * 2;
- MEM_freeN(index);
- index = MEM_mallocN((size_t)index_buffer_len * sizeof(index[0]), __func__);
- }
- imesh_face_calc_tesselation(im, f, index);
- for (i = 0; i < tottri; i++) {
- v1 = imesh_face_vert(im, f, index[i][0]);
- imesh_get_vert_co_db(im, v1, p1);
- v2 = imesh_face_vert(im, f, index[i][1]);
- imesh_get_vert_co_db(im, v2, p2);
- v3 = imesh_face_vert(im, f, index[i][2]);
- imesh_get_vert_co_db(im, v3, p3);
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf("face f%d tess tri %d is V=(%d,%d,%d)\n", f, i, v1, v2, v3);
- }
-#endif
- sub_v3_v3v3_db(a, p1, co);
- sub_v3_v3v3_db(b, p2, co);
- sub_v3_v3v3_db(c, p3, co);
-
- /* Calculate the solid angle of abc relative to origin.
- * Using Oosterom and Strackee formula. */
- alen = len_v3_db(a);
- blen = len_v3_db(b);
- clen = len_v3_db(c);
- cross_v3_v3v3_db(bxc, b, c);
- num = dot_v3v3_db(a, bxc);
- denom = alen * blen * clen + dot_v3v3_db(a, b) * clen + dot_v3v3_db(a, c) * blen +
- dot_v3v3_db(b, c) * alen;
- if (denom == 0.0) {
- denom = 10e-10;
- }
- x = atan2(num, denom);
- fgwn = 2 * x;
- if (negate) {
- fgwn = -fgwn;
- }
-#ifdef BOOLDEBUG
- if (dbg_level > 1) {
- printf("face f%d tess tri %d contributes %f (negated=%d\n", f, i, fgwn, negate);
- }
-#endif
- gwn += fgwn;
- }
- }
- gwn = gwn / (M_PI * 4.0);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("gwn=%f\n\n", gwn);
- }
-#endif
-
- MEM_freeN(index);
- return gwn;
-}
-
-/* Return true if point co is inside the volume implied by the
- * faces for which bs->test_fn returns the value side.
- */
-static bool point_is_inside_side(BoolState *bs, int side, const double co[3])
-{
- double gwn;
-
- gwn = generalized_winding_number(bs, side, co);
- return (fabs(gwn) >= 0.5);
-}
-
-static void do_boolean_op(BoolState *bs, const int boolean_mode)
-{
- int *groups_array;
- int(*group_index)[2];
- int group_tot, totface;
- double co[3];
- int i, f, fg, fg_end, fside, otherside;
- bool do_remove, do_flip, inside, both_sides, opp_normals;
- MeshChange meshchange;
-#ifdef BOOLDEBUG
- bool dbg_level = 0;
-
- if (dbg_level > 0) {
- printf("\nDO_BOOLEAN_OP, boolean_mode=%d\n\n", boolean_mode);
- }
-#endif
-
- init_meshchange(bs, &meshchange);
- meshchange.use_face_kill_loose = true;
-
- /* Partition faces into groups, where a group is a maximal set
- * of edge-connected faces on the same side (A vs B) of the boolean operand.
- */
- totface = imesh_totface(&bs->im);
- groups_array = BLI_memarena_alloc(bs->mem_arena, sizeof(*groups_array) * (size_t)totface);
- group_tot = imesh_calc_face_groups(bs, groups_array, &group_index);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("Groups\n");
- for (i = 0; i < group_tot; i++) {
- printf("group %d:\n ", i);
- fg = group_index[i][0];
- fg_end = fg + group_index[i][1];
- for (; fg != fg_end; fg++) {
- printf("%d ", groups_array[fg]);
- }
- printf("\n");
- }
- }
-#endif
-
- /* For each group, determine if it is inside or outside the part
- * on the other side, and remove and/or flip the normals of the
- * faces in the group according to the result and the boolean operation. */
- for (i = 0; i < group_tot; i++) {
- fg = group_index[i][0];
- fg_end = fg + group_index[i][1];
-
- /* Test if first face of group is inside. */
- f = groups_array[fg];
- fside = bs->face_side[f];
- both_sides = fside & (SIDE_A & SIDE_B);
- opp_normals = fside & BOTH_SIDES_OPP_NORMALS;
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("group %d fside = %d, both_sides = %d, opp_normals = %d\n",
- i,
- fside,
- both_sides,
- opp_normals);
- }
-#endif
-
- if (fside == 0) {
- continue;
- }
- otherside = fside ^ (SIDE_A | SIDE_B);
-
- if (both_sides) {
- do_remove = boolean_mode == BMESH_BOOLEAN_UNION && opp_normals;
- do_flip = boolean_mode == BMESH_BOOLEAN_DIFFERENCE && opp_normals && (fside | SIDE_A);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("both_sides case, do_remove=%d, do_flip=%d\n", do_remove, do_flip);
- }
-#endif
- }
- else {
- imesh_calc_point_in_face(&bs->im, f, co);
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("face %d test co=(%f,%f,%f)\n", f, F3(co));
- }
-#endif
-
- inside = point_is_inside_side(bs, otherside, co);
-
- switch (boolean_mode) {
- case BMESH_BOOLEAN_ISECT:
- do_remove = !inside;
- do_flip = false;
- break;
- case BMESH_BOOLEAN_UNION:
- do_remove = inside;
- do_flip = false;
- break;
- case BMESH_BOOLEAN_DIFFERENCE:
- do_remove = (fside & SIDE_A) ? inside : !inside;
- do_flip = (fside & SIDE_B);
- break;
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- printf("result for group %d: inside=%d, remove=%d, flip=%d\n\n",
- i,
- inside,
- do_remove,
- do_flip);
- }
-#endif
- }
-
- if (do_remove || do_flip) {
- for (; fg != fg_end; fg++) {
- f = groups_array[fg];
- if (do_remove) {
- meshdelete_add_face(&meshchange.delete, f);
- }
- else if (do_flip) {
- add_to_intset(bs, &meshchange.face_flip, f);
- }
- }
- }
- }
-
-#ifdef BOOLDEBUG
- if (dbg_level > 0) {
- dump_meshchange(&meshchange, "after boolean op");
- }
-#endif
-
- apply_meshchange_to_imesh(bs, &bs->im, &meshchange);
- meshchange_free_aux_data(&meshchange);
- MEM_freeN(group_index);
-}
-
-#ifdef BOOLDEBUG
-
-ATTU static void dump_part(const MeshPart *part, const char *label)
-{
- LinkNode *ln;
- int i;
- struct namelist {
- const char *name;
- LinkNode *list;
- } nl[3] = {{"verts", part->verts}, {"edges", part->edges}, {"faces", part->faces}};
-
- printf("part %s\n", label);
- for (i = 0; i < 3; i++) {
- if (nl[i].list) {
- printf(" %s:{", nl[i].name);
- for (ln = nl[i].list; ln; ln = ln->next) {
- printf("%d", POINTER_AS_INT(ln->link));
- if (ln->next) {
- printf(", ");
- }
- }
- printf("}\n");
- }
- }
- printf(" plane=(%.3f,%.3f,%.3f),%.3f:\n", F4(part->plane));
- printf(" bb=(%.3f,%.3f,%.3f)(%.3f,%.3f,%.3f)\n", F3(part->bbmin), F3(part->bbmax));
-}
-
-ATTU static void dump_partset(const MeshPartSet *partset)
-{
- int i;
- MeshPart *part;
- char partlab[20];
-
- printf("partset %s\n", partset->label);
- for (i = 0; i < partset->tot_part; i++) {
- part = partset_part(partset, i);
- sprintf(partlab, "%d", i);
- if (!part) {
- printf("<NULL PART>\n");
- }
- else {
- dump_part(part, partlab);
- }
- }
- printf(
- "partset bb=(%.3f,%.3f,%.3f)(%.3f,%.3f,%.3f)\n\n", F3(partset->bbmin), F3(partset->bbmax));
-}
-
-ATTU static void dump_partpartintersect(const PartPartIntersect *ppi, const char *label)
-{
- struct namelist {
- const char *name;
- LinkNode *list;
- } nl[3] = {{"verts", ppi->verts.list}, {"edges", ppi->edges.list}, {"faces", ppi->faces.list}};
- LinkNode *ln;
- int i;
-
- printf("partpartintersect %s parts a[%d] and b[%d]\n", label, ppi->a_index, ppi->b_index);
-
- for (i = 0; i < 3; i++) {
- if (nl[i].list) {
- printf(" %s:{", nl[i].name);
- for (ln = nl[i].list; ln; ln = ln->next) {
- printf("%d", POINTER_AS_INT(ln->link));
- if (ln->next) {
- printf(", ");
- }
- }
- printf("}\n");
- }
- }
-}
-
-ATTU static void dump_meshadd(const MeshAdd *ma, const char *label)
-{
- NewVert *nv;
- NewEdge *ne;
- NewFace *nf;
- int i, j;
-
- printf("meshadd %s\n", label);
- if (ma->totvert > 0) {
- printf("verts:\n");
- for (i = 0; i < ma->totvert; i++) {
- nv = ma->verts[i];
- printf(" %d: (%f,%f,%f) %d\n", i + ma->vindex_start, F3(nv->co), nv->example);
- }
- }
- if (ma->totedge > 0) {
- printf("edges:\n");
- for (i = 0; i < ma->totedge; i++) {
- ne = ma->edges[i];
- printf(" %d: (%d,%d) %d\n", i + ma->eindex_start, ne->v1, ne->v2, ne->example);
- }
- }
- if (ma->totface > 0) {
- printf("faces:\n");
- for (i = 0; i < ma->totface; i++) {
- nf = ma->faces[i];
- printf(" %d: face of length %d, example %d\n", i + ma->findex_start, nf->len, nf->example);
- if (nf->other_examples) {
- dump_intset(nf->other_examples, "other examples", " ");
- }
- for (j = 0; j < nf->len; j++) {
- printf("(v=%d,e=%d)", nf->vert_edge_pairs[j].first, nf->vert_edge_pairs[j].second);
- }
- printf("\n");
- }
- }
-}
-
-ATTU static void dump_bitmap(const BLI_bitmap *bmap, int tot)
-{
- int i;
-
- for (i = 0; i < tot; i++) {
- if (BLI_BITMAP_TEST_BOOL(bmap, i)) {
- printf("%d ", i);
- }
- }
-}
-
-ATTU static void dump_meshdelete(const MeshDelete *meshdelete, const char *label)
-{
- printf("MeshDelete %s\n", label);
- printf("verts: ");
- dump_bitmap(meshdelete->vert_bmap, meshdelete->totvert);
- printf("\nedges: ");
- dump_bitmap(meshdelete->edge_bmap, meshdelete->totedge);
- printf("\nfaces: ");
- dump_bitmap(meshdelete->face_bmap, meshdelete->totface);
- printf("\n");
-}
-
-ATTU static void dump_intintmap(const IntIntMap *map, const char *label, const char *prefix)
-{
- IntIntMapIterator iter;
-
- printf("%sintintmap %s\n", prefix, label);
- for (intintmap_iter_init(&iter, map); !intintmap_iter_done(&iter); intintmap_iter_step(&iter)) {
- printf("%s %d -> %d\n", prefix, intintmap_iter_key(&iter), intintmap_iter_value(&iter));
- }
-}
-
-ATTU static void dump_intset(const IntSet *set, const char *label, const char *prefix)
-{
- LinkNode *ln;
- int v;
-
- printf("%sintset %s\n%s", prefix, label, prefix);
- for (ln = set->list; ln; ln = ln->next) {
- v = POINTER_AS_INT(ln->link);
- printf("%d ", v);
- }
- printf("\n");
+ const int boolean_mode)
+{
+ BLI_assert(use_self);
+ Boolean_trimesh_input *in = trimesh_input_from_bm(
+ bm, looptris, looptris_tot, test_fn, user_data);
+ Boolean_trimesh_output *out = BLI_boolean_trimesh(in, boolean_mode);
+ BLI_assert(out != NULL);
+ free_trimesh_input(in);
+ BLI_boolean_trimesh_free(out);
+ return false;
}
-ATTU static void dump_meshchange(const MeshChange *change, const char *label)
+bool BM_mesh_boolean_knife(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool UNUSED(use_self),
+ const bool UNUSED(use_separate_all))
{
- printf("meshchange %s\n\n", label);
- dump_meshadd(&change->add, "add");
- printf("\n");
- dump_meshdelete(&change->delete, "delete");
- printf("\n");
- dump_intintmap(&change->vert_merge_map, "vert_merge_map", "");
- printf("\n");
- dump_intset(&change->intersection_edges, "intersection_edges", "");
- printf("\n");
- dump_intset(&change->face_flip, "face_flip", "");
- printf("\n");
-}
+ int v, t;
-ATTU static void dump_intlist_from_tables(const int *table,
- const int *start_table,
- const int *len_table,
- int index)
-{
- int start, len, i;
+ Boolean_trimesh_input *in = trimesh_input_from_bm(
+ bm, looptris, looptris_tot, test_fn, user_data);
+ Boolean_trimesh_output *out = BLI_boolean_trimesh(in, BOOLEAN_NONE);
- start = start_table[index];
- len = len_table[index];
- for (i = 0; i < len; i++) {
- printf("%d", table[start + i]);
- if (i < len - 1) {
- printf(" ");
+ /* For now, for testing, just create new BMesh elements for returned subdivided mesh. */
+ if (out->vert_len > 0 && out->tri_len > 0) {
+ BMVert **new_bmv = MEM_malloc_arrayN(out->vert_len, sizeof(BMVert *), __func__);
+ for (v = 0; v < out->vert_len; v++) {
+ new_bmv[v] = BM_vert_create(bm, out->vert_coord[v], NULL, BM_CREATE_NOP);
}
- }
-}
-
-ATTU static void dump_cdt_input(const CDT_input *cdt, const char *label)
-{
- int i;
-
- printf("cdt input %s\n", label);
- printf(" verts\n");
- for (i = 0; i < cdt->verts_len; i++) {
- printf(" %d: (%.3f,%.3f)\n", i, F2(cdt->vert_coords[i]));
- }
- printf(" edges\n");
- for (i = 0; i < cdt->edges_len; i++) {
- printf(" %d: (%d,%d)\n", i, cdt->edges[i][0], cdt->edges[i][1]);
- }
- printf(" faces\n");
- for (i = 0; i < cdt->faces_len; i++) {
- printf(" %d: ", i);
- dump_intlist_from_tables(cdt->faces, cdt->faces_start_table, cdt->faces_len_table, i);
- printf("\n");
- }
-}
-
-ATTU static void dump_cdt_result(const CDT_result *cdt, const char *label, const char *prefix)
-{
- int i;
-
- printf("%scdt result %s\n", prefix, label);
- printf("%s verts\n", prefix);
- for (i = 0; i < cdt->verts_len; i++) {
- printf("%s %d: (%.3f,%.3f) orig=[", prefix, i, F2(cdt->vert_coords[i]));
- dump_intlist_from_tables(
- cdt->verts_orig, cdt->verts_orig_start_table, cdt->verts_orig_len_table, i);
- printf("]\n");
- }
- printf("%s edges\n", prefix);
- for (i = 0; i < cdt->edges_len; i++) {
- printf("%s %d: (%d,%d) orig=[", prefix, i, cdt->edges[i][0], cdt->edges[i][1]);
- dump_intlist_from_tables(
- cdt->edges_orig, cdt->edges_orig_start_table, cdt->edges_orig_len_table, i);
- printf("]\n");
- }
- printf("%s faces\n", prefix);
- for (i = 0; i < cdt->faces_len; i++) {
- printf("%s %d: ", prefix, i);
- dump_intlist_from_tables(cdt->faces, cdt->faces_start_table, cdt->faces_len_table, i);
- printf(" orig=[");
- dump_intlist_from_tables(
- cdt->faces_orig, cdt->faces_orig_start_table, cdt->faces_orig_len_table, i);
- printf("]\n");
- }
-}
-# define BMI(e) BM_elem_index_get(e)
-# define CO3(v) (v)->co[0], (v)->co[1], (v)->co[2]
-static void dump_v(BMVert *v)
-{
- printf("v%d[(%.3f,%.3f,%.3f)]@%p", BMI(v), CO3(v), v);
-}
-
-static void dump_e(BMEdge *e)
-{
- printf("e%d[", BMI(e));
- dump_v(e->v1);
- printf(", ");
- dump_v(e->v2);
- printf("]@%p", e);
-}
-
-static void dump_f(BMFace *f)
-{
- printf("f%d@%p", BMI(f), f);
-}
-
-static void dump_l(BMLoop *l)
-{
- printf("l%d[", BMI(l));
- dump_v(l->v);
- printf(" ");
- dump_e(l->e);
- printf(" ");
- dump_f(l->f);
- printf("]@%p", l);
-}
-
-ATTU static void dump_bm(struct BMesh *bm, const char *msg)
-{
- BMIter iter, iter2;
- BMVert *v;
- BMEdge *e;
- BMFace *f;
- BMLoop *l;
-
- printf("BMesh %s: %d verts, %d edges, %d loops, %d faces\n",
- msg,
- bm->totvert,
- bm->totedge,
- bm->totloop,
- bm->totface);
-
- printf("verts:\n");
- BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
- dump_v(v);
- printf(" %c", BM_elem_flag_test(v, BM_ELEM_SELECT) ? 's' : ' ');
- printf(" %c\n", BM_elem_flag_test(v, BM_ELEM_TAG) ? 't' : ' ');
- }
- printf("edges:\n");
- BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
- dump_e(e);
- printf(" %c", BM_elem_flag_test(e, BM_ELEM_SELECT) ? 's' : ' ');
- printf(" %c\n", BM_elem_flag_test(e, BM_ELEM_TAG) ? 't' : ' ');
- }
- printf("faces:\n");
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- dump_f(f);
- printf(" %c", BM_elem_flag_test(f, BM_ELEM_SELECT) ? 's' : ' ');
- printf(" %c\n", BM_elem_flag_test(f, BM_ELEM_TAG) ? 't' : ' ');
- printf(" loops:\n");
- BM_ITER_ELEM (l, &iter2, f, BM_LOOPS_OF_FACE) {
- printf(" ");
- dump_l(l);
- printf(" ");
- printf(" %s\n", BM_elem_flag_test(l, (1 << 6)) ? "long" : "");
+ for (t = 0; t < out->tri_len; t++) {
+ BMVert *v0 = new_bmv[out->tri[t][0]];
+ BMVert *v1 = new_bmv[out->tri[t][1]];
+ BMVert *v2 = new_bmv[out->tri[t][2]];
+ BM_face_create_quad_tri(bm, v0, v1, v2, NULL, NULL, BM_CREATE_NOP);
}
}
-}
-
-static bool face_in_tested_mesh(BMFace *bmf, int side, uchar *face_side)
-{
- if (side == 0) {
- return true;
- }
- return face_side[BM_elem_index_get(bmf)] & side;
-}
-static bool edge_in_tested_mesh(BMEdge *bme, int side, uchar *face_side)
-{
- BMIter fiter;
- BMFace *bmf;
-
- if (side == 0) {
- return true;
- }
- /* If any attached face passes test, then edge is in. */
- BM_ITER_ELEM (bmf, &fiter, bme, BM_FACES_OF_EDGE) {
- if (face_side[BM_elem_index_get(bmf)] & side) {
- return true;
- }
- }
+ free_trimesh_input(in);
+ BLI_boolean_trimesh_free(out);
return false;
}
-
-/* Retricting to just the BMesh as defined by test_val etc.,
- * analyze things that might cause problems.
- */
-ATTU bool analyze_bmesh_for_boolean(BMesh *bm, bool verbose, int side, uchar *face_side)
-{
- BMIter eiter, liter;
- int i, face_count;
- BMEdge *bme;
- BMLoop *bml, *bml1, *bml2;
- int tot_non_manifold_edges_1 = 0;
- int tot_non_manifold_edges_3plus = 0;
- int tot_wire_edges = 0;
- int tot_inconsistent_normal_edges = 0;
-
- if (verbose) {
- printf("\nANALYZE_BMESH_FOR_BOOLEAN\n\n");
- }
- BM_ITER_MESH_INDEX (bme, &eiter, bm, BM_EDGES_OF_MESH, i) {
- if (edge_in_tested_mesh(bme, side, face_side)) {
- face_count = 0;
- bml1 = bml2 = NULL;
- BM_ITER_ELEM (bml, &liter, bme, BM_LOOPS_OF_EDGE) {
- if (face_in_tested_mesh(bml->f, side, face_side)) {
- face_count++;
- if (bml1 == NULL) {
- bml1 = bml;
- }
- else if (bml2 == NULL) {
- bml2 = bml;
- }
- }
- }
- if (face_count == 0) {
- tot_wire_edges++;
- if (verbose) {
- printf("wire edge e%d\n", i);
- }
- }
- else if (face_count == 1) {
- tot_non_manifold_edges_1++;
- if (verbose) {
- printf("one-face edge e%d\n", i);
- }
- }
- else if (face_count == 2) {
- /* For consistent normals, loops of the two faces should be opposite. */
- if (bml1->v == bml2->v) {
- tot_inconsistent_normal_edges++;
- if (verbose) {
- printf("inconsistent normal edge e%d\n", i);
- }
- }
- }
- else if (face_count >= 3) {
- tot_non_manifold_edges_3plus++;
- if (verbose) {
- printf("three-plus-face edge e%d\n", i);
- }
- }
- }
- }
- return tot_non_manifold_edges_1 == 0 && tot_non_manifold_edges_3plus == 0 &&
- tot_wire_edges == 0 && tot_inconsistent_normal_edges == 0;
-}
-
-#endif
-
-#ifdef PERFDEBUG
-# define NCOUNTS 6
-# define NMAXES 1
-struct PerfCounts {
- int count[NCOUNTS];
- int max[NMAXES];
-} perfdata;
-
-static void perfdata_init(void)
-{
- memset(&perfdata, 0, sizeof(perfdata));
-}
-
-static void incperfcount(int countnum)
-{
- perfdata.count[countnum]++;
-}
-
-static void doperfmax(int maxnum, int val)
-{
- perfdata.max[maxnum] = max_ii(perfdata.max[maxnum], val);
-}
-
-static void dump_perfdata(void)
-{
- int i;
- printf("\nPERFDATA\n");
- for (i = 0; i < NCOUNTS; i++) {
- printf(" count%d = %d\n", i, perfdata.count[i]);
- }
- for (i = 0; i < NMAXES; i++) {
- printf(" max%d = %d\n", i, perfdata.max[i]);
- }
-}
-#endif
diff --git a/source/blender/bmesh/tools/bmesh_boolean.h b/source/blender/bmesh/tools/bmesh_boolean.h
index adfcd8f4d77..2551cb536e2 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.h
+++ b/source/blender/bmesh/tools/bmesh_boolean.h
@@ -21,20 +21,22 @@
* \ingroup bmesh
*/
+
bool BM_mesh_boolean(BMesh *bm,
- int (*test_fn)(BMFace *f, void *user_data),
- void *user_data,
- const bool use_self,
- const bool use_separate,
- const int boolean_mode,
- const float eps);
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const int boolean_mode);
+
+bool BM_mesh_boolean_knife(BMesh *bm,
+ struct BMLoop *(*looptris)[3],
+ const int looptris_tot,
+ int (*test_fn)(BMFace *f, void *user_data),
+ void *user_data,
+ const bool use_self,
+ const bool use_separate_all);
-enum {
- BMESH_BOOLEAN_NONE = -1,
- /* aligned with BooleanModifierOp */
- BMESH_BOOLEAN_ISECT = 0,
- BMESH_BOOLEAN_UNION = 1,
- BMESH_BOOLEAN_DIFFERENCE = 2,
-};
#endif /* __BMESH_BOOLEAN_H__ */
diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
index 0fc0fb130fc..97dcd53b6c5 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -117,7 +117,7 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics)
cross_v3_v3v3(edge_plane, edge_vector, f->no);
copy_v3db_v3fl(edge_plane_db, edge_plane);
- if (normalize_v3_d(edge_plane_db) > (double)FLT_EPSILON) {
+ if (normalize_v3_db(edge_plane_db) > (double)FLT_EPSILON) {
Quadric q;
float center[3];
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 97dae08be6d..a5a67fde596 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -46,10 +46,9 @@
#include "mesh_intern.h" /* own include */
-#include "tools/bmesh_intersect.h"
#include "tools/bmesh_boolean.h"
+#include "tools/bmesh_intersect.h"
#include "tools/bmesh_separate.h"
-#include "tools/bmesh_edgesplit.h"
/* detect isolated holes and fill them */
#define USE_NET_ISLAND_CONNECT
@@ -150,7 +149,7 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
bool use_separate_cut = false;
const int separate_mode = RNA_enum_get(op->ptr, "separate_mode");
const float eps = RNA_float_get(op->ptr, "threshold");
- const bool newbool = RNA_boolean_get(op->ptr, "newbool");
+ const bool exact = RNA_boolean_get(op->ptr, "use_exact");
bool use_self;
bool has_isect;
@@ -195,23 +194,29 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
continue;
}
- if (newbool) {
- has_isect = BM_mesh_boolean(em->bm, test_fn, NULL, use_self, use_separate_all, -1, eps);
+ if (exact) {
+ has_isect = BM_mesh_boolean_knife(em->bm,
+ em->looptris,
+ em->tottri,
+ test_fn,
+ NULL,
+ use_self,
+ use_separate_all);
}
else {
has_isect = BM_mesh_intersect(em->bm,
- em->looptris,
- em->tottri,
- test_fn,
- NULL,
- use_self,
- use_separate_all,
- true,
- true,
- true,
- true,
- -1,
- eps);
+ em->looptris,
+ em->tottri,
+ test_fn,
+ NULL,
+ use_self,
+ use_separate_all,
+ true,
+ true,
+ true,
+ true,
+ -1,
+ eps);
}
if (use_separate_cut) {
@@ -219,9 +224,6 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
BM_mesh_separate_faces(
em->bm, BM_elem_cb_check_hflag_enabled_simple(const BMFace *, BM_ELEM_SELECT));
}
- else if (newbool && use_separate_all) {
- BM_mesh_edgesplit(em->bm, false, true, false);
- }
edbm_intersect_select(em, obedit->data, has_isect);
@@ -274,8 +276,12 @@ void MESH_OT_intersect(struct wmOperatorType *ot)
RNA_def_enum(
ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", "");
RNA_def_float_distance(
- ot->srna, "threshold", 0.00001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
- RNA_def_boolean(ot->srna, "newbool", true, "New", "Use the new algorithm?");
+ ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
+ RNA_def_boolean(ot->srna,
+ "use_exact",
+ true,
+ "Exact",
+ "Use the Exact-arithmetic boolean (slower, handles more cases");
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -298,8 +304,9 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
{
const int boolean_operation = RNA_enum_get(op->ptr, "operation");
bool use_swap = RNA_boolean_get(op->ptr, "use_swap");
+ bool use_self = RNA_boolean_get(op->ptr, "use_self");
+ bool use_exact = RNA_boolean_get(op->ptr, "use_exact");
const float eps = RNA_float_get(op->ptr, "threshold");
- const bool newbool = RNA_boolean_get(op->ptr, "newbool");
int (*test_fn)(BMFace *, void *);
bool has_isect;
@@ -317,23 +324,29 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
continue;
}
- if (newbool) {
- has_isect = BM_mesh_boolean(em->bm, test_fn, NULL, false, false, boolean_operation, eps);
+ if (use_exact) {
+ has_isect = BM_mesh_boolean(em->bm,
+ em->looptris,
+ em->tottri,
+ test_fn,
+ NULL,
+ use_self,
+ boolean_operation);
}
else {
has_isect = BM_mesh_intersect(em->bm,
- em->looptris,
- em->tottri,
- test_fn,
- NULL,
- false,
- false,
- true,
- true,
- false,
- true,
- boolean_operation,
- eps);
+ em->looptris,
+ em->tottri,
+ test_fn,
+ NULL,
+ false,
+ false,
+ true,
+ true,
+ false,
+ true,
+ boolean_operation,
+ eps);
}
edbm_intersect_select(em, obedit->data, has_isect);
@@ -372,7 +385,7 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
RNA_def_enum(ot->srna,
"operation",
isect_boolean_operation_items,
- BMESH_ISECT_BOOLEAN_DIFFERENCE,
+ BMESH_ISECT_BOOLEAN_UNION, /* DEBUG!! old default is Difference. */
"Boolean",
"");
RNA_def_boolean(ot->srna,
@@ -380,9 +393,18 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
false,
"Swap",
"Use with difference intersection to swap which side is kept");
+ RNA_def_boolean(ot->srna,
+ "use_self",
+ true, /* DEBUG!! */
+ "Self",
+ "Do self-union or self-intersection");
RNA_def_float_distance(
- ot->srna, "threshold", 0.00001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
- RNA_def_boolean(ot->srna, "newbool", true, "New", "Use the new algorithm?");
+ ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
+ RNA_def_boolean(ot->srna,
+ "use_exact",
+ true,
+ "Exact",
+ "Use the Exact-arithmetic boolean (slower, handles more cases");
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
index c1832d5290d..c2412b295ff 100644
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ b/source/blender/modifiers/intern/MOD_boolean.c
@@ -61,7 +61,6 @@
#include "bmesh.h"
#include "bmesh_tools.h"
-#include "tools/bmesh_boolean.h"
#include "tools/bmesh_intersect.h"
#ifdef DEBUG_TIME
@@ -71,15 +70,9 @@
static void initData(ModifierData *md)
{
- bool newbool = true;
BooleanModifierData *bmd = (BooleanModifierData *)md;
- if (newbool) {
- bmd->double_threshold = 1e-5f;
- }
- else {
- bmd->double_threshold = 1e-6f;
- }
+ bmd->double_threshold = 1e-6f;
bmd->operation = eBooleanModifierOp_Difference;
}
@@ -175,7 +168,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
{
BooleanModifierData *bmd = (BooleanModifierData *)md;
Mesh *result = mesh;
- bool newbool = true;
Mesh *mesh_other;
@@ -235,14 +227,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
int tottri;
BMLoop *(*looptris)[3];
- if (newbool) {
- looptris = NULL;
- tottri = 0;
- }
- else {
- looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
- }
+ looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
+
+ BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
/* postpone this until after tessellating
* so we can use the original normals before the vertex are moved */
@@ -323,27 +310,21 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
0;
}
- if (newbool) {
- BM_mesh_boolean(
- bm, bm_face_isect_pair, NULL, false, false, bmd->operation, bmd->double_threshold);
- }
- else {
- BM_mesh_intersect(bm,
- looptris,
- tottri,
- bm_face_isect_pair,
- NULL,
- false,
- use_separate,
- use_dissolve,
- use_island_connect,
- false,
- false,
- bmd->operation,
- bmd->double_threshold);
-
- MEM_freeN(looptris);
- }
+ BM_mesh_intersect(bm,
+ looptris,
+ tottri,
+ bm_face_isect_pair,
+ NULL,
+ false,
+ use_separate,
+ use_dissolve,
+ use_island_connect,
+ false,
+ false,
+ bmd->operation,
+ bmd->double_threshold);
+
+ MEM_freeN(looptris);
}
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 59c0021e0f3..1bd4c99e935 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -1649,7 +1649,6 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar
in.faces_start_table = in_faces_start_table;
in.faces_len_table = in_faces_len_table;
in.epsilon = epsilon;
- in.skip_input_modify = false;
res = BLI_delaunay_2d_cdt_calc(&in, output_type);
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index aed2a9350bb..0c70ab2f64b 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -79,6 +79,10 @@ if(WITH_TBB)
link_directories(${LIBDIR}/tbb/lib)
endif()
+if(WITH_GMP)
+ blender_include_dirs(${GMP_INCLUDE_DIRS})
+ link_directories(${LIBDIR}/gmp/lib)
+endif()
if(WITH_PYTHON)
blender_include_dirs(../blender/python)
@@ -694,6 +698,23 @@ elseif(WIN32)
)
endif()
+ if(WITH_GMP)
+ install(
+ FILES ${LIBDIR}/gmp/lib/libgmp-10.dll
+ DESTINATION "."
+ )
+ install(
+ FILES ${LIBDIR}/gmp/lib/libgmpxx.dll
+ DESTINATION "."
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ )
+ install(
+ FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll
+ DESTINATION "."
+ CONFIGURATIONS Debug
+ )
+ endif()
+
if(WITH_WINDOWS_PDB)
if(WITH_WINDOWS_STRIPPED_PDB)
# Icky hack for older cmake from https://stackoverflow.com/a/21198501