From 777690902663f3dc820f183329e404a6388485f4 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Wed, 1 Jul 2020 12:42:04 -0400 Subject: Change boolean blenlib interface to be purely C++. --- source/blender/blenlib/BLI_boolean.h | 93 ---------- source/blender/blenlib/BLI_boolean.hh | 67 ++++++++ source/blender/blenlib/CMakeLists.txt | 2 +- source/blender/blenlib/intern/boolean.cc | 231 +++++++------------------ source/blender/bmesh/tools/bmesh_boolean.cc | 7 +- tests/gtests/blenlib/BLI_boolean_test.cc | 258 ++++++++++------------------ 6 files changed, 223 insertions(+), 435 deletions(-) delete mode 100644 source/blender/blenlib/BLI_boolean.h create mode 100644 source/blender/blenlib/BLI_boolean.hh diff --git a/source/blender/blenlib/BLI_boolean.h b/source/blender/blenlib/BLI_boolean.h deleted file mode 100644 index 55155afc1d4..00000000000 --- a/source/blender/blenlib/BLI_boolean.h +++ /dev/null @@ -1,93 +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. - */ - -#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 *in0, - const Boolean_trimesh_input *in1, - int bool_optype); - -void BLI_boolean_trimesh_free(Boolean_trimesh_output *output); - -#ifdef __cplusplus -} - -# include "BLI_array.hh" -# include "BLI_math_mpq.hh" -# include "BLI_mesh_intersect.hh" -# include "BLI_mpq3.hh" - -namespace blender { -namespace meshintersect { - -struct PolyMeshOrig { - Array vert_orig; - Array> face_orig; - Array>> edge_orig; -}; - -struct PolyMesh { - Array vert; - Array> face; - /* triangulation can have zero length: then boolean will do it. */ - Array> triangulation; - /* orig can be dummy for boolean input, but has useful information for its output. */ - PolyMeshOrig orig; -}; - -PolyMesh boolean(PolyMesh &pm, int bool_optype, int nshapes, std::function shape_fn); - -void write_obj_polymesh(const Array &vert, - const Array> &face, - const std::string &objname); - -} // namespace meshintersect -} // namespace blender - -#endif - -#endif /* __BLI_BOOLEAN_H__ */ diff --git a/source/blender/blenlib/BLI_boolean.hh b/source/blender/blenlib/BLI_boolean.hh new file mode 100644 index 00000000000..1ced075a907 --- /dev/null +++ b/source/blender/blenlib/BLI_boolean.hh @@ -0,0 +1,67 @@ +/* + * 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_HH__ +#define __BLI_BOOLEAN_HH__ + +/** \file + * \ingroup bli + */ + +# include "BLI_array.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mesh_intersect.hh" +# include "BLI_mpq3.hh" + +namespace blender { +namespace meshintersect { + +/* Enum values after BOOLEAN_NONE need to match BMESH_ISECT_BOOLEAN_... values in editmesh_intersect.c. */ +enum bool_optype { + BOOLEAN_NONE = -1, + /* Aligned with BooleanModifierOp. */ + BOOLEAN_ISECT = 0, + BOOLEAN_UNION = 1, + BOOLEAN_DIFFERENCE = 2, +}; + +struct PolyMeshOrig { + Array vert_orig; + Array> face_orig; + Array>> edge_orig; +}; + +struct PolyMesh { + Array vert; + Array> face; + /* triangulation can have zero length: then boolean will do it. */ + Array> triangulation; + /* orig can be dummy for boolean input, but has useful information for its output. */ + PolyMeshOrig orig; +}; + +PolyMesh boolean(PolyMesh &pm, bool_optype op, int nshapes, std::function shape_fn); + +TriMesh boolean_trimesh(const TriMesh &tm, bool_optype op, int nshapes, std::function shape_fn); + +void write_obj_polymesh(const Array &vert, + const Array> &face, + const std::string &objname); + +} // namespace meshintersect +} // namespace blender + +#endif /* __BLI_BOOLEAN_HH__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 8d5d370df3d..52cdd6ca1af 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -157,7 +157,7 @@ set(SRC BLI_bitmap.h BLI_bitmap_draw_2d.h BLI_blenlib.h - BLI_boolean.h + BLI_boolean.hh BLI_boxpack_2d.h BLI_buffer.h BLI_color.hh diff --git a/source/blender/blenlib/intern/boolean.cc b/source/blender/blenlib/intern/boolean.cc index 55f969c1b81..bdf9b56dda6 100644 --- a/source/blender/blenlib/intern/boolean.cc +++ b/source/blender/blenlib/intern/boolean.cc @@ -33,7 +33,7 @@ #include "BLI_vector.hh" #include "BLI_vector_set.hh" -#include "BLI_boolean.h" +#include "BLI_boolean.hh" namespace blender { @@ -987,7 +987,7 @@ static int find_ambient_cell(const TriMesh &tm, static void propagate_windings_and_flag(PatchesInfo &pinfo, CellsInfo &cinfo, int c_ambient, - int bool_optype, + bool_optype op, int nshapes, std::function shape_fn) { @@ -1025,7 +1025,7 @@ static void propagate_windings_and_flag(PatchesInfo &pinfo, if (dbg_level > 1) { std::cout << " representative tri " << t << ": in shape " << shape << "\n"; } - cell_neighbor.set_winding_and_flag(cell, shape, winding_delta, bool_optype); + cell_neighbor.set_winding_and_flag(cell, shape, winding_delta, op); if (dbg_level > 1) { std::cout << " now cell_neighbor = " << cell_neighbor << "\n"; } @@ -1177,9 +1177,9 @@ static TriMesh extract_from_flag_diffs(const TriMesh &tm_subdivided, return tm_out; } -static const char *bool_optype_name(int bool_optype) +static const char *bool_optype_name(bool_optype op) { - switch (bool_optype) { + switch (op) { case BOOLEAN_NONE: return "none"; break; @@ -1196,72 +1196,6 @@ static const char *bool_optype_name(int bool_optype) } } -/* - * This function does a boolean operation on nshapes inputs. - * All the shapes are combined in tm_in. - * The shape_fn function should take a triangle index in tm_in and return - * a number in the range 0 to nshapes-1, to say which shape that triangle is in. - */ -static TriMesh nary_boolean(const TriMesh &tm_in, - int bool_optype, - int nshapes, - std::function shape_fn) -{ - constexpr int dbg_level = 0; - if (dbg_level > 0) { - std::cout << "BOOLEAN of " << nshapes << " operand" << (nshapes == 1 ? "" : "s") - << " op=" << bool_optype_name(bool_optype) << "\n"; - } - if (tm_in.vert.size() == 0 || tm_in.tri.size() == 0) { - return TriMesh(tm_in); - } - TriMesh tm_si = trimesh_self_intersect(tm_in); - /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */ - if (tm_si.tri.size() == 0 || bool_optype == BOOLEAN_NONE) { - return tm_si; - } - auto si_shape_fn = [shape_fn, tm_si](int t) { return shape_fn(tm_si.tri[t].orig()); }; - if (dbg_level > 1) { - write_obj_trimesh(tm_si.vert, tm_si.tri, "boolean_tm_input"); - std::cout << "boolean tm input:\n"; - for (int t = 0; t < static_cast(tm_si.tri.size()); ++t) { - std::cout << "tri " << t << " = " << tm_si.tri[t] << " shape " << si_shape_fn(t) << "\n"; - } - } - TriMeshTopology tm_si_topo(&tm_si); - PatchesInfo pinfo = find_patches(tm_si, tm_si_topo); - CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo); - cinfo.init_windings(nshapes); - int c_ambient = find_ambient_cell(tm_si, tm_si_topo, pinfo); - if (c_ambient == -1) { - /* TODO: find a way to propagate this error to user properly. */ - std::cout << "Could not find an ambient cell; input not valid?\n"; - return TriMesh(tm_si); - } - propagate_windings_and_flag(pinfo, cinfo, c_ambient, bool_optype, nshapes, si_shape_fn); - TriMesh tm_out = extract_from_flag_diffs(tm_si, pinfo, cinfo); - if (dbg_level > 1) { - write_obj_trimesh(tm_out.vert, tm_out.tri, "boolean_tm_output"); - } - return tm_out; -} - -static TriMesh self_boolean(const TriMesh &tm_in, int bool_optype) -{ - return nary_boolean(tm_in, bool_optype, 1, [](int UNUSED(t)) { return 0; }); -} - -static TriMesh binary_boolean(const TriMesh &tm_in_a, const TriMesh &tm_in_b, int bool_optype) -{ - /* Just combine the two pieces. We can tell by original triangle number which side it came - * from. - */ - TriMesh tm_in = concat_trimeshes(tm_in_a, tm_in_b); - int b_tri_start = static_cast(tm_in_a.tri.size()); - auto shape_fn = [b_tri_start](int t) { return (t >= b_tri_start ? 1 : 0); }; - return nary_boolean(tm_in, bool_optype, 2, shape_fn); -} - static Array triangulate_poly(int orig_face, const Array &face, const Array &vert) @@ -1985,7 +1919,57 @@ static PolyMesh polymesh_from_trimesh_with_dissolve(const TriMesh &tm_out, const return pm_out; } -/* Do the boolean operation bool_optype on the polygon mesh pm_in. +/* + * This function does a boolean operation on a TriMesh with nshapes inputs. + * All the shapes are combined in tm_in. + * The shape_fn function should take a triangle index in tm_in and return + * a number in the range 0 to nshapes-1, to say which shape that triangle is in. + */ +TriMesh boolean_trimesh(const TriMesh &tm_in, + bool_optype op, + int nshapes, + std::function shape_fn) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "BOOLEAN of " << nshapes << " operand" << (nshapes == 1 ? "" : "s") + << " op=" << bool_optype_name(op) << "\n"; + } + if (tm_in.vert.size() == 0 || tm_in.tri.size() == 0) { + return TriMesh(tm_in); + } + TriMesh tm_si = trimesh_self_intersect(tm_in); + /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */ + if (tm_si.tri.size() == 0 || op == BOOLEAN_NONE) { + return tm_si; + } + auto si_shape_fn = [shape_fn, tm_si](int t) { return shape_fn(tm_si.tri[t].orig()); }; + if (dbg_level > 1) { + write_obj_trimesh(tm_si.vert, tm_si.tri, "boolean_tm_input"); + std::cout << "boolean tm input:\n"; + for (int t = 0; t < static_cast(tm_si.tri.size()); ++t) { + std::cout << "tri " << t << " = " << tm_si.tri[t] << " shape " << si_shape_fn(t) << "\n"; + } + } + TriMeshTopology tm_si_topo(&tm_si); + PatchesInfo pinfo = find_patches(tm_si, tm_si_topo); + CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo); + cinfo.init_windings(nshapes); + int c_ambient = find_ambient_cell(tm_si, tm_si_topo, pinfo); + if (c_ambient == -1) { + /* TODO: find a way to propagate this error to user properly. */ + std::cout << "Could not find an ambient cell; input not valid?\n"; + return TriMesh(tm_si); + } + propagate_windings_and_flag(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn); + TriMesh tm_out = extract_from_flag_diffs(tm_si, pinfo, cinfo); + if (dbg_level > 1) { + write_obj_trimesh(tm_out.vert, tm_out.tri, "boolean_tm_output"); + } + return tm_out; +} + +/* Do the boolean operation op on the polygon mesh pm_in. * The boolean operation has nshapes input shapes. Each is a disjoint subset of the input polymesh. * The shape_fn argument, when applied to an input face argument, says which shape it is in * (should be a value from -1 to nshapes - 1: if -1, it is not part of any shape). @@ -1993,111 +1977,12 @@ static PolyMesh polymesh_from_trimesh_with_dissolve(const TriMesh &tm_out, const * optional triangulation: parallel to each face, it gives a set of IndexedTriangles that * triangulate that face. * pm arg isn't const because we will add triangulation if it is not there. */ -PolyMesh boolean(PolyMesh &pm_in, int bool_optype, int nshapes, std::function shape_fn) +PolyMesh boolean(PolyMesh &pm_in, bool_optype op, int nshapes, std::function shape_fn) { TriMesh tm_in = trimesh_from_polymesh(pm_in); - TriMesh tm_out = nary_boolean(tm_in, bool_optype, nshapes, shape_fn); + TriMesh tm_out = boolean_trimesh(tm_in, op, nshapes, shape_fn); return polymesh_from_trimesh_with_dissolve(tm_out, pm_in); } } // namespace meshintersect } // namespace blender - -/* - * Convert the C-style Boolean_trimesh_input into our internal C++ class for triangle meshes, - * TriMesh. - */ -static blender::meshintersect::TriMesh trimesh_from_input(const Boolean_trimesh_input *in, - int side) -{ - constexpr int dbg_level = 0; - BLI_assert(in != nullptr); - blender::meshintersect::TriMesh tm_in; - tm_in.vert = blender::Array(in->vert_len); - for (int v = 0; v < in->vert_len; ++v) { - tm_in.vert[v] = blender::mpq3( - in->vert_coord[v][0], in->vert_coord[v][1], in->vert_coord[v][2]); - } - tm_in.tri = blender::Array(in->tri_len); - for (int t = 0; t < in->tri_len; ++t) { - tm_in.tri[t] = blender::meshintersect::IndexedTriangle( - in->tri[t][0], in->tri[t][1], in->tri[t][2], t); - } - if (dbg_level > 0) { - /* Output in format that can be pasted into test spec. */ - std::cout << "Input side " << side << "\n"; - std::cout << tm_in.vert.size() << " " << tm_in.tri.size() << "\n"; - for (uint v = 0; v < tm_in.vert.size(); ++v) { - std::cout << " " << tm_in.vert[v][0].get_d() << " " << tm_in.vert[v][1].get_d() << " " - << tm_in.vert[v][2].get_d() << "\n"; - } - for (uint t = 0; t < tm_in.tri.size(); ++t) { - std::cout << " " << tm_in.tri[t].v0() << " " << tm_in.tri[t].v1() << " " - << tm_in.tri[t].v2() << "\n"; - } - std::cout << "\n"; - blender::meshintersect::write_obj_trimesh( - tm_in.vert, tm_in.tri, "boolean_input" + std::to_string(side)); - } - return tm_in; -} - -/* Do a boolean operation between one or two triangle meshes, and return the answer as another - * triangle mesh. The in_b argument may be NULL, meaning that the caller wants a unary boolean - * operation. If the bool_optype is BOOLEAN_NONE, this function just does the self intersection of - * the one or two meshes. This is a C interface. The caller must call BLI_boolean_trimesh_free() on - * the returned value when done with it. - */ -extern "C" Boolean_trimesh_output *BLI_boolean_trimesh(const Boolean_trimesh_input *in_a, - const Boolean_trimesh_input *in_b, - int bool_optype) -{ - constexpr int dbg_level = 0; - bool is_binary = in_b != NULL; - if (dbg_level > 0) { - std::cout << "BLI_BOOLEAN_TRIMESH op=" << blender::meshintersect::bool_optype_name(bool_optype) - << (is_binary ? " binary" : " unary") << "\n"; - } - blender::meshintersect::TriMesh tm_in_a = trimesh_from_input(in_a, 0); - blender::meshintersect::TriMesh tm_out; - if (is_binary) { - blender::meshintersect::TriMesh tm_in_b = trimesh_from_input(in_b, 1); - tm_out = blender::meshintersect::binary_boolean(tm_in_a, tm_in_b, bool_optype); - } - else { - tm_out = blender::meshintersect::self_boolean(tm_in_a, bool_optype); - } - if (dbg_level > 1) { - blender::meshintersect::write_html_trimesh( - tm_out.vert, tm_out.tri, "mesh_boolean_test.html", "after self_boolean"); - } - int nv = tm_out.vert.size(); - int nt = tm_out.tri.size(); - Boolean_trimesh_output *output = static_cast( - MEM_mallocN(sizeof(*output), __func__)); - output->vert_len = nv; - output->tri_len = nt; - output->vert_coord = static_castvert_coord)>( - MEM_malloc_arrayN(nv, sizeof(output->vert_coord[0]), __func__)); - output->tri = static_casttri)>( - MEM_malloc_arrayN(nt, sizeof(output->tri[0]), __func__)); - for (int v = 0; v < nv; ++v) { - output->vert_coord[v][0] = static_cast(tm_out.vert[v][0].get_d()); - output->vert_coord[v][1] = static_cast(tm_out.vert[v][1].get_d()); - output->vert_coord[v][2] = static_cast(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; -} - -/* Free the memory used in the return value of BLI_boolean_trimesh. */ -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/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc index 56385f741b5..40f893005ee 100644 --- a/source/blender/bmesh/tools/bmesh_boolean.cc +++ b/source/blender/bmesh/tools/bmesh_boolean.cc @@ -21,7 +21,7 @@ */ #include "BLI_array.hh" -#include "BLI_boolean.h" +#include "BLI_boolean.hh" #include "BLI_math.h" #include "BLI_math_mpq.hh" #include "BLI_mesh_intersect.hh" @@ -169,7 +169,8 @@ static int bmesh_boolean(BMesh *bm, } }; } - PolyMesh pm_out = boolean(pm_in, boolean_mode, nshapes, shape_fn); + bool_optype op = static_cast(boolean_mode); + PolyMesh pm_out = boolean(pm_in, op, nshapes, shape_fn); apply_polymesh_output_to_bmesh(bm, pm_out); return pm_in.vert.size() != pm_out.vert.size() || pm_in.face.size() != pm_out.face.size(); } @@ -221,7 +222,7 @@ bool BM_mesh_boolean_knife(BMesh *bm, const bool use_self, const bool use_separate_all) { - return blender::meshintersect::bmesh_boolean(bm, looptris, looptris_tot, test_fn, user_data, use_self, use_separate_all, BOOLEAN_NONE); + return blender::meshintersect::bmesh_boolean(bm, looptris, looptris_tot, test_fn, user_data, use_self, use_separate_all, blender::meshintersect::BOOLEAN_NONE); } } /* extern "C" */ diff --git a/tests/gtests/blenlib/BLI_boolean_test.cc b/tests/gtests/blenlib/BLI_boolean_test.cc index 6503d9ca946..5e542ef0f73 100644 --- a/tests/gtests/blenlib/BLI_boolean_test.cc +++ b/tests/gtests/blenlib/BLI_boolean_test.cc @@ -9,62 +9,65 @@ #include "MEM_guardedalloc.h" #include "BLI_array.hh" -#include "BLI_boolean.h" +#include "BLI_boolean.hh" #include "BLI_math_mpq.hh" #include "BLI_mpq3.hh" #include "BLI_vector.hh" -/* Class that can make a Boolean_trimesh_input from a string spec. +using blender::Array; +using blender::Vector; +using blender::mpq3; +using blender::meshintersect::TriMesh; +using blender::meshintersect::PolyMesh; +using blender::meshintersect::IndexedTriangle; +using blender::meshintersect::BOOLEAN_NONE; +using blender::meshintersect::BOOLEAN_ISECT; +using blender::meshintersect::BOOLEAN_UNION; +using blender::meshintersect::BOOLEAN_DIFFERENCE; +using blender::meshintersect::boolean; +using blender::meshintersect::boolean_trimesh; +using blender::meshintersect::write_obj_trimesh; +using blender::meshintersect::write_obj_polymesh; + +/* Class that can make a TriMesh from a string spec. * The spec has #verts #tris on the first line, then all the vert coords, * then all the tris as vert index triples. */ class BT_input { public: + TriMesh trimesh; BT_input(const char *spec) { std::istringstream ss(spec); std::string line; getline(ss, line); std::istringstream hdrss(line); - m_bti.vert_coord = nullptr; - m_bti.tri = nullptr; - hdrss >> m_bti.vert_len >> m_bti.tri_len; - if (m_bti.vert_len > 0 && m_bti.tri_len > 0) { - m_bti.vert_coord = new float[m_bti.vert_len][3]; - m_bti.tri = new int[m_bti.tri_len][3]; + int nv, nt; + hdrss >> nv >> nt; + trimesh.vert = Array(nv); + trimesh.tri = Array(nt); + if (nv > 0 && nt > 0) { int i = 0; - while (i < m_bti.vert_len && getline(ss, line)) { + while (i < nv && getline(ss, line)) { std::istringstream iss(line); - iss >> m_bti.vert_coord[i][0] >> m_bti.vert_coord[i][1] >> m_bti.vert_coord[i][2]; + iss >> trimesh.vert[i][0] >> trimesh.vert[i][1] >> trimesh.vert[i][2]; ++i; } i = 0; - while (i < m_bti.tri_len && getline(ss, line)) { + while (i < nt && getline(ss, line)) { std::istringstream tss(line); - tss >> m_bti.tri[i][0] >> m_bti.tri[i][1] >> m_bti.tri[i][2]; + int v0, v1, v2; + tss >> v0 >> v1 >> v2; + trimesh.tri[i] = IndexedTriangle(v0, v1, v2, i); ++i; } } } - - ~BT_input() - { - delete[] m_bti.vert_coord; - delete[] m_bti.tri; - } - - Boolean_trimesh_input *input() - { - return &m_bti; - } - - private: - Boolean_trimesh_input m_bti; }; class BP_input { public: - blender::meshintersect::PolyMesh polymesh; + PolyMesh polymesh; BP_input(const char *spec) { @@ -74,8 +77,8 @@ class BP_input { std::istringstream hdrss(line); int nv, nf; hdrss >> nv >> nf; - polymesh.vert = blender::Array(nv); - polymesh.face = blender::Array>(nf); + polymesh.vert = Array(nv); + polymesh.face = Array>(nf); if (nv > 0 && nf > 0) { int i = 0; while (i < nv && getline(ss, line)) { @@ -86,12 +89,12 @@ class BP_input { i = 0; while (i < nf && getline(ss, line)) { std::istringstream tss(line); - blender::Vector f; + Vector f; int v; while (tss >> v) { f.append(v); } - polymesh.face[i] = blender::Array(f.size()); + polymesh.face[i] = Array(f.size()); std::copy(f.begin(), f.end(), polymesh.face[i].begin()); ++i; } @@ -99,96 +102,28 @@ class BP_input { } }; -/* 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]); - -static void write_obj(const Boolean_trimesh_output *out, const std::string objname) -{ - constexpr const char *objdir = "/tmp/"; - if (out->tri_len == 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 (int v = 0; v < out->vert_len; ++v) { - float *co = out->vert_coord[v]; - f << "v " << co[0] << " " << co[1] << " " << co[2] << "\n"; - } - - for (int i = 0; i < out->tri_len; ++i) { - int matindex = i % numcolors; - f << "usemtl mat" + std::to_string(matindex) + "\n"; - /* OBJ files use 1-indexing for vertices. */ - int *tri = out->tri[i]; - f << "f " << tri[0] + 1 << " " << tri[1] + 1 << " " << tri[2] + 1 << "\n"; - } - 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"; - } -} constexpr bool DO_OBJ = true; TEST(eboolean, Empty) { - Boolean_trimesh_input in; - in.vert_len = 0; - in.tri_len = 0; - in.vert_coord = NULL; - in.tri = NULL; - Boolean_trimesh_output *out = BLI_boolean_trimesh(&in, nullptr, BOOLEAN_NONE); - EXPECT_EQ(out->vert_len, 0); - EXPECT_EQ(out->tri_len, 0); - BLI_boolean_trimesh_free(out); + TriMesh in; + TriMesh out = boolean_trimesh(in, BOOLEAN_NONE, 1, [](int){return 0;}); + EXPECT_EQ(out.vert.size(), 0); + EXPECT_EQ(out.tri.size(), 0); } TEST(eboolean, TetTet) { const char *spec = R"(8 8 - 0.0 0.0 0.0 - 2.0 0.0 0.0 - 1.0 2.0 0.0 - 1.0 1.0 2.0 - 0.0 0.0 1.0 - 2.0 0.0 1.0 - 1.0 2.0 1.0 - 1.0 1.0 3.0 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 0 2 1 0 1 3 1 2 3 @@ -198,35 +133,34 @@ TEST(eboolean, TetTet) 5 6 7 6 4 7 )"; + BT_input bti(spec); - Boolean_trimesh_output *out = BLI_boolean_trimesh(bti.input(), nullptr, BOOLEAN_NONE); - EXPECT_EQ(out->vert_len, 11); - EXPECT_EQ(out->tri_len, 20); + TriMesh out = boolean_trimesh(bti.trimesh, BOOLEAN_NONE, 1, [](int){return 0;}); + EXPECT_EQ(out.vert.size(), 11); + EXPECT_EQ(out.tri.size(), 20); if (DO_OBJ) { - write_obj(out, "tettet"); + write_obj_trimesh(out.vert, out.tri, "tettet"); } - BLI_boolean_trimesh_free(out); - Boolean_trimesh_output *out2 = BLI_boolean_trimesh(bti.input(), nullptr, BOOLEAN_UNION); - EXPECT_EQ(out2->vert_len, 10); - EXPECT_EQ(out2->tri_len, 16); + TriMesh out2 =boolean_trimesh(bti.trimesh, BOOLEAN_UNION, 1, [](int){return 0;}); + EXPECT_EQ(out2.vert.size(), 10); + EXPECT_EQ(out2.tri.size(), 16); if (DO_OBJ) { - write_obj(out2, "tettet_union"); + write_obj_trimesh(out2.vert, out2.tri, "tettet_union"); } - BLI_boolean_trimesh_free(out2); } TEST(eboolean, TetTet2) { const char *spec = R"(8 8 - 0.0 1.0 -1.0 - 0.875 -0.5 -1.0 - -0.875 -0.5 -1.0 - 0.0 0.0 1.0 - 0.0 1.0 0.0 - 0.875 -0.5 0.0 - -0.875 -0.5 0.0 - 0.0 0.0 2.0 + 0 1 -1 + 7/8 -1/2 -1 + -7/8 -1/2 -1 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 2 0 3 1 0 1 2 1 3 2 @@ -238,13 +172,12 @@ TEST(eboolean, TetTet2) )"; BT_input bti(spec); - Boolean_trimesh_output *out = BLI_boolean_trimesh(bti.input(), nullptr, BOOLEAN_UNION); - EXPECT_EQ(out->vert_len, 10); - EXPECT_EQ(out->tri_len, 16); + TriMesh out =boolean_trimesh(bti.trimesh, BOOLEAN_UNION, 1, [](int){return 0;}); + EXPECT_EQ(out.vert.size(), 10); + EXPECT_EQ(out.tri.size(), 16); if (DO_OBJ) { - write_obj(out, "tettet2_union"); + write_obj_trimesh(out.vert, out.tri, "tettet2_union"); } - BLI_boolean_trimesh_free(out); } TEST(eboolean, CubeTet) @@ -258,10 +191,10 @@ TEST(eboolean, CubeTet) 1 -1 1 1 1 -1 1 1 1 - 0 0.5 0.5 - 0.5 -0.25 0.5 - -0.5 -0.25 0.5 - 0 0 1.5 + 0 1/2 1/2 + 1/2 -1/4 1/2 + -1/2 -1/4 1/2 + 0 0 3/2 0 1 3 0 3 2 2 3 7 @@ -281,47 +214,42 @@ TEST(eboolean, CubeTet) )"; BT_input bti(spec); - Boolean_trimesh_output *out = BLI_boolean_trimesh(bti.input(), nullptr, BOOLEAN_UNION); - EXPECT_EQ(out->vert_len, 14); - EXPECT_EQ(out->tri_len, 24); + TriMesh out = boolean_trimesh(bti.trimesh, BOOLEAN_UNION, 1, [](int){return 0;}); + EXPECT_EQ(out.vert.size(), 14); + EXPECT_EQ(out.tri.size(), 24); if (DO_OBJ) { - write_obj(out, "cubetet_union"); + write_obj_trimesh(out.vert, out.tri, "cubetet_union"); } - BLI_boolean_trimesh_free(out); } TEST(eboolean, BinaryTetTet) { - const char *spec_a = R"(4 4 - 0.0 0.0 0.0 - 2.0 0.0 0.0 - 1.0 2.0 0.0 - 1.0 1.0 2.0 - 0 2 1 - 0 1 3 - 1 2 3 - 2 0 3 - )"; - const char *spec_b = R"(4 4 - 0.0 0.0 1.0 - 2.0 0.0 1.0 - 1.0 2.0 1.0 - 1.0 1.0 3.0 + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 0 2 1 0 1 3 1 2 3 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 )"; - BT_input bti_a(spec_a); - BT_input bti_b(spec_b); - Boolean_trimesh_output *out = BLI_boolean_trimesh(bti_a.input(), bti_b.input(), BOOLEAN_ISECT); - EXPECT_EQ(out->vert_len, 4); - EXPECT_EQ(out->tri_len, 4); + BT_input bti(spec); + TriMesh out = boolean_trimesh(bti.trimesh, BOOLEAN_ISECT, 2, [](int t){return t < 4 ? 0 : 1;}); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.tri.size(), 4); if (DO_OBJ) { - write_obj(out, "binary_tettet_isect"); + write_obj_trimesh(out.vert, out.tri, "binary_tettet_isect"); } - BLI_boolean_trimesh_free(out); } TEST(eboolean, PolyCubeCube) -- cgit v1.2.3