diff --git a/src/cpp/test/web-ifc-test.cpp b/src/cpp/test/web-ifc-test.cpp index 2b0c59d4..cb24eafa 100644 --- a/src/cpp/test/web-ifc-test.cpp +++ b/src/cpp/test/web-ifc-test.cpp @@ -462,7 +462,7 @@ int main() // std::string content = ReadFile("C:/Users/qmoya/Desktop/MODELS/15.ifc"); // std::string content = ReadFile("C:/Users/qmoya/Desktop/MODELS/F_MA_160_ALT3.ifc"); // std::string content = ReadFile("C:/Users/qmoya/Desktop/MODELS/1256.ifc"); - std::string content = ReadFile("C:/Users/qmoya/Desktop/MODELS/15.ifc"); + std::string content = ReadFile("C:/Users/qmoya/Desktop/MODELS/muysimple.ifc"); struct LoaderSettings { @@ -501,7 +501,7 @@ int main() start = ms(); - SpecificLoadTest(loader, geometryLoader, 211736); + SpecificLoadTest(loader, geometryLoader, 3994); // auto meshes = LoadAllTest(loader, geometryLoader, -1); // auto rebars = GetAllRebars(loader, geometryLoader); // std::cout << GetLine(loader, 225) << std::endl; diff --git a/src/cpp/wasm/web-ifc-wasm.cpp b/src/cpp/wasm/web-ifc-wasm.cpp index 7653d224..1fd4f753 100644 --- a/src/cpp/wasm/web-ifc-wasm.cpp +++ b/src/cpp/wasm/web-ifc-wasm.cpp @@ -14,6 +14,7 @@ #include "../version.h" #include "../web-ifc/geometry/operations/bim-geometry/extrusion.h" #include "../web-ifc/geometry/operations/bim-geometry/sweep.h" +#include "../web-ifc/geometry/operations/bim-geometry/circularSweep.h" #include "../web-ifc/geometry/operations/bim-geometry/revolution.h" #include "../web-ifc/geometry/operations/bim-geometry/cylindricalRevolution.h" #include "../web-ifc/geometry/operations/bim-geometry/parabola.h" @@ -800,6 +801,12 @@ bimGeometry::Sweep CreateSweep() return bimGeometry::Sweep(); } +bimGeometry::CircularSweep CreateCircularSweep() +{ + return bimGeometry::CircularSweep(); +} + + bimGeometry::Revolve CreateRevolution() { return bimGeometry::Revolve(); @@ -842,8 +849,6 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("GetSweptDiskSolid", &webifc::geometry::IfcGeometry::GetSweptDiskSolid) ; - - emscripten::value_object("dvec4") .field("x", &glm::dvec4::x) .field("y", &glm::dvec4::y) @@ -962,12 +967,13 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("GetBuffers", &bimGeometry::AABB::GetBuffers) .function("SetValues", &bimGeometry::AABB::SetValues) ; - emscripten::class_("Extrusion") .constructor<>() .function("GetBuffers", &bimGeometry::Extrusion::GetBuffers) .function("SetValues", &bimGeometry::Extrusion::SetValues) + .function("SetHoles", &bimGeometry::Extrusion::SetHoles) + .function("ClearHoles", &bimGeometry::Extrusion::ClearHoles) ; emscripten::class_("Sweep") @@ -975,6 +981,12 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("GetBuffers", &bimGeometry::Sweep::GetBuffers) .function("SetValues", &bimGeometry::Sweep::SetValues) ; + + emscripten::class_("CircularSweep") + .constructor<>() + .function("GetBuffers", &bimGeometry::CircularSweep::GetBuffers) + .function("SetValues", &bimGeometry::CircularSweep::SetValues) + ; emscripten::class_("Revolution") .constructor<>() @@ -1015,6 +1027,7 @@ EMSCRIPTEN_BINDINGS(my_module) { emscripten::function("CreateAABB", &CreateAABB); emscripten::function("CreateExtrusion", &CreateExtrusion); emscripten::function("CreateSweep", &CreateSweep); + emscripten::function("CreateCircularSweep", &CreateCircularSweep); emscripten::function("CreateRevolution", &CreateRevolution); emscripten::function("CreateCylindricalRevolution", &CreateCylindricalRevolution); emscripten::function("CreateParabola", &CreateParabola); diff --git a/src/cpp/web-ifc/geometry/operations/bim-geometry/circularSweep.cpp b/src/cpp/web-ifc/geometry/operations/bim-geometry/circularSweep.cpp new file mode 100644 index 00000000..83252bc6 --- /dev/null +++ b/src/cpp/web-ifc/geometry/operations/bim-geometry/circularSweep.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include "circularSweep.h" +#include "epsilons.h" +#include "geometry.h" +#include "utils.h" + +using Vec = glm::dvec3; + +namespace bimGeometry { + + Buffers CircularSweep::GetBuffers() + { + Buffers buffers; + + // Canviar la funcio revolution + Geometry geom = SweepCircular(scaling, closed, profilePoints, radius, directrix, initialDirectrixNormal, rotate90); + + for (int r = 0; r < geom.numFaces; r++) + { + bimGeometry::Face f = geom.GetFace(r); + buffers.AddTri(geom.GetPoint(f.i0), geom.GetPoint(f.i1), geom.GetPoint(f.i2)); + } + + return buffers; + } + + void CircularSweep::SetValues(double scaling_, bool closed_, std::vector profilePoints_, double radius_, std::vector directrix_, std::vector initialDirectrixNormal_, bool rotate90_) { + profilePoints.clear(); + for (size_t i = 0; i + 2 < profilePoints_.size(); i += 3) { + profilePoints.emplace_back(profilePoints_[i], profilePoints_[i + 1], profilePoints_[i + 2]); + } + directrix.clear(); + for (size_t i = 0; i + 2 < directrix_.size(); i += 3) { + directrix.emplace_back(directrix_[i], directrix_[i + 1], directrix_[i + 2]); + } + initialDirectrixNormal = glm::dvec3(initialDirectrixNormal_[0], initialDirectrixNormal_[1], initialDirectrixNormal_[2]); + scaling = scaling_; + closed = closed_; + radius = radius_; + rotate90 = rotate90_; + } +} diff --git a/src/cpp/web-ifc/geometry/operations/bim-geometry/circularSweep.h b/src/cpp/web-ifc/geometry/operations/bim-geometry/circularSweep.h new file mode 100644 index 00000000..0fd23635 --- /dev/null +++ b/src/cpp/web-ifc/geometry/operations/bim-geometry/circularSweep.h @@ -0,0 +1,26 @@ +#include +#include +#include +#include "buffers.h" + +using Vec = glm::dvec3; + +#pragma once + +namespace bimGeometry { + + struct CircularSweep + { + double scaling; + bool closed; + std::vector profilePoints; + double radius; + std::vector directrix; + glm::dvec3 initialDirectrixNormal; + bool rotate90; + + + void SetValues(double scaling_, bool closed_, std::vector profilePoints_, double radius_, std::vector directrix_, std::vector initialDirectrixNormal_, bool rotate90_); + Buffers GetBuffers(); + }; +} \ No newline at end of file diff --git a/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.cpp b/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.cpp index ca2cb3f2..d2558f97 100644 --- a/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.cpp +++ b/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.cpp @@ -14,7 +14,28 @@ namespace bimGeometry { { Buffers buffers; - Geometry geom = Extrude(profile, dir, len); + Geometry geom; + if(!cap) + { + geom = Extrude(profile, dir, len); + } + else + { + uint32_t profileNumber = 1 + holes.size(); + std::vector> profiles(profileNumber); + + for (size_t i = 0; i < profile.size(); i++) { + profiles[0].emplace_back(profile[i].x, profile[i].y, profile[i].z); + } + + for (size_t i = 0; i < holes.size(); i++) { + for (size_t j = 0; j < holes[i].size(); j++) { + profiles[i + 1].emplace_back(holes[i][j].x, holes[i][j].y, holes[i][j].z); + } + } + + geom = Extrude(profiles, dir, len, cuttingPlaneNormal, cuttingPlanePos); + } for (int r = 0; r < geom.numFaces; r++) { @@ -25,7 +46,7 @@ namespace bimGeometry { return buffers; } - void Extrusion::SetValues(std::vector profile_, std::vector dir_, double len_) { + void Extrusion::SetValues(std::vector profile_, std::vector dir_, double len_, std::vector cuttingPlaneNormal_, std::vector cuttingPlanePos_, bool cap_) { profile.clear(); for (size_t i = 0; i + 2 < profile_.size(); i += 3) { profile.emplace_back(profile_[i], profile_[i + 1], profile_[i + 2]); @@ -34,5 +55,20 @@ namespace bimGeometry { dir = glm::dvec3(dir_[0], dir_[1], dir_[2]); } len = len_; + cap = cap_; + cuttingPlanePos = glm::dvec3(cuttingPlanePos_[0], cuttingPlanePos_[1], cuttingPlanePos_[2]); + cuttingPlaneNormal = glm::dvec3(cuttingPlaneNormal_[0], cuttingPlaneNormal_[1], cuttingPlaneNormal_[2]); + } + + void Extrusion::SetHoles(std::vector hole_) { + std::vector hole; + for (size_t i = 0; i + 2 < hole_.size(); i += 3) { + hole.emplace_back(hole_[i], hole_[i + 1], hole_[i + 2]); + } + holes.push_back(hole); + } + + void Extrusion::ClearHoles() { + holes.clear(); } } diff --git a/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.h b/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.h index 16e8cba0..e1bfb8fa 100644 --- a/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.h +++ b/src/cpp/web-ifc/geometry/operations/bim-geometry/extrusion.h @@ -11,11 +11,17 @@ namespace bimGeometry { struct Extrusion { + bool cap; double len; - glm::dvec3 dir; + glm::dvec3 dir; + glm::dvec3 cuttingPlanePos; + glm::dvec3 cuttingPlaneNormal; std::vector profile; + std::vector> holes; - void SetValues(std::vector profile_, std::vector dir_, double len_); + void SetValues(std::vector profile_, std::vector dir_, double len_, std::vector cuttingPlaneNormal_, std::vector cuttingPlanePos_, bool cap_); + void SetHoles(std::vector hole_); + void ClearHoles(); Buffers GetBuffers(); }; } \ No newline at end of file diff --git a/src/cpp/web-ifc/geometry/operations/bim-geometry/utils.h b/src/cpp/web-ifc/geometry/operations/bim-geometry/utils.h index c56f7307..afb7beae 100644 --- a/src/cpp/web-ifc/geometry/operations/bim-geometry/utils.h +++ b/src/cpp/web-ifc/geometry/operations/bim-geometry/utils.h @@ -1,9 +1,11 @@ +#include #include #include #include #include "geometry.h" #include "epsilons.h" #include "curve.h" +#include #pragma once @@ -92,6 +94,15 @@ namespace bimGeometry return (angle / (2 * CONST_PI)) * 360; } + inline bool GetWindingOfTriangle(const glm::dvec3 &a, const glm::dvec3 &b, const glm::dvec3 &c) + { + glm::dvec3 v12(b - a); + glm::dvec3 v13(c - a); + + glm::dvec3 norm = glm::normalize(glm::cross(v12, v13)); + return glm::dot(norm, glm::dvec3(0, 0, 1)) > 0.0; + } + inline Geometry Revolution(glm::dmat4 transform, double startDegrees, double endDegrees, std::vector Profile, double numRots) { Geometry geometry; @@ -533,6 +544,153 @@ namespace bimGeometry return geom; } + inline bimGeometry::Geometry Extrude(std::vector> profile, glm::dvec3 dir, double distance, glm::dvec3 cuttingPlaneNormal = glm::dvec3(0), glm::dvec3 cuttingPlanePos = glm::dvec3(0)) + { + bimGeometry::Geometry geom; + std::vector holesIndicesHash; + + // check if first point is equal to last point, otherwise the outer loop of the shape is not closed + glm::dvec3 lastToFirstPoint = profile[0].front() - profile[0].back(); + if (glm::length(lastToFirstPoint) > 1e-8) { + profile[0].push_back(profile[0].front()); + } + + // build the caps + { + using Point = std::array; + int polygonCount = profile.size(); // Main profile + holes + std::vector> polygon(polygonCount); + + glm::dvec3 normal = dir; + + for (size_t i = 0; i < profile[0].size(); i++) + { + glm::dvec2 pt = profile[0][i]; + glm::dvec4 et = glm::dvec4(glm::dvec3(pt, 0) + dir * distance, 1); + + geom.AddPoint(et, normal); + polygon[0].push_back(Point{pt.x, pt.y}); + } + + for (size_t i = 0; i < profile[0].size(); i++) + { + holesIndicesHash.push_back(false); + } + + for (size_t i = 1; i < profile.size(); i++) + { + std::vector hole = profile[i]; + int pointCount = hole.size(); + + for (int j = 0; j < pointCount; j++) + { + holesIndicesHash.push_back(j == 0); + + glm::dvec3 pt = hole[j]; + glm::dvec4 et = glm::dvec4(pt + dir * distance, 1); + + profile[0].push_back(pt); + geom.AddPoint(et, normal); + polygon[i].push_back({pt.x, pt.y}); // Index 0 is main profile; see earcut reference + } + } + + std::vector indices = mapbox::earcut(polygon); + + if (indices.size() < 3) + { + return geom; + } + + uint32_t offset = 0; + bool winding = GetWindingOfTriangle(geom.GetPoint(offset + indices[0]), geom.GetPoint(offset + indices[1]), geom.GetPoint(offset + indices[2])); + bool flipWinding = !winding; + + for (size_t i = 0; i < indices.size(); i += 3) + { + if (flipWinding) + { + geom.AddFace(offset + indices[i + 0], offset + indices[i + 2], offset + indices[i + 1]); + } + else + { + geom.AddFace(offset + indices[i + 0], offset + indices[i + 1], offset + indices[i + 2]); + } + } + + offset += geom.numPoints; + + normal = -dir; + + for (size_t i = 0; i < profile[0].size(); i++) + { + glm::dvec2 pt = profile[0][i]; + glm::dvec4 et = glm::dvec4(glm::dvec3(pt, 0), 1); + + if (cuttingPlaneNormal != glm::dvec3(0)) + { + et = glm::dvec4(glm::dvec3(pt, 0), 1); + glm::dvec3 transDir = glm::dvec4(dir, 0); + + // project {et} onto the plane, following the extrusion normal + double ldotn = glm::dot(transDir, cuttingPlaneNormal); + if (ldotn == 0) + { + + } + else + { + glm::dvec3 dpos = cuttingPlanePos - glm::dvec3(et); + double dist = glm::dot(dpos, cuttingPlaneNormal) / ldotn; + // we want to apply dist, even when negative + et = et + glm::dvec4(dist * transDir, 1); + } + } + + geom.AddPoint(et, normal); + } + + for (size_t i = 0; i < indices.size(); i += 3) + { + if (flipWinding) + { + geom.AddFace(offset + indices[i + 0], offset + indices[i + 1], offset + indices[i + 2]); + } + else + { + geom.AddFace(offset + indices[i + 0], offset + indices[i + 2], offset + indices[i + 1]); + } + } + } + + uint32_t capSize = profile[0].size(); + for (size_t i = 1; i < capSize; i++) + { + // https://github.com/tomvandig/web-ifc/issues/5 + if (holesIndicesHash[i]) + { + continue; + } + + uint32_t bl = i - 1; + uint32_t br = i - 0; + + uint32_t tl = capSize + i - 1; + uint32_t tr = capSize + i - 0; + + // this winding should be correct + geom.AddFace(geom.GetPoint(tl), + geom.GetPoint(br), + geom.GetPoint(bl)); + + geom.AddFace(geom.GetPoint(tl), + geom.GetPoint(tr), + geom.GetPoint(br)); + } + + return geom; + } + inline glm::dvec3 projectOntoPlane(const glm::dvec3 &origin, const glm::dvec3 &normal, const glm::dvec3 &point, const glm::dvec3 &dir) { // project {et} onto the plane, following the extrusion normal @@ -549,15 +707,6 @@ namespace bimGeometry } } - inline bool GetWindingOfTriangle(const glm::dvec3 &a, const glm::dvec3 &b, const glm::dvec3 &c) - { - glm::dvec3 v12(b - a); - glm::dvec3 v13(c - a); - - glm::dvec3 norm = glm::normalize(glm::cross(v12, v13)); - return glm::dot(norm, glm::dvec3(0, 0, 1)) > 0.0; - } - //! This implementation generates much more vertices than needed, and does not have smoothed normals // TODO: Review rotate90 value, as it should be inferred from IFC but the source data had not been identified yet // An arbitrary value has been added in IFCSURFACECURVESWEPTAREASOLID but this is a bad solution @@ -757,4 +906,293 @@ namespace bimGeometry return geom; } + + inline Geometry SweepCircular(const double scaling, const bool closed, const std::vector &profile, const double radius, const std::vector &directrix, const glm::dvec3 &initialDirectrixNormal = glm::dvec3(0), const bool rotate90 = false) + { + Geometry geom; + + std::vector> dpts; + + // Remove repeated points + for (size_t i = 0; i < directrix.size(); i++) + { + if (i < directrix.size() - 1) + { + if (glm::distance(directrix[i], directrix[i + 1]) > EPS_BIG2 * scaling) + { + dpts.push_back(directrix[i]); + } + } + else + { + dpts.push_back(directrix[i]); + } + } + + if (closed) + { + glm::vec<3, glm::f64> dirStart = dpts[dpts.size() - 2] - dpts[dpts.size() - 1]; + glm::vec<3, glm::f64> dirEnd = dpts[1] - dpts[0]; + std::vector> newDpts; + newDpts.push_back(dpts[0] + dirStart); + for (size_t i = 0; i < dpts.size(); i++) + { + newDpts.push_back(dpts[i]); + } + newDpts.push_back(dpts[dpts.size() - 1] + dirEnd); + dpts = newDpts; + } + + if (dpts.size() <= 1) + { + // nothing to sweep + return geom; + } + + // compute curve for each part of the directrix + std::vector> curves; + std::vector transforms; + + for (size_t i = 0; i < dpts.size(); i++) + { + std::vector segmentForCurve; + + glm::dvec3 directrix2; + glm::dvec3 planeNormal; + glm::dvec3 directrixSegmentNormal; + glm::dvec3 planeOrigin; + + if (i == 0) // start + { + planeNormal = glm::normalize(dpts[1] - dpts[0]); + directrixSegmentNormal = planeNormal; + planeOrigin = dpts[0]; + directrix2 = planeNormal; + } + else if (i == dpts.size() - 1) // end + { + planeNormal = glm::normalize(dpts[i] - dpts[i - 1]); + directrixSegmentNormal = planeNormal; + planeOrigin = dpts[i]; + directrix2 = planeNormal; + } + else // middle + { + // possibly the directrix is bad + glm::dvec3 n1 = glm::normalize(dpts[i] - dpts[i - 1]); + glm::dvec3 n2 = glm::normalize(dpts[i + 1] - dpts[i]); + glm::dvec3 p = glm::normalize(glm::cross(n1, n2)); + directrix2 = -n1; + + // double prod = glm::dot(n1, n2); + + if (std::isnan(p.x)) + { + // TODO: sometimes outliers cause the perp to become NaN! + // this is bad news, as it nans the points added to the final mesh + // also, it's hard to bail out now :/ + // see curve.add() for more info on how this is currently "solved" + } + + glm::dvec3 u1 = glm::normalize(glm::cross(n1, p)); + glm::dvec3 u2 = glm::normalize(glm::cross(n2, p)); + + // TODO: When n1 and n2 have similar direction but opposite side... + // ... projection tend to infinity. -> glm::dot(n1, n2) + // I implemented a bad solution to prevent projection to infinity + if (glm::dot(n1, n2) < -0.9) + { + n2 = -n2; + u2 = -u2; + } + + glm::dvec3 au = glm::normalize(u1 + u2); + planeNormal = glm::normalize(glm::cross(au, p)); + directrixSegmentNormal = n1; // n1 or n2 doesn't matter + + planeOrigin = dpts[i]; + } + + glm::dvec3 dz = glm::normalize(directrix2); + glm::dvec3 dx = glm::dvec3(1, 0, 0); + glm::dvec3 dy = glm::dvec3(0, 1, 0); + + double parallelZ = glm::abs(glm::dot(dz, glm::dvec3(0, 0, 1))); + + if(parallelZ > 1 - EPS_BIG2) + { + dx = glm::normalize(glm::cross(dz, glm::dvec3(0, 1, 0))); + } else { + dx = glm::normalize(glm::cross(dz, glm::dvec3(0, 0, 1))); + } + + dy = glm::normalize(glm::cross(dz, dx)); + + glm::dmat4 profileScale = glm::dmat4( + glm::dvec4(dx * radius, 0), + glm::dvec4(dy * radius, 0), + glm::dvec4(dz, 0), + glm::dvec4(planeOrigin, 1)); + + transforms.push_back(profileScale); + + if (curves.empty()) + { + // construct initial curve + glm::dvec3 left; + glm::dvec3 right; + if (initialDirectrixNormal == glm::dvec3(0)) + { + left = glm::cross(directrixSegmentNormal, glm::dvec3(directrixSegmentNormal.y, directrixSegmentNormal.x, directrixSegmentNormal.z)); + if (left == glm::dvec3(0, 0, 0)) + { + left = glm::cross(directrixSegmentNormal, glm::dvec3(directrixSegmentNormal.x, directrixSegmentNormal.z, directrixSegmentNormal.y)); + } + if (left == glm::dvec3(0, 0, 0)) + { + left = glm::cross(directrixSegmentNormal, glm::dvec3(directrixSegmentNormal.z, directrixSegmentNormal.y, directrixSegmentNormal.x)); + } + right = glm::normalize(glm::cross(directrixSegmentNormal, left)); + left = glm::normalize(glm::cross(directrixSegmentNormal, right)); + } + else + { + left = glm::cross(directrixSegmentNormal, initialDirectrixNormal); + glm::dvec3 side = glm::normalize(initialDirectrixNormal); + right = glm::normalize(glm::cross(directrixSegmentNormal, left)); + left = glm::normalize(glm::cross(directrixSegmentNormal, right)); + right *= side; + } + + if (left == glm::dvec3(0, 0, 0)) + { + } + + // project profile onto planeNormal, place on planeOrigin + // TODO: look at holes + auto &ppts = profile; + for (auto &pt2D : ppts) + { + glm::dvec3 pt = -pt2D.x * left + -pt2D.y * right + planeOrigin; + if(rotate90) + { + pt = -pt2D.x * right - pt2D.y * left + planeOrigin; + } + glm::dvec3 proj = bimGeometry::projectOntoPlane(planeOrigin, planeNormal, pt, directrixSegmentNormal); + + segmentForCurve.push_back(proj); + } + } + else + { + // project previous curve onto the normal + const std::vector &prevCurve = curves.back(); + + auto &ppts = prevCurve; + for (auto &pt : ppts) + { + glm::dvec3 proj = bimGeometry::projectOntoPlane(planeOrigin, planeNormal, pt, directrixSegmentNormal); + + segmentForCurve.push_back(proj); + } + } + + if (!closed || (i != 0 && i != dpts.size() - 1)) + { + curves.push_back(segmentForCurve); + } + } + + if (closed) + { + dpts.pop_back(); + dpts.erase(dpts.begin()); + } + + // connect the curves + for (size_t i = 1; i < dpts.size(); i++) + { + glm::dvec3 p1 = dpts[i - 1]; + glm::dvec3 p2 = dpts[i]; + glm::dvec3 dir = p1 - p2; + glm::dvec4 ddir = glm::dvec4(dir, 0); + const double di = glm::distance(p1, p2); + + //Only segments smaller than 10 cm will be represented, those that are bigger will be standardized + + const auto &c1 = curves[i - 1]; + const auto &c2 = curves[i]; + + uint32_t capSize = c1.size(); + for (size_t j = 1; j < capSize; j++) + { + glm::dvec3 bl = c1[j - 1]; + glm::dvec3 br = c1[j - 0]; + glm::dvec3 tl = c2[j - 1]; + glm::dvec3 tr = c2[j - 0]; + + geom.AddFace(tl, br, bl); + geom.AddFace(tl, tr, br); + } + } + + return geom; + } + + inline Geometry SectionedSurface(std::vector> profiles) + { + Geometry geom; + + // Iterate over each profile, and create a surface by connecting the corresponding points with faces. + for (size_t i = 0; i < profiles.size() - 1; i++) + { + std::vector &profile1 = profiles[i]; + std::vector &profile2 = profiles[i + 1]; + + // Check that the profiles have the same number of points + if (profile1.size() != profile2.size()) + { + } + + std::vector indices; + + // Create faces by connecting corresponding points from the two profiles + for (size_t j = 0; j < profile1.size(); j++) + { + glm::dvec3 &p1 = profile1[j]; + int j2 = 0; + if (profile1.size() > 1) + { + double pr = (double)j / (double)(profile1.size() - 1); + j2 = pr * (profile2.size() - 1); + } + glm::dvec3 &p2 = profile2[j2]; + + glm::dvec3 normal = glm::dvec3(0.0, 0.0, 1.0); + + if (glm::distance(p1, p2) > 1E-5) + { + normal = glm::normalize(glm::cross(p2 - p1, glm::cross(p2 - p1, glm::dvec3(0.0, 0.0, 1.0)))); + } + + geom.AddPoint(p1, normal); + geom.AddPoint(p2, normal); + + indices.push_back(geom.numPoints - 2); + indices.push_back(geom.numPoints - 1); + } + + // Create the faces + if (indices.size() > 0) + { + for (size_t j = 0; j < indices.size() - 2; j += 4) + { + geom.AddFace(indices[j], indices[j + 1], indices[j + 2]); + geom.AddFace(indices[j + 2], indices[j + 1], indices[j + 3]); + } + } + } + + return geom; + } } \ No newline at end of file diff --git a/src/cpp/web-ifc/geometry/operations/geometryutils.h b/src/cpp/web-ifc/geometry/operations/geometryutils.h index 748d7f79..22636c88 100644 --- a/src/cpp/web-ifc/geometry/operations/geometryutils.h +++ b/src/cpp/web-ifc/geometry/operations/geometryutils.h @@ -39,6 +39,16 @@ namespace webifc::geometry ifcGeom.indexData = geom.indexData; ifcGeom.numPoints = geom.numPoints; ifcGeom.numFaces = geom.numFaces; + if(ifcGeom.planeData.size() != ifcGeom.numFaces) + { + for (size_t i = 0; i < ifcGeom.numFaces; i++) + { + if(i >= ifcGeom.planeData.size()) + { + ifcGeom.planeData.push_back(-1); + } + } + } return ifcGeom; } @@ -51,10 +61,9 @@ namespace webifc::geometry inline IfcGeometry SweepCircular(const double scaling, const bool closed, const IfcProfile &profile, const double radius, const IfcCurve &directrix, const glm::dvec3 &initialDirectrixNormal = glm::dvec3(0), const bool rotate90 = false) { spdlog::debug("[SweepCircular({})]"); - IfcGeometry geom; - - std::vector> dpts; + std::vector profile_vector; + std::vector directrix_vector; // Remove repeated points for (size_t i = 0; i < directrix.points.size(); i++) { @@ -62,230 +71,22 @@ namespace webifc::geometry { if (glm::distance(directrix.points[i], directrix.points[i + 1]) > EPS_BIG2 * scaling) { - dpts.push_back(directrix.points[i]); + directrix_vector.push_back(directrix.points[i]); } } else { - dpts.push_back(directrix.points[i]); + directrix_vector.push_back(directrix.points[i]); } } - if (closed) - { - glm::vec<3, glm::f64> dirStart = dpts[dpts.size() - 2] - dpts[dpts.size() - 1]; - glm::vec<3, glm::f64> dirEnd = dpts[1] - dpts[0]; - std::vector> newDpts; - newDpts.push_back(dpts[0] + dirStart); - for (size_t i = 0; i < dpts.size(); i++) - { - newDpts.push_back(dpts[i]); - } - newDpts.push_back(dpts[dpts.size() - 1] + dirEnd); - dpts = newDpts; + auto &ppts = profile.curve.points; + for (auto &pt2D : ppts) + { + profile_vector.push_back(pt2D); } - if (dpts.size() <= 1) - { - // nothing to sweep - return geom; - } - - // compute curve for each part of the directrix - std::vector curves; - std::vector transforms; - - for (size_t i = 0; i < dpts.size(); i++) - { - IfcCurve segmentForCurve; - - glm::dvec3 directrix2; - glm::dvec3 planeNormal; - glm::dvec3 directrixSegmentNormal; - glm::dvec3 planeOrigin; - - if (i == 0) // start - { - planeNormal = glm::normalize(dpts[1] - dpts[0]); - directrixSegmentNormal = planeNormal; - planeOrigin = dpts[0]; - directrix2 = planeNormal; - } - else if (i == dpts.size() - 1) // end - { - planeNormal = glm::normalize(dpts[i] - dpts[i - 1]); - directrixSegmentNormal = planeNormal; - planeOrigin = dpts[i]; - directrix2 = planeNormal; - } - else // middle - { - // possibly the directrix is bad - glm::dvec3 n1 = glm::normalize(dpts[i] - dpts[i - 1]); - glm::dvec3 n2 = glm::normalize(dpts[i + 1] - dpts[i]); - glm::dvec3 p = glm::normalize(glm::cross(n1, n2)); - directrix2 = -n1; - - // double prod = glm::dot(n1, n2); - - if (std::isnan(p.x)) - { - // TODO: sometimes outliers cause the perp to become NaN! - // this is bad news, as it nans the points added to the final mesh - // also, it's hard to bail out now :/ - // see curve.add() for more info on how this is currently "solved" -#if defined(_DEBUG) - printf("NaN perp!\n"); -#endif - } - - glm::dvec3 u1 = glm::normalize(glm::cross(n1, p)); - glm::dvec3 u2 = glm::normalize(glm::cross(n2, p)); - - // TODO: When n1 and n2 have similar direction but opposite side... - // ... projection tend to infinity. -> glm::dot(n1, n2) - // I implemented a bad solution to prevent projection to infinity - if (glm::dot(n1, n2) < -0.9) - { - n2 = -n2; - u2 = -u2; - } - - glm::dvec3 au = glm::normalize(u1 + u2); - planeNormal = glm::normalize(glm::cross(au, p)); - directrixSegmentNormal = n1; // n1 or n2 doesn't matter - - planeOrigin = dpts[i]; - } - - glm::dvec3 dz = glm::normalize(directrix2); - glm::dvec3 dx = glm::dvec3(1, 0, 0); - glm::dvec3 dy = glm::dvec3(0, 1, 0); - - double parallelZ = glm::abs(glm::dot(dz, glm::dvec3(0, 0, 1))); - - if(parallelZ > 1 - EPS_BIG2) - { - dx = glm::normalize(glm::cross(dz, glm::dvec3(0, 1, 0))); - } else { - dx = glm::normalize(glm::cross(dz, glm::dvec3(0, 0, 1))); - } - - dy = glm::normalize(glm::cross(dz, dx)); - - glm::dmat4 profileScale = glm::dmat4( - glm::dvec4(dx * radius, 0), - glm::dvec4(dy * radius, 0), - glm::dvec4(dz, 0), - glm::dvec4(planeOrigin, 1)); - - transforms.push_back(profileScale); - - if (curves.empty()) - { - // construct initial curve - glm::dvec3 left; - glm::dvec3 right; - if (initialDirectrixNormal == glm::dvec3(0)) - { - left = glm::cross(directrixSegmentNormal, glm::dvec3(directrixSegmentNormal.y, directrixSegmentNormal.x, directrixSegmentNormal.z)); - if (left == glm::dvec3(0, 0, 0)) - { - left = glm::cross(directrixSegmentNormal, glm::dvec3(directrixSegmentNormal.x, directrixSegmentNormal.z, directrixSegmentNormal.y)); - } - if (left == glm::dvec3(0, 0, 0)) - { - left = glm::cross(directrixSegmentNormal, glm::dvec3(directrixSegmentNormal.z, directrixSegmentNormal.y, directrixSegmentNormal.x)); - } - right = glm::normalize(glm::cross(directrixSegmentNormal, left)); - left = glm::normalize(glm::cross(directrixSegmentNormal, right)); - } - else - { - left = glm::cross(directrixSegmentNormal, initialDirectrixNormal); - glm::dvec3 side = glm::normalize(initialDirectrixNormal); - right = glm::normalize(glm::cross(directrixSegmentNormal, left)); - left = glm::normalize(glm::cross(directrixSegmentNormal, right)); - right *= side; - } - - if (left == glm::dvec3(0, 0, 0)) - { - printf("0 left vec in sweep!\n"); - } - - // project profile onto planeNormal, place on planeOrigin - // TODO: look at holes - auto &ppts = profile.curve.points; - for (auto &pt2D : ppts) - { - glm::dvec3 pt = -pt2D.x * left + -pt2D.y * right + planeOrigin; - if(rotate90) - { - pt = -pt2D.x * right - pt2D.y * left + planeOrigin; - } - glm::dvec3 proj = bimGeometry::projectOntoPlane(planeOrigin, planeNormal, pt, directrixSegmentNormal); - - segmentForCurve.Add(proj); - } - } - else - { - // project previous curve onto the normal - const IfcCurve &prevCurve = curves.back(); - - auto &ppts = prevCurve.points; - for (auto &pt : ppts) - { - glm::dvec3 proj = bimGeometry::projectOntoPlane(planeOrigin, planeNormal, pt, directrixSegmentNormal); - - segmentForCurve.Add(proj); - } - } - - if (!closed || (i != 0 && i != dpts.size() - 1)) - { - curves.push_back(segmentForCurve); - } - } - - if (closed) - { - dpts.pop_back(); - dpts.erase(dpts.begin()); - } - - // connect the curves - for (size_t i = 1; i < dpts.size(); i++) - { - glm::dvec3 p1 = dpts[i - 1]; - glm::dvec3 p2 = dpts[i]; - glm::dvec3 dir = p1 - p2; - glm::dvec4 ddir = glm::dvec4(dir, 0); - const double di = glm::distance(p1, p2); - - //Only segments smaller than 10 cm will be represented, those that are bigger will be standardized - - const auto &c1 = curves[i - 1].points; - const auto &c2 = curves[i].points; - - uint32_t capSize = c1.size(); - for (size_t j = 1; j < capSize; j++) - { - glm::dvec3 bl = c1[j - 1]; - glm::dvec3 br = c1[j - 0]; - glm::dvec3 tl = c2[j - 1]; - glm::dvec3 tr = c2[j - 0]; - - geom.AddFace(tl, br, bl); - geom.AddFace(tl, tr, br); - } - } - - // DumpSVGCurve(directrix.points, glm::dvec3(), "directrix.html"); - // DumpIfcGeometry(geom, "sweep.obj"); - - return geom; + return ToIfcGeometry(bimGeometry::SweepCircular(scaling, closed, profile_vector, radius, directrix_vector, initialDirectrixNormal, rotate90)); } inline bool computeSafeNormal(const glm::dvec3 v1, const glm::dvec3 v2, const glm::dvec3 v3, glm::dvec3 &normal, double eps = 0) @@ -518,69 +319,29 @@ namespace webifc::geometry } } - inline IfcGeometry SectionedSurface(IfcCrossSections profiles) + inline IfcGeometry SectionedSurface(IfcCrossSections profiles_) { spdlog::debug("[SectionedSurface({})]"); - IfcGeometry geom; - - // Iterate over each profile, and create a surface by connecting the corresponding points with faces. - for (size_t i = 0; i < profiles.curves.size() - 1; i++) - { - IfcCurve &profile1 = profiles.curves[i]; - IfcCurve &profile2 = profiles.curves[i + 1]; - - // Check that the profiles have the same number of points - if (profile1.points.size() != profile2.points.size()) - { - spdlog::error("[SectionedSurface()] profiles must have the same number of points in SectionedSurface"); - } - - std::vector indices; - // Create faces by connecting corresponding points from the two profiles - for (size_t j = 0; j < profile1.points.size(); j++) - { - glm::dvec3 &p1 = profile1.points[j]; - int j2 = 0; - if (profile1.points.size() > 1) - { - double pr = (double)j / (double)(profile1.points.size() - 1); - j2 = pr * (profile2.points.size() - 1); - } - glm::dvec3 &p2 = profile2.points[j2]; - - glm::dvec3 normal = glm::dvec3(0.0, 0.0, 1.0); - - if (glm::distance(p1, p2) > 1E-5) - { - normal = glm::normalize(glm::cross(p2 - p1, glm::cross(p2 - p1, glm::dvec3(0.0, 0.0, 1.0)))); - } + std::vector> profiles; - geom.AddPoint(p1, normal); - geom.AddPoint(p2, normal); - - indices.push_back(geom.numPoints - 2); - indices.push_back(geom.numPoints - 1); - } - - // Create the faces - if (indices.size() > 0) + for (size_t i = 0; i < profiles_.curves.size(); i++) + { + std::vector profile; + for (size_t j = 0; j < profiles_.curves[i].points.size(); j++) { - for (size_t j = 0; j < indices.size() - 2; j += 4) - { - geom.AddFace(indices[j], indices[j + 1], indices[j + 2]); - geom.AddFace(indices[j + 2], indices[j + 1], indices[j + 3]); - } + profile.push_back({profiles_.curves[i].points[j].x, profiles_.curves[i].points[j].y, profiles_.curves[i].points[j].z}); } + profiles.push_back(profile); } - return geom; + return ToIfcGeometry(bimGeometry::SectionedSurface(profiles)); } inline IfcGeometry Extrude(IfcProfile profile, glm::dvec3 dir, double distance, glm::dvec3 cuttingPlaneNormal = glm::dvec3(0), glm::dvec3 cuttingPlanePos = glm::dvec3(0)) { spdlog::debug("[Extrude({})]"); - IfcGeometry geom; + std::vector holesIndicesHash; // check if first point is equal to last point, otherwise the outer loop of the shape is not closed @@ -589,144 +350,29 @@ namespace webifc::geometry profile.curve.points.push_back(profile.curve.points.front()); } - // build the caps + std::vector> profile_vector; + std::vector profile_contour; + for (size_t i = 0; i < profile.curve.points.size(); i++) { - using Point = std::array; - int polygonCount = 1 + profile.holes.size(); // Main profile + holes - std::vector> polygon(polygonCount); - - glm::dvec3 normal = dir; - - for (size_t i = 0; i < profile.curve.points.size(); i++) - { - glm::dvec2 pt = profile.curve.points[i]; - glm::dvec4 et = glm::dvec4(glm::dvec3(pt, 0) + dir * distance, 1); - - geom.AddPoint(et, normal); - polygon[0].push_back({pt.x, pt.y}); - } - - for (size_t i = 0; i < profile.curve.points.size(); i++) - { - holesIndicesHash.push_back(false); - } - - for (size_t i = 0; i < profile.holes.size(); i++) - { - IfcCurve hole = profile.holes[i]; - int pointCount = hole.points.size(); - - for (int j = 0; j < pointCount; j++) - { - holesIndicesHash.push_back(j == 0); - - glm::dvec2 pt = hole.points[j]; - glm::dvec4 et = glm::dvec4(glm::dvec3(pt, 0) + dir * distance, 1); - - profile.curve.Add(pt); - geom.AddPoint(et, normal); - polygon[i + 1].push_back({pt.x, pt.y}); // Index 0 is main profile; see earcut reference - } - } - - std::vector indices = mapbox::earcut(polygon); - - if (indices.size() < 3) - { - // probably a degenerate polygon - spdlog::error("[Extrude()] degenerate polygon in extrude"); - return geom; - } - - uint32_t offset = 0; - bool winding = bimGeometry::GetWindingOfTriangle(geom.GetPoint(offset + indices[0]), geom.GetPoint(offset + indices[1]), geom.GetPoint(offset + indices[2])); - bool flipWinding = !winding; - - for (size_t i = 0; i < indices.size(); i += 3) - { - if (flipWinding) - { - geom.AddFace(offset + indices[i + 0], offset + indices[i + 2], offset + indices[i + 1]); - } - else - { - geom.AddFace(offset + indices[i + 0], offset + indices[i + 1], offset + indices[i + 2]); - } - } - - offset += geom.numPoints; - - normal = -dir; - - for (size_t i = 0; i < profile.curve.points.size(); i++) - { - glm::dvec2 pt = profile.curve.points[i]; - glm::dvec4 et = glm::dvec4(glm::dvec3(pt, 0), 1); - - if (cuttingPlaneNormal != glm::dvec3(0)) - { - et = glm::dvec4(glm::dvec3(pt, 0), 1); - glm::dvec3 transDir = glm::dvec4(dir, 0); - - // project {et} onto the plane, following the extrusion normal - double ldotn = glm::dot(transDir, cuttingPlaneNormal); - if (ldotn == 0) - { - printf("0 direction in extrude\n"); - } - else - { - glm::dvec3 dpos = cuttingPlanePos - glm::dvec3(et); - double dist = glm::dot(dpos, cuttingPlaneNormal) / ldotn; - // we want to apply dist, even when negative - et = et + glm::dvec4(dist * transDir, 1); - } - } - - geom.AddPoint(et, normal); - } - - for (size_t i = 0; i < indices.size(); i += 3) - { - if (flipWinding) - { - geom.AddFace(offset + indices[i + 0], offset + indices[i + 1], offset + indices[i + 2]); - } - else - { - geom.AddFace(offset + indices[i + 0], offset + indices[i + 2], offset + indices[i + 1]); - } - } + profile_contour.push_back(profile.curve.points[i]); } - - uint32_t capSize = profile.curve.points.size(); - for (size_t i = 1; i < capSize; i++) + profile_vector.push_back(profile_contour); + for (size_t i = 0; i < profile.holes.size(); i++) { - // https://github.com/tomvandig/web-ifc/issues/5 - if (holesIndicesHash[i]) + std::vector hole_contour; + IfcCurve hole = profile.holes[i]; + int pointCount = hole.points.size(); + for (int j = 0; j < pointCount; j++) { - continue; + hole_contour.push_back(hole.points[j]); } - - uint32_t bl = i - 1; - uint32_t br = i - 0; - - uint32_t tl = capSize + i - 1; - uint32_t tr = capSize + i - 0; - - // this winding should be correct - geom.AddFace(geom.GetPoint(tl), - geom.GetPoint(br), - geom.GetPoint(bl)); - - geom.AddFace(geom.GetPoint(tl), - geom.GetPoint(tr), - geom.GetPoint(br)); + profile_vector.push_back(hole_contour); } - return geom; + return ToIfcGeometry(bimGeometry::Extrude(profile_vector, dir, distance, cuttingPlaneNormal, cuttingPlanePos)); } + // TODO: Send to bimGeometry inline double VectorToAngle2D(double x, double y) { double dd = sqrt(x * x + y * y); @@ -841,77 +487,42 @@ namespace webifc::geometry } } - inline void flattenRecursive(IfcComposedMesh &mesh, std::unordered_map &geometryMap, std::vector &geoms, glm::dmat4 mat) - { - glm::dmat4 newMat = mat * mesh.transformation; + inline void flattenRecursive(IfcComposedMesh &mesh, std::unordered_map &geometryMap, std::vector &geoms, glm::dmat4 mat) + { + glm::dmat4 newMat = mat * mesh.transformation; - bool transformationBreaksWinding = MatrixFlipsTriangles(newMat); + bool transformationBreaksWinding = MatrixFlipsTriangles(newMat); - auto geomIt = geometryMap.find(mesh.expressID); + auto geomIt = geometryMap.find(mesh.expressID); - if (geomIt != geometryMap.end()) - { - auto meshGeom = geomIt->second; + if (geomIt != geometryMap.end()) + { + auto meshGeom = geomIt->second; - if (meshGeom.part.size() > 0) + if (meshGeom.part.size() > 0) + { + for (uint32_t i = 0; i < meshGeom.part.size(); i++) { - for (uint32_t i = 0; i < meshGeom.part.size(); i++) - { - - IfcGeometry newMeshGeom = meshGeom.part[i]; - if (newMeshGeom.numFaces) - { - IfcGeometry newGeom; - newGeom.halfSpace = newMeshGeom.halfSpace; - if (newGeom.halfSpace) - { - newGeom.halfSpaceOrigin = newMat * glm::dvec4(newMeshGeom.halfSpaceOrigin, 1); - newGeom.halfSpaceX = newMat * glm::dvec4(newMeshGeom.halfSpaceX, 1); - newGeom.halfSpaceY = newMat * glm::dvec4(newMeshGeom.halfSpaceY, 1); - newGeom.halfSpaceZ = newMat * glm::dvec4(newMeshGeom.halfSpaceZ, 1); - } - - for (uint32_t i = 0; i < newMeshGeom.numFaces; i++) - { - Face f = newMeshGeom.GetFace(i); - glm::dvec3 a = newMat * glm::dvec4(newMeshGeom.GetPoint(f.i0), 1); - glm::dvec3 b = newMat * glm::dvec4(newMeshGeom.GetPoint(f.i1), 1); - glm::dvec3 c = newMat * glm::dvec4(newMeshGeom.GetPoint(f.i2), 1); - - if (transformationBreaksWinding) - { - newGeom.AddFace(b, a, c); - } - else - { - newGeom.AddFace(a, b, c); - } - } - geoms.push_back(newGeom); - } - } - } - else - { - if (meshGeom.numFaces) + IfcGeometry newMeshGeom = meshGeom.part[i]; + if (newMeshGeom.numFaces) { IfcGeometry newGeom; - newGeom.halfSpace = meshGeom.halfSpace; + newGeom.halfSpace = newMeshGeom.halfSpace; if (newGeom.halfSpace) { - newGeom.halfSpaceOrigin = newMat * glm::dvec4(meshGeom.halfSpaceOrigin, 1); - newGeom.halfSpaceX = newMat * glm::dvec4(meshGeom.halfSpaceX, 1); - newGeom.halfSpaceY = newMat * glm::dvec4(meshGeom.halfSpaceY, 1); - newGeom.halfSpaceZ = newMat * glm::dvec4(meshGeom.halfSpaceZ, 1); + newGeom.halfSpaceOrigin = newMat * glm::dvec4(newMeshGeom.halfSpaceOrigin, 1); + newGeom.halfSpaceX = newMat * glm::dvec4(newMeshGeom.halfSpaceX, 1); + newGeom.halfSpaceY = newMat * glm::dvec4(newMeshGeom.halfSpaceY, 1); + newGeom.halfSpaceZ = newMat * glm::dvec4(newMeshGeom.halfSpaceZ, 1); } - for (uint32_t i = 0; i < meshGeom.numFaces; i++) + for (uint32_t i = 0; i < newMeshGeom.numFaces; i++) { - Face f = meshGeom.GetFace(i); - glm::dvec3 a = newMat * glm::dvec4(meshGeom.GetPoint(f.i0), 1); - glm::dvec3 b = newMat * glm::dvec4(meshGeom.GetPoint(f.i1), 1); - glm::dvec3 c = newMat * glm::dvec4(meshGeom.GetPoint(f.i2), 1); + Face f = newMeshGeom.GetFace(i); + glm::dvec3 a = newMat * glm::dvec4(newMeshGeom.GetPoint(f.i0), 1); + glm::dvec3 b = newMat * glm::dvec4(newMeshGeom.GetPoint(f.i1), 1); + glm::dvec3 c = newMat * glm::dvec4(newMeshGeom.GetPoint(f.i2), 1); if (transformationBreaksWinding) { @@ -927,13 +538,48 @@ namespace webifc::geometry } } } - - for (auto &c : mesh.children) + else { - flattenRecursive(c, geometryMap, geoms, newMat); + if (meshGeom.numFaces) + { + IfcGeometry newGeom; + newGeom.halfSpace = meshGeom.halfSpace; + if (newGeom.halfSpace) + { + newGeom.halfSpaceOrigin = newMat * glm::dvec4(meshGeom.halfSpaceOrigin, 1); + newGeom.halfSpaceX = newMat * glm::dvec4(meshGeom.halfSpaceX, 1); + newGeom.halfSpaceY = newMat * glm::dvec4(meshGeom.halfSpaceY, 1); + newGeom.halfSpaceZ = newMat * glm::dvec4(meshGeom.halfSpaceZ, 1); + } + + for (uint32_t i = 0; i < meshGeom.numFaces; i++) + { + Face f = meshGeom.GetFace(i); + glm::dvec3 a = newMat * glm::dvec4(meshGeom.GetPoint(f.i0), 1); + glm::dvec3 b = newMat * glm::dvec4(meshGeom.GetPoint(f.i1), 1); + glm::dvec3 c = newMat * glm::dvec4(meshGeom.GetPoint(f.i2), 1); + + if (transformationBreaksWinding) + { + newGeom.AddFace(b, a, c); + } + else + { + newGeom.AddFace(a, b, c); + } + } + + geoms.push_back(newGeom); + } } } + for (auto &c : mesh.children) + { + flattenRecursive(c, geometryMap, geoms, newMat); + } + } + inline std::vector flatten(IfcComposedMesh &mesh, std::unordered_map &geometryMap, glm::dmat4 mat = glm::dmat4(1)) { std::vector geoms; diff --git a/src/cpp/web-ifc/geometry/operations/mesh_utils.h b/src/cpp/web-ifc/geometry/operations/mesh_utils.h index f5380539..a18dffec 100644 --- a/src/cpp/web-ifc/geometry/operations/mesh_utils.h +++ b/src/cpp/web-ifc/geometry/operations/mesh_utils.h @@ -435,6 +435,7 @@ namespace webifc::geometry } } + // TODO: sent to bimGeometry inline void TriangulateBspline(IfcGeometry &geometry, std::vector const& bounds, IfcSurface const& surface, double const scaling) { spdlog::debug("[TriangulateBspline({})]"); diff --git a/src/ts/web-ifc-api.ts b/src/ts/web-ifc-api.ts index dcd1e686..846a8999 100644 --- a/src/ts/web-ifc-api.ts +++ b/src/ts/web-ifc-api.ts @@ -162,7 +162,9 @@ export interface AABB { export interface Extrusion { GetBuffers(): Buffers; - SetValues(profile_: Array, dir_: Array, len_: number): void; + SetValues(profile_: Array, dir_: Array, len_: number, cuttingPlaneNormal_: Array, cuttingPlanePos_: Array, cap_: boolean): void; + SetHoles(profile_: Array): void; + ClearHoles(): void; } export interface Sweep { @@ -178,6 +180,19 @@ export interface Sweep { ): void; } +export interface CircularSweep { + GetBuffers(): Buffers; + SetValues( + scaling: number, + closed: boolean, + profile: Array, + radius: number, + directrix: Array, + initialNormal?: Array, + rotate90?: boolean, + ): void; +} + export interface Revolution { GetBuffers(): Buffers; SetValues(profile_: Array, transform_: Array, startDegrees_: number, endDegrees_: number, numRots_: number): void; @@ -523,6 +538,11 @@ export class IfcAPI { return this.wasmModule.CreateSweep(); } + CreateCircularSweep() + { + return this.wasmModule.CreateCircularSweep(); + } + CreateRevolution() { return this.wasmModule.CreateRevolution();