mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-28 15:53:00 +00:00
99c514828d
Addresses aothms's review comment on PR #8759: "I don't understand (or like...) why a convert_impl(const taxonomy::collection::ptr collection, ...) overload is necessary... this doesn't sound like something that every geometry kernel impl should handle by itself. Rather something like a reduce on Result in the AbstractKernel generic implementation that Concatenates the items internally instead of aggregating them into a vector." Removes OpenCascadeKernel::convert_impl(collection), the per-kernel override that special-cased a homogeneous taxonomy::point3 collection (e.g. a whole IfcCartesianPointList3D "point cloud") into a bulk fast path. The batching now lives once, generically, in AbstractKernel::convert_impl(collection): it still converts each child individually through the kernel's own convert_impl(point3), but folds the resulting shapes into a single result via a new generic ConversionResultShape::concat_many() instead of aggregating one ConversionResult per point into the vector. Any kernel that implements convert_impl(point3) benefits automatically, with no collection-level override of its own. concat_many() is a bulk sibling of the existing pairwise concat(): combining N shapes via repeated pairwise concat() is either quadratic in one direction (concat() classifies its receiver via is_compound_of_faces(), which does a full sub-tree scan; calling it on an ever-growing accumulator is O(n) per call) or produces an O(n) deep nested shape in the other direction (still O(n) receiver classification per call is avoided, but the resulting compound is n levels deep, making the *later* TopExp_Explorer traversal during triangulation O(n) per vertex on average, i.e. O(n^2) overall). concat_many() gives kernels a way to combine everything in one O(n) bulk operation instead. The default implementation (repeated concat(), for kernels that never exercise this path) preserves correctness; OpenCascadeShape::concat_many() overrides it with a single BRep_Builder pass building one flat compound, matching the original per-kernel fast path's performance exactly (verified by benchmark: ~0.7-0.8 us/point from 10k-100k points, flat, matching the original PR's own ~0.5-0.9 us/point claim). Ported onto a clean v0.8.0 base (the original prototype was built on top of Dion Moult's experimental ifcviewer-wgpu branch, PR #8759). Fixes an id() access bug introduced while rewriting point.cpp for the generic path: taxonomy::item::instance is a raw pointer on this base, so both the new collection reduction here and the point3 conversion in point.cpp need instance->as<IfcUtil::IfcBaseEntity>()->id(), the same pattern already used throughout the rest of this file and every other kernels/opencascade/*.cpp, not a direct instance->id()/.id() call. This contribution was produced with the assistance of an AI coding tool.
176 lines
8.1 KiB
C++
176 lines
8.1 KiB
C++
/********************************************************************************
|
|
* *
|
|
* This file is part of IfcOpenShell. *
|
|
* *
|
|
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the Lesser GNU General Public License as published by *
|
|
* the Free Software Foundation, either version 3.0 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* IfcOpenShell 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 *
|
|
* Lesser GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the Lesser GNU General Public License *
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
********************************************************************************/
|
|
|
|
#ifndef IFCGEOM_H
|
|
#define IFCGEOM_H
|
|
|
|
#include <cmath>
|
|
#include <array>
|
|
|
|
#include <gp_Pnt.hxx>
|
|
#include <gp_Vec.hxx>
|
|
#include <gp_Mat.hxx>
|
|
#include <gp_Mat2d.hxx>
|
|
#include <gp_GTrsf.hxx>
|
|
#include <gp_GTrsf2d.hxx>
|
|
#include <gp_Trsf.hxx>
|
|
#include <gp_Trsf2d.hxx>
|
|
#include <gp_Quaternion.hxx>
|
|
#include <TopoDS.hxx>
|
|
#include <TopoDS_Wire.hxx>
|
|
#include <TopoDS_Face.hxx>
|
|
#include <Geom_Curve.hxx>
|
|
#include <gp_Pln.hxx>
|
|
#include <BOPAlgo_Operation.hxx>
|
|
#include <BRep_Builder.hxx>
|
|
#include <BRepBuilderAPI_MakeEdge.hxx>
|
|
|
|
#include "../../../ifcgeom/AbstractKernel.h"
|
|
|
|
#include "../../../ifcgeom/IfcGeomElement.h"
|
|
#include "../../../ifcgeom/IfcGeomRepresentation.h"
|
|
#include "../../../ifcgeom/ConversionResult.h"
|
|
|
|
#include "../../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h"
|
|
|
|
#include "../../../ifcgeom/kernels/ifc_geomlibrary_api.h"
|
|
|
|
#include "../../../ifcgeom/taxonomy.h"
|
|
#include "../../../ifcgeom/ConversionSettings.h"
|
|
|
|
namespace {
|
|
template <typename Fn>
|
|
bool handle_occt_exception(Fn&& fn) {
|
|
try {
|
|
return std::forward<Fn>(fn)();
|
|
} catch (const Standard_Failure& e) {
|
|
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
|
throw std::runtime_error(e.GetMessageString());
|
|
} else {
|
|
throw std::runtime_error("Unknown error creating geometry");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace IfcGeom {
|
|
|
|
class IFC_GEOMLIBRARY_API OpenCascadeKernel : public ifcopenshell::geometry::kernels::AbstractKernel {
|
|
private:
|
|
|
|
/*
|
|
faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping
|
|
M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share
|
|
the same underlying edge but with orientation reversed. This then later speeds op the process of creating a
|
|
manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance
|
|
threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c).
|
|
*/
|
|
|
|
class faceset_helper {
|
|
private:
|
|
OpenCascadeKernel* kernel_;
|
|
std::set<int> duplicates_;
|
|
std::map<int, int> vertex_mapping_;
|
|
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
|
|
double eps_;
|
|
bool non_manifold_;
|
|
|
|
void loop_(const ifcopenshell::geometry::taxonomy::loop::ptr ps, const std::function<void(int, int, bool)>& callback);
|
|
public:
|
|
faceset_helper(OpenCascadeKernel* kernel, const ifcopenshell::geometry::taxonomy::shell::ptr l);
|
|
~faceset_helper();
|
|
|
|
bool non_manifold() const { return non_manifold_; }
|
|
bool& non_manifold() { return non_manifold_; }
|
|
double epsilon() const { return eps_; }
|
|
|
|
bool edge(int A, int B, TopoDS_Edge& e);
|
|
|
|
bool wire(const ifcopenshell::geometry::taxonomy::loop::ptr loop, TopoDS_Wire& wire);
|
|
bool wires(const ifcopenshell::geometry::taxonomy::loop::ptr loop, NCollection_List<TopoDS_Shape>& wires);
|
|
};
|
|
|
|
faceset_helper* faceset_helper_;
|
|
|
|
double precision_;
|
|
public:
|
|
OpenCascadeKernel(const ifcopenshell::geometry::Settings& settings, Logger& logger = Logger::Root())
|
|
: AbstractKernel("opencascade", settings, logger)
|
|
, faceset_helper_(nullptr)
|
|
, precision_(settings.get<ifcopenshell::geometry::settings::Precision>().get())
|
|
{}
|
|
|
|
virtual AbstractKernel* clone(Logger& logger) const {
|
|
return new OpenCascadeKernel(settings(), logger);
|
|
}
|
|
|
|
virtual bool supports_boolean_operations() const { return true; }
|
|
|
|
bool convert(const ifcopenshell::geometry::taxonomy::extrusion::ptr, TopoDS_Shape&);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::face::ptr, TopoDS_Shape&, bool reversed_surface = false);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::loop::ptr, TopoDS_Wire&);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::matrix4::ptr, gp_GTrsf&);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::shell::ptr, TopoDS_Shape&);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::solid::ptr, TopoDS_Shape&);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::loft::ptr, TopoDS_Shape&);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::bspline_surface::ptr bs, Handle(Geom_Surface) surf);
|
|
bool convert(const ifcopenshell::geometry::taxonomy::sweep_along_curve::ptr, TopoDS_Shape&);
|
|
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::edge::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::loop::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::face::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::solid::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::shell::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::extrusion::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::revolve::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::boolean_result::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::loft::ptr, IfcGeom::ConversionResults&);
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::sweep_along_curve::ptr, IfcGeom::ConversionResults&);
|
|
// Prototype for issue #134 / #1409 / #5218, see point.cpp. The bulk
|
|
// point-cloud fast path is generic infrastructure in
|
|
// AbstractKernel::convert_impl(collection), not a per-kernel override.
|
|
virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::point3::ptr, IfcGeom::ConversionResults&);
|
|
|
|
virtual bool convert_openings(const IfcUtil::IfcBaseEntity* entity, const std::vector<std::pair<ifcopenshell::geometry::taxonomy::ptr, ifcopenshell::geometry::taxonomy::matrix4>>& openings,
|
|
const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes);
|
|
virtual bool unify_shapes(const IfcGeom::ConversionResults& input, IfcGeom::ConversionResults& output);
|
|
|
|
typedef boost::variant<boost::blank, Handle(Geom_Curve), TopoDS_Wire> curve_creation_visitor_result_type;
|
|
curve_creation_visitor_result_type convert_curve(const ifcopenshell::geometry::taxonomy::ptr);
|
|
Handle(Geom_Surface) convert_surface(const ifcopenshell::geometry::taxonomy::ptr);
|
|
|
|
template <typename T, typename U>
|
|
static T convert_xyz(const U& u) {
|
|
const auto& vs = u.ccomponents();
|
|
return T(vs(0), vs(1), vs(2));
|
|
}
|
|
|
|
// @todo eliminate
|
|
template <typename T, typename U>
|
|
static T convert_xyz2(const U& vs) {
|
|
return T(vs(0), vs(1), vs(2));
|
|
}
|
|
};
|
|
|
|
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection);
|
|
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced);
|
|
|
|
}
|
|
#endif
|