diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93d0921..cf0ed8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: config: - { os: "ubuntu-20.04", build-dir: "build-linux" } - { os: "windows-2019", build-dir: "build-windows" } - - { os: "macos-11", build-dir: "build-macos" } + - { os: "macos-12", build-dir: "build-macos" } steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/wheel.yml b/.github/workflows/wheel.yml index d24dc34..980d627 100644 --- a/.github/workflows/wheel.yml +++ b/.github/workflows/wheel.yml @@ -11,8 +11,8 @@ jobs: config: - { os: "ubuntu-20.04", arch: "auto64" } - { os: "windows-2019", arch: "auto64" } - - { os: "macos-11", arch: "auto64" } - - { os: "macos-11", arch: "universal2" } + - { os: "macos-12", arch: "auto64" } + - { os: "macos-12", arch: "universal2" } steps: - uses: actions/checkout@v2 with: @@ -40,7 +40,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, windows-2019, macos-11] + os: [ubuntu-20.04, windows-2019, macos-12] python-version: ['3.6', '3.11'] steps: diff --git a/.gitignore b/.gitignore index 439e729..e2d7f98 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ build/* dist/* wheelhouse/* coacd.egg-info/* +*obj +*wrl +*ply +*DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1a52edb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,91 @@ +{ + "files.associations": { + "__bit_reference": "cpp", + "__bits": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "__verbose_abort": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "format": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ratio": "cpp", + "regex": "cpp", + "set": "cpp", + "span": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "variant": "cpp", + "vector": "cpp", + "*.tcc": "cpp", + "chrono": "cpp", + "compare": "cpp", + "concepts": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "source_location": "cpp", + "utility": "cpp", + "stop_token": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bd3ccd..d2685a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ else() endif() if (MSVC) + add_compile_options("/bigobj") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W0 -fpermissive -D_USE_MATH_DEFINES") set(CMAKE_SHARED_LIBRARY_PREFIX "lib") else() diff --git a/README.md b/README.md index fa662bc..d8f09e5 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Here is the description of the parameters (sorted by importance). * `-r/--resolution`: sampling resolution for Hausdorff distance calculation (1e3~1e4), default = 2000. * `--pca`: flag to enable PCA pre-processing, default = false. * `-k`: value of $k$ for R_v calculation, default = 0.3. +* `-am/--approximate-mode`: approximation shape type ("ch" for convex hulls, "box" for cubes), default = "ch". * `--seed`: random seed used for sampling, default = random(). An example of changing the parameters: diff --git a/main.cpp b/main.cpp index 7aaca22..f44a41d 100644 --- a/main.cpp +++ b/main.cpp @@ -66,6 +66,10 @@ int main(int argc, char *argv[]) { params.preprocess_mode = argv[i + 1]; } + if (strcmp(argv[i], "-am") == 0 || strcmp(argv[i], "--approximate-mode") == 0) + { + params.apx_mode = argv[i + 1]; + } if (strcmp(argv[i], "-pr") == 0 || strcmp(argv[i], "--prep-resolution") == 0) { sscanf(argv[i + 1], "%d", ¶ms.prep_resolution); diff --git a/public/coacd.cpp b/public/coacd.cpp index e0c78c5..f840ed5 100644 --- a/public/coacd.cpp +++ b/public/coacd.cpp @@ -16,7 +16,7 @@ std::vector CoACD(Mesh const &input, double threshold, int max_convex_hull, std::string preprocess_mode, int prep_resolution, int sample_resolution, int mcts_nodes, int mcts_iteration, int mcts_max_depth, - bool pca, bool merge, unsigned int seed) { + bool pca, bool merge, std::string apx_mode, unsigned int seed) { logger::info("threshold {}", threshold); logger::info("max # convex hull {}", max_convex_hull); @@ -27,6 +27,7 @@ std::vector CoACD(Mesh const &input, double threshold, logger::info("mcts nodes {}", mcts_nodes); logger::info("mcts iterations {}", mcts_iteration); logger::info("merge {}", merge); + logger::info("approximate mode {}", apx_mode); logger::info("seed {}", seed); if (threshold < 0.01) { @@ -56,6 +57,7 @@ std::vector CoACD(Mesh const &input, double threshold, params.mcts_max_depth = mcts_max_depth; params.pca = pca; params.merge = merge; + params.apx_mode = apx_mode; params.seed = seed; Model m; @@ -127,7 +129,7 @@ CoACD_MeshArray CoACD_run(CoACD_Mesh const &input, double threshold, int prep_resolution, int sample_resolution, int mcts_nodes, int mcts_iteration, int mcts_max_depth, bool pca, bool merge, - unsigned int seed) { + int apx_mode, unsigned int seed) { coacd::Mesh mesh; for (uint64_t i = 0; i < input.vertices_count; ++i) { mesh.vertices.push_back({input.vertices_ptr[3 * i], @@ -140,7 +142,7 @@ CoACD_MeshArray CoACD_run(CoACD_Mesh const &input, double threshold, input.triangles_ptr[3 * i + 2]}); } - std::string pm; + std::string pm, apx; if (preprocess_mode == preprocess_on) { pm = "on"; } else if (preprocess_mode == preprocess_off) { @@ -149,9 +151,17 @@ CoACD_MeshArray CoACD_run(CoACD_Mesh const &input, double threshold, pm = "auto"; } + if (apx_mode == apx_ch) { + apx = "ch"; + } else if (apx_mode == apx_box) { + apx = "box"; + } else { + throw std::runtime_error("invalid approximation mode " + std::to_string(apx_mode)); + } + auto meshes = coacd::CoACD(mesh, threshold, max_convex_hull, pm, prep_resolution, sample_resolution, mcts_nodes, - mcts_iteration, mcts_max_depth, pca, merge, seed); + mcts_iteration, mcts_max_depth, pca, merge, apx, seed); CoACD_MeshArray arr; arr.meshes_ptr = new CoACD_Mesh[meshes.size()]; diff --git a/public/coacd.h b/public/coacd.h index 169623b..80e8571 100644 --- a/public/coacd.h +++ b/public/coacd.h @@ -22,7 +22,7 @@ std::vector CoACD(Mesh const &input, double threshold = 0.05, int prep_resolution = 50, int sample_resolution = 2000, int mcts_nodes = 20, int mcts_iteration = 150, int mcts_max_depth = 3, bool pca = false, - bool merge = true, unsigned int seed = 0); + bool merge = true, std::string apx_mode = "ch", unsigned int seed = 0); void set_log_level(std::string_view level); } // namespace coacd @@ -47,12 +47,15 @@ constexpr int preprocess_auto = 0; constexpr int preprocess_on = 1; constexpr int preprocess_off = 2; +constexpr int apx_ch = 0; +constexpr int apx_box = 1; + CoACD_MeshArray COACD_API CoACD_run(CoACD_Mesh const &input, double threshold, int max_convex_hull, int preprocess_mode, int prep_resolution, int sample_resolution, int mcts_nodes, int mcts_iteration, int mcts_max_depth, bool pca, bool merge, - unsigned int seed); + int apx_mode, unsigned int seed); void COACD_API CoACD_setLogLevel(char const *level); } diff --git a/python/package/__init__.py b/python/package/__init__.py index a00f414..db18fd2 100644 --- a/python/package/__init__.py +++ b/python/package/__init__.py @@ -55,6 +55,7 @@ class CoACD_MeshArray(ctypes.Structure): c_int, c_bool, c_bool, + c_int, c_uint, ] _lib.CoACD_run.restype = CoACD_MeshArray @@ -84,6 +85,7 @@ def run_coacd( mcts_max_depth: int = 3, pca: int = False, merge: bool = True, + apx_mode: str = "ch", seed: int = 0, ): vertices = np.ascontiguousarray(mesh.vertices, dtype=np.double) @@ -110,6 +112,11 @@ def run_coacd( else: pm = 0 + if apx_mode == "ch": + apx = 0 + elif apx_mode == "box": + apx = 1 + mesh_array = _lib.CoACD_run( mesh, threshold, @@ -122,6 +129,7 @@ def run_coacd( mcts_max_depth, pca, merge, + apx, seed, ) diff --git a/python/package/bin/coacd b/python/package/bin/coacd index 179642d..803bdd4 100644 --- a/python/package/bin/coacd +++ b/python/package/bin/coacd @@ -97,6 +97,13 @@ if __name__ == "__main__": action="store_true", help="Use PCA to align input mesh. Suitable for non-axis-aligned mesh.", ) + parser.add_argument( + "-am", + "--apx-mode", + type=str, + default="ch", + help="Approximation shape mode (ch/box).", + ) parser.add_argument("--seed", type=int, default=0, help="Random seed.") args = parser.parse_args() @@ -125,6 +132,7 @@ if __name__ == "__main__": mcts_max_depth=args.mcts_max_depth, pca=args.pca, merge=not args.no_merge, + apx_mode=args.apx_mode, seed=args.seed, ) mesh_parts = [] diff --git a/src/config.h b/src/config.h index e3166e8..ca6c1b3 100644 --- a/src/config.h +++ b/src/config.h @@ -35,6 +35,7 @@ namespace coacd bool merge; int max_convex_hull; double dmc_thres; + string apx_mode; /////////////// MCTS Config /////////////// int mcts_iteration; @@ -55,6 +56,7 @@ namespace coacd pca = false; merge = true; dmc_thres = 0.55; + apx_mode = "ch"; mcts_iteration = 150; mcts_max_depth = 3; diff --git a/src/io.cpp b/src/io.cpp index 259c018..da0aea1 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -20,6 +20,7 @@ namespace coacd logger::info("\tPCA (ON/OFF): {}", params.pca); logger::info("\tk for Rv: {}", params.rv_k); logger::info("\tHausdorff Sampling Resolution: {}", params.resolution); + logger::info("\tApproximation Mode (ch/box): {}", params.apx_mode); logger::info("\tRandom Seed: {}", params.seed); } diff --git a/src/mcts.cpp b/src/mcts.cpp index 9e0496d..f0a7626 100644 --- a/src/mcts.cpp +++ b/src/mcts.cpp @@ -55,7 +55,7 @@ namespace coacd ori_mesh_area = MeshArea(initial_part); ori_mesh_volume = MeshVolume(initial_part); Model ch; - initial_part.ComputeCH(ch); + initial_part.ComputeAPX(ch); ori_meshCH_volume = MeshVolume(ch); current_cost = 0; // accumulated score } @@ -72,7 +72,7 @@ namespace coacd ori_mesh_area = MeshArea(initial_part); ori_mesh_volume = MeshVolume(initial_part); Model ch; - initial_part.ComputeCH(ch); + initial_part.ComputeAPX(ch); ori_meshCH_volume = MeshVolume(ch); current_cost = 0; } @@ -153,8 +153,8 @@ namespace coacd _current_parts.push_back(current_parts[i]); } } - pos.ComputeCH(posCH); - neg.ComputeCH(negCH); + pos.ComputeAPX(posCH); + neg.ComputeAPX(negCH); double cost_pos = ComputeRv(pos, posCH, params.rv_k); double cost_neg = ComputeRv(neg, negCH, params.rv_k); Part part_pos(params, pos); @@ -269,7 +269,7 @@ namespace coacd double max_cost; Model ch; - m.ComputeCH(ch); + m.ComputeAPX(ch); Model pos, neg, posCH, negCH; flag = Clip(m, pos, neg, first_plane, tmp); @@ -278,8 +278,8 @@ namespace coacd final_cost = INF; return false; } - pos.ComputeCH(posCH); - neg.ComputeCH(negCH); + pos.ComputeAPX(posCH); + neg.ComputeAPX(negCH); double pos_cost = ComputeRv(pos, posCH, params.rv_k); double neg_cost = ComputeRv(neg, negCH, params.rv_k); scores.push_back(pos_cost); @@ -303,8 +303,8 @@ namespace coacd final_cost = INF; return false; } - _pos.ComputeCH(_posCH); - _neg.ComputeCH(_negCH); + _pos.ComputeAPX(_posCH); + _neg.ComputeAPX(_negCH); double _pos_cost = ComputeRv(_pos, _posCH, params.rv_k); double _neg_cost = ComputeRv(_neg, _negCH, params.rv_k); @@ -653,8 +653,8 @@ namespace coacd if (pos.points.size() <= 0 || neg.points.size() <= 0) continue; - pos.ComputeCH(posCH); - neg.ComputeCH(negCH); + pos.ComputeAPX(posCH); + neg.ComputeAPX(negCH); H = ComputeTotalRv(m, pos, posCH, neg, negCH, params.rv_k, planes[i]); } @@ -740,8 +740,8 @@ namespace coacd _current_parts.push_back(current_state.current_parts[i]); } } - pos.ComputeCH(posCH); - neg.ComputeCH(negCH); + pos.ComputeAPX(posCH); + neg.ComputeAPX(negCH); double cost_pos = ComputeRv(pos, posCH, params.rv_k); double cost_neg = ComputeRv(neg, negCH, params.rv_k); @@ -849,7 +849,7 @@ namespace coacd { int computation_budget = params.mcts_iteration; Model initial_mesh = node->get_state()->current_parts[0].current_mesh, initial_ch; - initial_mesh.ComputeCH(initial_ch); + initial_mesh.ComputeAPX(initial_ch); double cost = ComputeRv(initial_mesh, initial_ch, params.rv_k) / params.mcts_max_depth; vector current_path; diff --git a/src/model_obj.cpp b/src/model_obj.cpp index d166650..887d75a 100644 --- a/src/model_obj.cpp +++ b/src/model_obj.cpp @@ -101,7 +101,43 @@ namespace coacd } } - void Model::ComputeCH(Model &convex) + void Model::ComputeAPX(Model &convex, string apx_mode, bool if_vch) + { + if (apx_mode == "box") + ComputeBOX(convex); + else if (apx_mode == "ch" and !if_vch) + ComputeCH(convex); + else + ComputeVCH(convex); + } + + void Model::ComputeBOX(Model &convex) + { + // compute the box mesh according to the bounding box of the points + convex.points.push_back({bbox[1], bbox[2], bbox[5]}); + convex.points.push_back({bbox[1], bbox[3], bbox[5]}); + convex.points.push_back({bbox[0], bbox[3], bbox[5]}); + convex.points.push_back({bbox[0], bbox[2], bbox[5]}); + convex.points.push_back({bbox[1], bbox[2], bbox[4]}); + convex.points.push_back({bbox[1], bbox[3], bbox[4]}); + convex.points.push_back({bbox[0], bbox[3], bbox[4]}); + convex.points.push_back({bbox[0], bbox[2], bbox[4]}); + + convex.triangles.push_back({0, 1, 3}); + convex.triangles.push_back({1, 2, 3}); + convex.triangles.push_back({1, 4, 5}); + convex.triangles.push_back({0, 4, 1}); + convex.triangles.push_back({6, 5, 4}); + convex.triangles.push_back({4, 7, 6}); + convex.triangles.push_back({3, 2, 6}); + convex.triangles.push_back({6, 7, 3}); + convex.triangles.push_back({1, 6, 2}); + convex.triangles.push_back({5, 6, 1}); + convex.triangles.push_back({0, 3, 4}); + convex.triangles.push_back({3, 7, 4}); + } + + void Model::ComputeCH(Model &convex, bool if_vch) { /* fast convex hull algorithm */ bool flag = true; diff --git a/src/model_obj.h b/src/model_obj.h index d9d4b67..1190791 100644 --- a/src/model_obj.h +++ b/src/model_obj.h @@ -84,7 +84,9 @@ namespace coacd void ExtractPointSet(vector &samples, vector &sample_tri_ids, unsigned int seed = 1235, size_t resolution = 2000, double base = 0, bool flag = false, Plane plane = Plane(0, 0, 0, 0)); vector GetPoints(size_t resolution); double *GetBBox() { return bbox; } - void ComputeCH(Model &convex); + void ComputeAPX(Model &convex, string apx_mode = "ch", bool if_vch = false); + void ComputeBOX(Model &convex); + void ComputeCH(Model &convex, bool if_vch = false); void ComputeVCH(Model &convex); }; diff --git a/src/process.cpp b/src/process.cpp index 86ba731..36e5919 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -100,7 +100,7 @@ namespace coacd return true; } - void MergeCH(Model &ch1, Model &ch2, Model &ch) + void MergeCH(Model &ch1, Model &ch2, Model &ch, Params ¶ms) { Model merge; merge.points.insert(merge.points.end(), ch1.points.begin(), ch1.points.end()); @@ -109,7 +109,7 @@ namespace coacd for (int i = 0; i < (int)ch2.triangles.size(); i++) merge.triangles.push_back({int(ch2.triangles[i][0] + ch1.points.size()), int(ch2.triangles[i][1] + ch1.points.size()), int(ch2.triangles[i][2] + ch1.points.size())}); - merge.ComputeCH(ch); + merge.ComputeAPX(ch, params.apx_mode, true); } double MergeConvexHulls(Model &m, vector &meshs, vector &cvxs, Params ¶ms, double epsilon, double threshold) @@ -140,7 +140,7 @@ namespace coacd if (dist < threshold) { Model combinedCH; - MergeCH(cvxs[p1], cvxs[p2], combinedCH); + MergeCH(cvxs[p1], cvxs[p2], combinedCH, params); costMatrix[idx] = ComputeHCost(cvxs[p1], cvxs[p2], combinedCH, params.rv_k, params.resolution, params.seed); precostMatrix[idx] = max(ComputeHCost(meshs[p1], cvxs[p1], params.rv_k, 3000, params.seed), @@ -199,7 +199,7 @@ namespace coacd // Make the lowest cost row and column into a new hull Model cch; - MergeCH(cvxs[p1], cvxs[p2], cch); + MergeCH(cvxs[p1], cvxs[p2], cch, params); cvxs[p2] = cch; std::swap(cvxs[p1], cvxs[cvxs.size() - 1]); @@ -215,7 +215,7 @@ namespace coacd if (dist < threshold) { Model combinedCH; - MergeCH(cvxs[p2], cvxs[i], combinedCH); + MergeCH(cvxs[p2], cvxs[i], combinedCH, params); costMatrix[rowIdx] = ComputeHCost(cvxs[p2], cvxs[i], combinedCH, params.rv_k, params.resolution, params.seed); precostMatrix[rowIdx++] = max(precostMatrix[p2] + bestCost, precostMatrix[i]); } @@ -230,7 +230,7 @@ namespace coacd if (dist < threshold) { Model combinedCH; - MergeCH(cvxs[p2], cvxs[i], combinedCH); + MergeCH(cvxs[p2], cvxs[i], combinedCH, params); costMatrix[rowIdx] = ComputeHCost(cvxs[p2], cvxs[i], combinedCH, params.rv_k, params.resolution, params.seed); precostMatrix[rowIdx] = max(precostMatrix[p2] + bestCost, precostMatrix[i]); } @@ -310,7 +310,7 @@ namespace coacd Model pmesh = InputParts[p], pCH; Plane bestplane; - pmesh.ComputeVCH(pCH); + pmesh.ComputeAPX(pCH, params.apx_mode, true); double h = ComputeHCost(pmesh, pCH, params.rv_k, params.resolution, params.seed, 0.0001, false); if (h > params.threshold)