mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 10:33:20 +00:00
Vibe code an implementation that uses manifold
This commit is contained in:
@@ -74,6 +74,7 @@ option(BUILD_PACKAGE "" OFF)
|
||||
|
||||
option(WITH_OPENCASCADE "Enable geometry interpretation using Open CASCADE" ON)
|
||||
option(WITH_CGAL "Enable geometry interpretation using CGAL" ON)
|
||||
option(WITH_MANIFOLD "Enable geometry interpretation using Manifold" OFF)
|
||||
option(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON)
|
||||
option(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
|
||||
option(HDF5_SUPPORT "Enable HDF5 support (requires HDF5, zlib)" ON)
|
||||
@@ -215,6 +216,22 @@ if(BUILD_IFCGEOM AND WITH_OPENCASCADE)
|
||||
list(APPEND GEOMETRY_KERNELS opencascade)
|
||||
endif()
|
||||
|
||||
message(STATUS "BUILD_IFCGEOM WITH_MANIFOLD: ${BUILD_IFCGEOM} ${WITH_MANIFOLD}")
|
||||
|
||||
if(BUILD_IFCGEOM AND WITH_MANIFOLD)
|
||||
find_package(manifold CONFIG REQUIRED)
|
||||
if(TARGET manifold::manifold)
|
||||
set(MANIFOLD_LIBRARIES manifold::manifold)
|
||||
elseif(TARGET manifold)
|
||||
set(MANIFOLD_LIBRARIES manifold)
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to determine manifold target")
|
||||
endif()
|
||||
add_definitions(-DIFOPSH_WITH_MANIFOLD)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_MANIFOLD)
|
||||
list(APPEND GEOMETRY_KERNELS manifold)
|
||||
endif()
|
||||
|
||||
if(GLTF_SUPPORT)
|
||||
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
|
||||
if(NOT JSON_INCLUDE_DIR)
|
||||
|
||||
@@ -277,6 +277,9 @@ int main(int argc, char** argv) {
|
||||
std::string offset_str, rotation_str;
|
||||
|
||||
std::string default_kernel;
|
||||
#ifdef IFOPSH_WITH_MANIFOLD
|
||||
default_kernel = "manifold";
|
||||
#endif
|
||||
#ifdef IFOPSH_WITH_CGAL
|
||||
default_kernel = "cgal";
|
||||
#endif
|
||||
@@ -292,7 +295,7 @@ int main(int argc, char** argv) {
|
||||
po::options_description geom_options("Geometry options");
|
||||
geom_options.add_options()
|
||||
("kernel", po::value<std::string>(&geometry_kernel)->default_value(default_kernel),
|
||||
"Geometry kernel to use (opencascade, cgal, cgal-simple, hybrid-cgal-simple-opencascade).")
|
||||
"Geometry kernel to use (opencascade, cgal, cgal-simple, manifold, hybrid-cgal-simple-opencascade).")
|
||||
("threads,j", po::value<int>(&num_threads)->default_value(1),
|
||||
"Number of parallel processing threads for geometry interpretation.")
|
||||
("center-model",
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
#undef CgalKernel
|
||||
#endif
|
||||
|
||||
#ifdef IFOPSH_WITH_MANIFOLD
|
||||
#include "../ifcgeom/kernels/manifold/ManifoldKernel.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
inline bool is_valid_for_kernel(const ifcopenshell::geometry::kernels::AbstractKernel* k, const IfcGeom::ConversionResult& shp) {
|
||||
#ifdef IFOPSH_WITH_OPENCASCADE
|
||||
@@ -50,6 +54,11 @@ namespace {
|
||||
if (k->geometry_library() == "cgal") {
|
||||
return dynamic_cast<ifcopenshell::geometry::CgalShape*>(shp.Shape().get()) != nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef IFOPSH_WITH_MANIFOLD
|
||||
if (k->geometry_library() == "manifold") {
|
||||
return dynamic_cast<ifcopenshell::geometry::ManifoldShape*>(shp.Shape().get()) != nullptr;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -186,6 +195,12 @@ namespace ifcopenshell {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef IFOPSH_WITH_MANIFOLD
|
||||
if (geometry_library_lower == "manifold") {
|
||||
return std::make_unique<ManifoldKernel>(conv_settings);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (geometry_library_lower.rfind("hybrid-", 0) == 0) {
|
||||
geometry_library_lower = geometry_library_lower.substr(strlen("hybrid"));
|
||||
std::vector<std::unique_ptr<AbstractKernel>> kernels;
|
||||
@@ -213,6 +228,12 @@ namespace ifcopenshell {
|
||||
kernels.emplace_back(new CgalKernel(conv_settings));
|
||||
geometry_library_lower = geometry_library_lower.substr(strlen("cgal"));
|
||||
}
|
||||
#endif
|
||||
#ifdef IFOPSH_WITH_MANIFOLD
|
||||
if (geometry_library_lower.find("manifold", 0) == 0) {
|
||||
kernels.emplace_back(new ManifoldKernel(conv_settings));
|
||||
geometry_library_lower = geometry_library_lower.substr(strlen("manifold"));
|
||||
}
|
||||
#endif
|
||||
if (kernels.size() != n + 1) {
|
||||
throw ifcopenshell::exception("Invalid hybrid kernel " + geometry_library);
|
||||
@@ -236,4 +257,4 @@ namespace ifcopenshell {
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,534 @@
|
||||
#include "ManifoldConversionResult.h"
|
||||
|
||||
#include "../../../ifcgeom/IfcGeomRepresentation.h"
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
using IfcGeom::ConversionResultShape;
|
||||
using IfcGeom::NumberNativeDouble;
|
||||
using IfcGeom::OpaqueCoordinate;
|
||||
using IfcGeom::OpaqueNumber;
|
||||
|
||||
namespace {
|
||||
using Mesh = manifold::MeshGL64;
|
||||
|
||||
Mesh transform_mesh(const Mesh& mesh, const ifcopenshell::geometry::taxonomy::matrix4& place) {
|
||||
Mesh result = mesh;
|
||||
const auto& m = place.ccomponents();
|
||||
const bool flip = m.block<3, 3>(0, 0).determinant() < 0.;
|
||||
for (size_t i = 0; i < mesh.NumVert(); ++i) {
|
||||
Eigen::Vector4d v(
|
||||
mesh.vertProperties[i * mesh.numProp + 0],
|
||||
mesh.vertProperties[i * mesh.numProp + 1],
|
||||
mesh.vertProperties[i * mesh.numProp + 2],
|
||||
1.);
|
||||
auto v2 = m * v;
|
||||
result.vertProperties[i * result.numProp + 0] = v2(0);
|
||||
result.vertProperties[i * result.numProp + 1] = v2(1);
|
||||
result.vertProperties[i * result.numProp + 2] = v2(2);
|
||||
}
|
||||
if (flip) {
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
std::swap(result.triVerts[i * 3 + 1], result.triVerts[i * 3 + 2]);
|
||||
}
|
||||
}
|
||||
result.runTransform.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<manifold::Manifold> make_manifold(const Mesh& mesh) {
|
||||
manifold::Manifold solid(mesh);
|
||||
if (solid.Status() == manifold::Manifold::Error::NoError) {
|
||||
return solid;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
manifold::Box mesh_bbox(const Mesh& mesh) {
|
||||
if (!mesh.NumVert()) {
|
||||
return {};
|
||||
}
|
||||
manifold::Box box(
|
||||
manifold::vec3(
|
||||
mesh.vertProperties[0],
|
||||
mesh.vertProperties[1],
|
||||
mesh.vertProperties[2]),
|
||||
manifold::vec3(
|
||||
mesh.vertProperties[0],
|
||||
mesh.vertProperties[1],
|
||||
mesh.vertProperties[2]));
|
||||
for (size_t i = 1; i < mesh.NumVert(); ++i) {
|
||||
box.Union(manifold::vec3(
|
||||
mesh.vertProperties[i * mesh.numProp + 0],
|
||||
mesh.vertProperties[i * mesh.numProp + 1],
|
||||
mesh.vertProperties[i * mesh.numProp + 2]));
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
double bbox_volume(const manifold::Box& box) {
|
||||
if (!box.IsFinite()) {
|
||||
return 0.;
|
||||
}
|
||||
const auto size = box.Size();
|
||||
return size[0] * size[1] * size[2];
|
||||
}
|
||||
|
||||
double triangle_area(const Mesh& mesh, size_t tri) {
|
||||
auto idx = [&](int corner) { return mesh.triVerts[tri * 3 + corner]; };
|
||||
auto point = [&](uint32_t i) {
|
||||
return Eigen::Vector3d(
|
||||
mesh.vertProperties[i * mesh.numProp + 0],
|
||||
mesh.vertProperties[i * mesh.numProp + 1],
|
||||
mesh.vertProperties[i * mesh.numProp + 2]);
|
||||
};
|
||||
const auto a = point(idx(0));
|
||||
const auto b = point(idx(1));
|
||||
const auto c = point(idx(2));
|
||||
return 0.5 * ((b - a).cross(c - a)).norm();
|
||||
}
|
||||
|
||||
double mesh_area(const Mesh& mesh) {
|
||||
double area = 0.;
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
area += triangle_area(mesh, i);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
double mesh_volume(const Mesh& mesh) {
|
||||
double volume = 0.;
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
auto idx = [&](int corner) { return mesh.triVerts[i * 3 + corner]; };
|
||||
auto point = [&](uint32_t v) {
|
||||
return Eigen::Vector3d(
|
||||
mesh.vertProperties[v * mesh.numProp + 0],
|
||||
mesh.vertProperties[v * mesh.numProp + 1],
|
||||
mesh.vertProperties[v * mesh.numProp + 2]);
|
||||
};
|
||||
const auto a = point(idx(0));
|
||||
const auto b = point(idx(1));
|
||||
const auto c = point(idx(2));
|
||||
volume += a.dot(b.cross(c)) / 6.;
|
||||
}
|
||||
return std::abs(volume);
|
||||
}
|
||||
|
||||
struct EdgeHash {
|
||||
size_t operator()(const std::pair<uint32_t, uint32_t>& edge) const {
|
||||
return std::hash<uint64_t>()((uint64_t(edge.first) << 32) ^ uint64_t(edge.second));
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<std::pair<uint32_t, uint32_t>, int, EdgeHash> count_edges(const Mesh& mesh) {
|
||||
std::unordered_map<std::pair<uint32_t, uint32_t>, int, EdgeHash> edges;
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
uint32_t tri[3] = {
|
||||
mesh.triVerts[i * 3 + 0],
|
||||
mesh.triVerts[i * 3 + 1],
|
||||
mesh.triVerts[i * 3 + 2]
|
||||
};
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
auto a = tri[j];
|
||||
auto b = tri[(j + 1) % 3];
|
||||
if (a > b) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
edges[{a, b}]++;
|
||||
}
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
double mesh_length(const Mesh& mesh) {
|
||||
double length = 0.;
|
||||
auto edges = count_edges(mesh);
|
||||
for (const auto& pair : edges) {
|
||||
const auto a = pair.first.first;
|
||||
const auto b = pair.first.second;
|
||||
Eigen::Vector3d p(
|
||||
mesh.vertProperties[a * mesh.numProp + 0],
|
||||
mesh.vertProperties[a * mesh.numProp + 1],
|
||||
mesh.vertProperties[a * mesh.numProp + 2]);
|
||||
Eigen::Vector3d q(
|
||||
mesh.vertProperties[b * mesh.numProp + 0],
|
||||
mesh.vertProperties[b * mesh.numProp + 1],
|
||||
mesh.vertProperties[b * mesh.numProp + 2]);
|
||||
length += (q - p).norm();
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
int mesh_edges(const Mesh& mesh) {
|
||||
return (int)count_edges(mesh).size();
|
||||
}
|
||||
|
||||
ifcopenshell::geometry::ManifoldPart make_part(const manifold::Manifold& solid) {
|
||||
return { solid.GetMeshGL64(), solid };
|
||||
}
|
||||
|
||||
ifcopenshell::geometry::ManifoldPart make_box_part(const manifold::Box& box) {
|
||||
const auto size = box.Size();
|
||||
auto solid = manifold::Manifold::Cube(manifold::vec3(size[0], size[1], size[2]), false).Translate(box.min);
|
||||
return make_part(solid);
|
||||
}
|
||||
}
|
||||
|
||||
ifcopenshell::geometry::ManifoldShape::ManifoldShape(const ManifoldPart& part)
|
||||
: parts_{ part } {}
|
||||
|
||||
ifcopenshell::geometry::ManifoldShape::ManifoldShape(ManifoldPart&& part)
|
||||
: parts_{ std::move(part) } {}
|
||||
|
||||
ifcopenshell::geometry::ManifoldShape::ManifoldShape(const std::vector<ManifoldPart>& parts)
|
||||
: parts_(parts) {}
|
||||
|
||||
ifcopenshell::geometry::ManifoldShape::ManifoldShape(std::vector<ManifoldPart>&& parts)
|
||||
: parts_(std::move(parts)) {}
|
||||
|
||||
std::optional<manifold::Manifold> ifcopenshell::geometry::ManifoldShape::as_manifold() const {
|
||||
if (parts_.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<manifold::Manifold> solids;
|
||||
solids.reserve(parts_.size());
|
||||
for (const auto& part : parts_) {
|
||||
if (!part.solid) {
|
||||
return std::nullopt;
|
||||
}
|
||||
solids.push_back(*part.solid);
|
||||
}
|
||||
if (solids.size() == 1) {
|
||||
return solids.front();
|
||||
}
|
||||
return manifold::Manifold::BatchBoolean(solids, manifold::OpType::Add);
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::ManifoldShape::Triangulate(ifcopenshell::geometry::Settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int item_id, int surface_style_id) const {
|
||||
for (const auto& part : parts_) {
|
||||
auto mesh = transform_mesh(part.mesh, place);
|
||||
std::vector<int> indices(mesh.NumVert());
|
||||
for (size_t i = 0; i < mesh.NumVert(); ++i) {
|
||||
indices[i] = t->addVertex(
|
||||
item_id,
|
||||
surface_style_id,
|
||||
mesh.vertProperties[i * mesh.numProp + 0],
|
||||
mesh.vertProperties[i * mesh.numProp + 1],
|
||||
mesh.vertProperties[i * mesh.numProp + 2]);
|
||||
}
|
||||
auto edges = count_edges(mesh);
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
t->addFace(
|
||||
item_id,
|
||||
surface_style_id,
|
||||
indices[mesh.triVerts[i * 3 + 0]],
|
||||
indices[mesh.triVerts[i * 3 + 1]],
|
||||
indices[mesh.triVerts[i * 3 + 2]]);
|
||||
}
|
||||
for (const auto& edge : edges) {
|
||||
if (edge.second == 1) {
|
||||
t->registerEdge(item_id, indices[edge.first.first], indices[edge.first.second]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::ManifoldShape::Serialize(const ifcopenshell::geometry::taxonomy::matrix4& place, std::string& result) const {
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(17);
|
||||
size_t offset = 0;
|
||||
for (const auto& part : parts_) {
|
||||
auto mesh = transform_mesh(part.mesh, place);
|
||||
for (size_t i = 0; i < mesh.NumVert(); ++i) {
|
||||
stream << "v "
|
||||
<< mesh.vertProperties[i * mesh.numProp + 0] << " "
|
||||
<< mesh.vertProperties[i * mesh.numProp + 1] << " "
|
||||
<< mesh.vertProperties[i * mesh.numProp + 2] << "\n";
|
||||
}
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
stream << "f "
|
||||
<< mesh.triVerts[i * 3 + 0] + 1 + offset << " "
|
||||
<< mesh.triVerts[i * 3 + 1] + 1 + offset << " "
|
||||
<< mesh.triVerts[i * 3 + 2] + 1 + offset << "\n";
|
||||
}
|
||||
offset += mesh.NumVert();
|
||||
}
|
||||
result = stream.str();
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::ManifoldShape::surface_genus() const {
|
||||
int genus = 0;
|
||||
for (const auto& part : parts_) {
|
||||
if (!part.solid) {
|
||||
return 0;
|
||||
}
|
||||
genus += part.solid->Genus();
|
||||
}
|
||||
return genus;
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::ManifoldShape::is_manifold() const {
|
||||
return std::all_of(parts_.begin(), parts_.end(), [](const auto& part) { return part.solid.has_value(); });
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::ManifoldShape::num_vertices() const {
|
||||
size_t total = 0;
|
||||
for (const auto& part : parts_) {
|
||||
total += part.mesh.NumVert();
|
||||
}
|
||||
return (int)total;
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::ManifoldShape::num_edges() const {
|
||||
int total = 0;
|
||||
for (const auto& part : parts_) {
|
||||
total += part.solid ? (int)part.solid->NumEdge() : mesh_edges(part.mesh);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
int ifcopenshell::geometry::ManifoldShape::num_faces() const {
|
||||
size_t total = 0;
|
||||
for (const auto& part : parts_) {
|
||||
total += part.solid ? part.solid->NumTri() : part.mesh.NumTri();
|
||||
}
|
||||
return (int)total;
|
||||
}
|
||||
|
||||
double ifcopenshell::geometry::ManifoldShape::bounding_box(void*& box_ptr) const {
|
||||
bool initialized = false;
|
||||
auto* box = static_cast<manifold::Box*>(box_ptr);
|
||||
if (!box) {
|
||||
box = new manifold::Box();
|
||||
box_ptr = box;
|
||||
}
|
||||
for (const auto& part : parts_) {
|
||||
auto bbox = part.solid ? part.solid->BoundingBox() : mesh_bbox(part.mesh);
|
||||
if (!bbox.IsFinite()) {
|
||||
continue;
|
||||
}
|
||||
if (!initialized) {
|
||||
*box = bbox;
|
||||
initialized = true;
|
||||
} else {
|
||||
box->Union(bbox.min);
|
||||
box->Union(bbox.max);
|
||||
}
|
||||
}
|
||||
return initialized ? bbox_volume(*box) : 0.;
|
||||
}
|
||||
|
||||
std::pair<OpaqueCoordinate<3>, OpaqueCoordinate<3>> ifcopenshell::geometry::ManifoldShape::bounding_box() const {
|
||||
void* box_ptr = nullptr;
|
||||
bounding_box(box_ptr);
|
||||
auto* box = static_cast<manifold::Box*>(box_ptr);
|
||||
if (!box || !box->IsFinite()) {
|
||||
delete box;
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
auto result = std::make_pair(
|
||||
OpaqueCoordinate<3>(
|
||||
new NumberNativeDouble(box->min[0]),
|
||||
new NumberNativeDouble(box->min[1]),
|
||||
new NumberNativeDouble(box->min[2])),
|
||||
OpaqueCoordinate<3>(
|
||||
new NumberNativeDouble(box->max[0]),
|
||||
new NumberNativeDouble(box->max[1]),
|
||||
new NumberNativeDouble(box->max[2])));
|
||||
delete box;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::ManifoldShape::set_box(void* box_ptr) {
|
||||
auto* box = static_cast<manifold::Box*>(box_ptr);
|
||||
if (!box || !box->IsFinite()) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
parts_ = { make_box_part(*box) };
|
||||
}
|
||||
|
||||
OpaqueNumber* ifcopenshell::geometry::ManifoldShape::length() {
|
||||
double total = 0.;
|
||||
for (const auto& part : parts_) {
|
||||
total += mesh_length(part.mesh);
|
||||
}
|
||||
return new NumberNativeDouble(total);
|
||||
}
|
||||
|
||||
OpaqueNumber* ifcopenshell::geometry::ManifoldShape::area() {
|
||||
double total = 0.;
|
||||
for (const auto& part : parts_) {
|
||||
total += part.solid ? part.solid->SurfaceArea() : mesh_area(part.mesh);
|
||||
}
|
||||
return new NumberNativeDouble(total);
|
||||
}
|
||||
|
||||
OpaqueNumber* ifcopenshell::geometry::ManifoldShape::volume() {
|
||||
double total = 0.;
|
||||
for (const auto& part : parts_) {
|
||||
total += part.solid ? part.solid->Volume() : mesh_volume(part.mesh);
|
||||
}
|
||||
return new NumberNativeDouble(total);
|
||||
}
|
||||
|
||||
OpaqueCoordinate<3> ifcopenshell::geometry::ManifoldShape::position() {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
|
||||
OpaqueCoordinate<3> ifcopenshell::geometry::ManifoldShape::axis() {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
|
||||
OpaqueCoordinate<4> ifcopenshell::geometry::ManifoldShape::plane_equation() {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
|
||||
std::vector<ConversionResultShape*> ifcopenshell::geometry::ManifoldShape::convex_decomposition() {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::halfspaces() {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::box() {
|
||||
void* box_ptr = nullptr;
|
||||
bounding_box(box_ptr);
|
||||
auto* box = static_cast<manifold::Box*>(box_ptr);
|
||||
if (!box || !box->IsFinite()) {
|
||||
delete box;
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
auto* result = new ManifoldShape(make_box_part(*box));
|
||||
delete box;
|
||||
return result;
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::solid() {
|
||||
if (!is_manifold()) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
return new ManifoldShape(parts_);
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::wrap_in_compound() {
|
||||
return new ManifoldShape(parts_);
|
||||
}
|
||||
|
||||
std::vector<ConversionResultShape*> ifcopenshell::geometry::ManifoldShape::vertices() {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
std::vector<ConversionResultShape*> ifcopenshell::geometry::ManifoldShape::edges() {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
std::vector<ConversionResultShape*> ifcopenshell::geometry::ManifoldShape::facets() {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::add(ConversionResultShape* other) {
|
||||
auto* rhs = dynamic_cast<ManifoldShape*>(other);
|
||||
if (!rhs) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
auto a = as_manifold();
|
||||
auto b = rhs->as_manifold();
|
||||
if (!a || !b) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
return new ManifoldShape(make_part(*a + *b));
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::subtract(ConversionResultShape* other) {
|
||||
auto* rhs = dynamic_cast<ManifoldShape*>(other);
|
||||
if (!rhs) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
auto a = as_manifold();
|
||||
auto b = rhs->as_manifold();
|
||||
if (!a || !b) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
return new ManifoldShape(make_part(*a - *b));
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::intersect(ConversionResultShape* other) {
|
||||
auto* rhs = dynamic_cast<ManifoldShape*>(other);
|
||||
if (!rhs) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
auto a = as_manifold();
|
||||
auto b = rhs->as_manifold();
|
||||
if (!a || !b) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
return new ManifoldShape(make_part(*a ^ *b));
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::concat(ConversionResultShape* other) {
|
||||
auto* rhs = dynamic_cast<ManifoldShape*>(other);
|
||||
if (!rhs) {
|
||||
throw std::runtime_error("Invalid shape");
|
||||
}
|
||||
auto parts = parts_;
|
||||
parts.insert(parts.end(), rhs->parts_.begin(), rhs->parts_.end());
|
||||
return new ManifoldShape(std::move(parts));
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::ManifoldShape::map(OpaqueCoordinate<4>&, OpaqueCoordinate<4>&) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
void ifcopenshell::geometry::ManifoldShape::map(const std::vector<OpaqueCoordinate<4>>&, const std::vector<OpaqueCoordinate<4>>&) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
ConversionResultShape* ifcopenshell::geometry::ManifoldShape::moved(ifcopenshell::geometry::taxonomy::matrix4::ptr place) const {
|
||||
std::vector<ManifoldPart> moved_parts;
|
||||
moved_parts.reserve(parts_.size());
|
||||
for (const auto& part : parts_) {
|
||||
auto mesh = transform_mesh(part.mesh, *place);
|
||||
auto solid = part.solid ? make_manifold(mesh) : std::nullopt;
|
||||
if (part.solid && !solid) {
|
||||
throw std::runtime_error("Failed to transform shape");
|
||||
}
|
||||
moved_parts.push_back({ std::move(mesh), std::move(solid) });
|
||||
}
|
||||
return new ManifoldShape(std::move(moved_parts));
|
||||
}
|
||||
|
||||
bool ifcopenshell::geometry::ManifoldShape::surface_area_along_direction(double, const ifcopenshell::geometry::taxonomy::matrix4::ptr& place, double& along_x, double& along_y, double& along_z) const {
|
||||
along_x = along_y = along_z = 0.;
|
||||
for (const auto& part : parts_) {
|
||||
auto mesh = transform_mesh(part.mesh, *place);
|
||||
for (size_t i = 0; i < mesh.NumTri(); ++i) {
|
||||
auto point = [&](uint32_t v) {
|
||||
return Eigen::Vector3d(
|
||||
mesh.vertProperties[v * mesh.numProp + 0],
|
||||
mesh.vertProperties[v * mesh.numProp + 1],
|
||||
mesh.vertProperties[v * mesh.numProp + 2]);
|
||||
};
|
||||
const auto a = point(mesh.triVerts[i * 3 + 0]);
|
||||
const auto b = point(mesh.triVerts[i * 3 + 1]);
|
||||
const auto c = point(mesh.triVerts[i * 3 + 2]);
|
||||
auto n = (b - a).cross(c - a);
|
||||
const auto norm = n.norm();
|
||||
if (norm < 1.e-12) {
|
||||
continue;
|
||||
}
|
||||
const auto area = 0.5 * norm;
|
||||
n /= norm;
|
||||
along_x += area * std::abs(n(0));
|
||||
along_y += area * std::abs(n(1));
|
||||
along_z += area * std::abs(n(2));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef IFCGEOMMANIFOLDREPRESENTATION_H
|
||||
#define IFCGEOMMANIFOLDREPRESENTATION_H
|
||||
|
||||
#include <manifold/manifold.h>
|
||||
|
||||
#include "../../../ifcgeom/ConversionResult.h"
|
||||
#include "../../../ifcgeom/kernels/ifc_geomlibrary_api.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace ifcopenshell {
|
||||
namespace geometry {
|
||||
|
||||
struct IFC_GEOMLIBRARY_API ManifoldPart {
|
||||
manifold::MeshGL64 mesh;
|
||||
std::optional<manifold::Manifold> solid;
|
||||
};
|
||||
|
||||
class IFC_GEOMLIBRARY_API ManifoldShape : public IfcGeom::ConversionResultShape {
|
||||
public:
|
||||
ManifoldShape() = default;
|
||||
explicit ManifoldShape(const ManifoldPart& part);
|
||||
explicit ManifoldShape(ManifoldPart&& part);
|
||||
explicit ManifoldShape(const std::vector<ManifoldPart>& parts);
|
||||
explicit ManifoldShape(std::vector<ManifoldPart>&& parts);
|
||||
|
||||
const std::vector<ManifoldPart>& parts() const { return parts_; }
|
||||
std::optional<manifold::Manifold> as_manifold() const;
|
||||
|
||||
virtual void Triangulate(ifcopenshell::geometry::Settings settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int item_id, int surface_style_id) const;
|
||||
virtual void Serialize(const ifcopenshell::geometry::taxonomy::matrix4& place, std::string&) const;
|
||||
|
||||
virtual int surface_genus() const;
|
||||
virtual bool is_manifold() const;
|
||||
|
||||
virtual int num_vertices() const;
|
||||
virtual int num_edges() const;
|
||||
virtual int num_faces() const;
|
||||
|
||||
virtual double bounding_box(void*&) const;
|
||||
virtual std::pair<IfcGeom::OpaqueCoordinate<3>, IfcGeom::OpaqueCoordinate<3>> bounding_box() const;
|
||||
virtual void set_box(void* b);
|
||||
|
||||
virtual IfcGeom::OpaqueNumber* length();
|
||||
virtual IfcGeom::OpaqueNumber* area();
|
||||
virtual IfcGeom::OpaqueNumber* volume();
|
||||
|
||||
virtual IfcGeom::OpaqueCoordinate<3> position();
|
||||
virtual IfcGeom::OpaqueCoordinate<3> axis();
|
||||
virtual IfcGeom::OpaqueCoordinate<4> plane_equation();
|
||||
|
||||
virtual std::vector<IfcGeom::ConversionResultShape*> convex_decomposition();
|
||||
virtual IfcGeom::ConversionResultShape* halfspaces();
|
||||
virtual IfcGeom::ConversionResultShape* box();
|
||||
virtual IfcGeom::ConversionResultShape* solid();
|
||||
virtual IfcGeom::ConversionResultShape* wrap_in_compound();
|
||||
|
||||
virtual std::vector<IfcGeom::ConversionResultShape*> vertices();
|
||||
virtual std::vector<IfcGeom::ConversionResultShape*> edges();
|
||||
virtual std::vector<IfcGeom::ConversionResultShape*> facets();
|
||||
|
||||
virtual IfcGeom::ConversionResultShape* add(IfcGeom::ConversionResultShape*);
|
||||
virtual IfcGeom::ConversionResultShape* subtract(IfcGeom::ConversionResultShape*);
|
||||
virtual IfcGeom::ConversionResultShape* intersect(IfcGeom::ConversionResultShape*);
|
||||
virtual IfcGeom::ConversionResultShape* concat(IfcGeom::ConversionResultShape*);
|
||||
|
||||
virtual void map(IfcGeom::OpaqueCoordinate<4>& from, IfcGeom::OpaqueCoordinate<4>& to);
|
||||
virtual void map(const std::vector<IfcGeom::OpaqueCoordinate<4>>& from, const std::vector<IfcGeom::OpaqueCoordinate<4>>& to);
|
||||
virtual IfcGeom::ConversionResultShape* moved(ifcopenshell::geometry::taxonomy::matrix4::ptr) const;
|
||||
|
||||
virtual bool surface_area_along_direction(double tol, const ifcopenshell::geometry::taxonomy::matrix4::ptr&, double& along_x, double& along_y, double& along_z) const;
|
||||
|
||||
private:
|
||||
std::vector<ManifoldPart> parts_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
#ifndef MANIFOLD_KERNEL_H
|
||||
#define MANIFOLD_KERNEL_H
|
||||
|
||||
#include <manifold/manifold.h>
|
||||
|
||||
#include "../../../ifcgeom/AbstractKernel.h"
|
||||
#include "../../../ifcgeom/kernels/ifc_geomlibrary_api.h"
|
||||
#include "../../../ifcgeom/kernels/manifold/ManifoldConversionResult.h"
|
||||
|
||||
namespace ifcopenshell {
|
||||
namespace geometry {
|
||||
namespace kernels {
|
||||
|
||||
class IFC_GEOMLIBRARY_API ManifoldKernel : public AbstractKernel {
|
||||
public:
|
||||
ManifoldKernel(const Settings& settings)
|
||||
: AbstractKernel("manifold", settings) {}
|
||||
|
||||
virtual AbstractKernel* clone() const {
|
||||
return new ManifoldKernel(settings());
|
||||
}
|
||||
|
||||
virtual bool supports_boolean_operations() const { return true; }
|
||||
|
||||
virtual bool convert_impl(const taxonomy::extrusion::ptr, IfcGeom::ConversionResults&);
|
||||
virtual bool convert_impl(const taxonomy::shell::ptr, IfcGeom::ConversionResults&);
|
||||
virtual bool convert_impl(const taxonomy::solid::ptr, IfcGeom::ConversionResults&);
|
||||
virtual bool convert_impl(const taxonomy::boolean_result::ptr, IfcGeom::ConversionResults&);
|
||||
|
||||
virtual bool convert_openings(const express::Base& entity, const std::vector<std::pair<taxonomy::ptr, ifcopenshell::geometry::taxonomy::matrix4>>& openings,
|
||||
const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+34
-1
@@ -200,7 +200,7 @@ IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
|
||||
echo PYTHONHOME=%PYTHONHOME%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
|
||||
)
|
||||
|
||||
goto :SWIG
|
||||
goto :manifold
|
||||
|
||||
:nuget
|
||||
set DEPENDENCY_NAME=nuget
|
||||
@@ -732,6 +732,39 @@ IF NOT %ERRORLEVEL%==0 GOTO :Error
|
||||
call :InstallCMakeProject "%DEPENDENCY_DIR%\%BUILD_DIR%" %BUILD_CFG%
|
||||
IF NOT %ERRORLEVEL%==0 GOTO :Error
|
||||
|
||||
:manifold
|
||||
set DEPENDENCY_NAME=manifold
|
||||
set MANIFOLD_VERSION=3.2.1
|
||||
set DEPENDENCY_DIR=%DEPS_DIR%\manifold-%MANIFOLD_VERSION%
|
||||
set DEPENDENCY_INSTALL_DIR=%INSTALL_DIR%\manifold-%MANIFOLD_VERSION%
|
||||
echo MANIFOLD_ROOT=%DEPENDENCY_INSTALL_DIR%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
|
||||
|
||||
IF EXIST "%DEPENDENCY_INSTALL_DIR%" (
|
||||
echo Found existing "%DEPENDENCY_INSTALL_DIR%", skipping
|
||||
goto :Eigen
|
||||
)
|
||||
|
||||
call :GitCloneAndCheckoutRevision https://github.com/elalish/manifold.git "%DEPENDENCY_DIR%" v%MANIFOLD_VERSION%
|
||||
IF NOT %ERRORLEVEL%==0 GOTO :Error
|
||||
cd "%DEPENDENCY_DIR%"
|
||||
git reset --hard
|
||||
|
||||
call :RunCMake -DCMAKE_INSTALL_PREFIX="%DEPENDENCY_INSTALL_DIR%" ^
|
||||
-DBUILD_SHARED_LIBS=OFF ^
|
||||
-DMANIFOLD_PAR=OFF ^
|
||||
-DMANIFOLD_CROSS_SECTION=OFF ^
|
||||
-DMANIFOLD_PYBIND=OFF ^
|
||||
-DMANIFOLD_JSBIND=OFF ^
|
||||
-DMANIFOLD_CBIND=OFF ^
|
||||
-DMANIFOLD_TEST=OFF ^
|
||||
-DMANIFOLD_EXPORT=OFF ^
|
||||
-DMANIFOLD_DOWNLOADS=OFF
|
||||
IF NOT %ERRORLEVEL%==0 GOTO :Error
|
||||
call :BuildSolution "%DEPENDENCY_DIR%\%BUILD_DIR%\manifold.sln" %BUILD_CFG%
|
||||
IF NOT %ERRORLEVEL%==0 GOTO :Error
|
||||
call :InstallCMakeProject "%DEPENDENCY_DIR%\%BUILD_DIR%" %BUILD_CFG%
|
||||
IF NOT %ERRORLEVEL%==0 GOTO :Error
|
||||
|
||||
:: :tbb
|
||||
:: set DEPENDENCY_NAME=tbb
|
||||
:: set DEPENDENCY_DIR=%DEPS_DIR%\tbb
|
||||
|
||||
Reference in New Issue
Block a user