diff --git a/src/examples/faces.cpp b/src/examples/faces.cpp new file mode 100644 index 0000000000..5c765b860c --- /dev/null +++ b/src/examples/faces.cpp @@ -0,0 +1,176 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +/******************************************************************************** + * * + * Example that generates various forms of IfcFace * + * * + ********************************************************************************/ + +#include "../ifcparse/Ifc2x3.h" +#include "../ifcparse/IfcUtil.h" +#include "../ifcparse/IfcHierarchyHelper.h" + +typedef std::string S; +typedef IfcParse::IfcGlobalId guid; +boost::none_t const null = (static_cast(0)); + +static int x = 0; + +void create_testcase(IfcHierarchyHelper& file, IfcSchema::IfcFace* face, const std::string& name) { + IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list); + faces->push(face); + IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces); + + IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list); + shells->push(shell); + IfcSchema::IfcFaceBasedSurfaceModel* model = new IfcSchema::IfcFaceBasedSurfaceModel(shells); + + IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy( + guid(), 0, name, null, null, 0, 0, null, null); + file.addBuildingProduct(product); + product->setOwnerHistory(file.getSingle()); + + product->setObjectPlacement(file.addLocalPlacement(0, 1000 * x++, 0)); + + IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list); + IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list); + + items->push(model); + IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation( + file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items); + reps->push(rep); + + IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps); + file.addEntity(shape); + + product->setRepresentation(shape); +} + +int main(int argc, char** argv) { + IfcHierarchyHelper file; + { + IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list); + points->push(file.addTriplet(-400, -400, 0)); + points->push(file.addTriplet(+400, -400, 0)); + points->push(file.addTriplet(+400, +400, 0)); + points->push(file.addTriplet(-400, +400, 0)); + IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points); + IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true); + + IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list); + bounds->push(bound); + IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds); + create_testcase(file, face, "polyloop"); + } + { + IfcSchema::IfcCartesianPoint* point1 = file.addTriplet(+400, 0., 0.); + IfcSchema::IfcCartesianPoint* point2 = file.addTriplet(-400, 0., 0.); + IfcSchema::IfcVertexPoint* vertex1 = new IfcSchema::IfcVertexPoint(point1); + IfcSchema::IfcVertexPoint* vertex2 = new IfcSchema::IfcVertexPoint(point2); + IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(file.addPlacement2d(), 400.); + IfcSchema::IfcEdgeCurve* edge1 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, circle, true); + IfcSchema::IfcEdgeCurve* edge2 = new IfcSchema::IfcEdgeCurve(vertex2, vertex1, circle, true); + IfcSchema::IfcOrientedEdge* oriented_edge1 = new IfcSchema::IfcOrientedEdge(edge1, true); + IfcSchema::IfcOrientedEdge* oriented_edge2 = new IfcSchema::IfcOrientedEdge(edge2, true); + IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list); + edges->push(oriented_edge1); + edges->push(oriented_edge2); + IfcSchema::IfcEdgeLoop* loop = new IfcSchema::IfcEdgeLoop(edges); + IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true); + + IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list); + bounds->push(bound); + IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds); + create_testcase(file, face, "circle"); + } + { + IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list); + points->push(file.addTriplet(-400, -400, 0)); + points->push(file.addTriplet(+400, -400, 0)); + points->push(file.addTriplet(+400, +400, 0)); + points->push(file.addTriplet(-400, +400, 0)); + IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points); + IfcSchema::IfcFaceOuterBound* outer_bound = new IfcSchema::IfcFaceOuterBound(loop, true); + + IfcSchema::IfcCartesianPoint::list::ptr points2 (new IfcSchema::IfcCartesianPoint::list); + points2->push(file.addTriplet(-300, -300, 0)); + points2->push(file.addTriplet(-100, -300, 0)); + points2->push(file.addTriplet(-100, +300, 0)); + points2->push(file.addTriplet(-300, +300, 0)); + IfcSchema::IfcPolyLoop* loop2 = new IfcSchema::IfcPolyLoop(points2); + IfcSchema::IfcFaceBound* inner_bound1 = new IfcSchema::IfcFaceBound(loop2, false); + + IfcSchema::IfcCartesianPoint::list::ptr points3 (new IfcSchema::IfcCartesianPoint::list); + points3->push(file.addTriplet(+100, +300, 0)); + points3->push(file.addTriplet(+300, +300, 0)); + points3->push(file.addTriplet(+300, -300, 0)); + points3->push(file.addTriplet(+100, -300, 0)); + IfcSchema::IfcPolyLoop* loop3 = new IfcSchema::IfcPolyLoop(points3); + IfcSchema::IfcFaceBound* inner_bound2 = new IfcSchema::IfcFaceBound(loop3, true); + + IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list); + bounds->push(inner_bound1); + bounds->push(outer_bound); + bounds->push(inner_bound2); + IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds); + create_testcase(file, face, "polyloop with holes"); + } + { + IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list); + points->push(file.addTriplet(-400, -400, 0)); + points->push(file.addTriplet(-100, -400, 0)); + points->push(file.addTriplet(-100, +400, 0)); + points->push(file.addTriplet(-400, +400, 0)); + IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points); + IfcSchema::IfcFaceOuterBound* bound1 = new IfcSchema::IfcFaceOuterBound(loop, true); + + IfcSchema::IfcCartesianPoint::list::ptr points2 (new IfcSchema::IfcCartesianPoint::list); + points2->push(file.addTriplet(+100, +400, 0)); + points2->push(file.addTriplet(+400, +400, 0)); + points2->push(file.addTriplet(+400, -400, 0)); + points2->push(file.addTriplet(+100, -400, 0)); + IfcSchema::IfcPolyLoop* loop2 = new IfcSchema::IfcPolyLoop(points2); + IfcSchema::IfcFaceOuterBound* bound2 = new IfcSchema::IfcFaceOuterBound(loop2, false); + + IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list); + bounds->push(bound1); + bounds->push(bound2); + IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds); + create_testcase(file, face, "multiple outer boundaries (invalid)"); + } + { + IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list); + points->push(file.addTriplet(-400, -400, 1e-6)); + points->push(file.addTriplet(+400, -400, 0)); + points->push(file.addTriplet(+400, +400, 0)); + points->push(file.addTriplet(-400, +400, 0)); + IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points); + IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true); + + IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list); + bounds->push(bound); + IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds); + create_testcase(file, face, "imprecise polyloop"); + } + const std::string filename = "faces.ifc"; + file.header().file_name().name(filename); + std::ofstream f(filename.c_str()); + f << file; +} \ No newline at end of file diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 410345451a..c645788dd1 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -120,10 +120,6 @@ int main(int argc, char** argv) { "operand before applying the subtraction operation. This may " "introduce a performance improvement at the risk of failing, in " "which case the subtraction is applied one-by-one.") - ("force-ccw-face-orientation", - "Recompute topological face normals using Newell's Method to " - "guarantee that face vertices are defined in a Counter Clock " - "Wise order, even if the faces are not part of a closed shell.") ("disable-opening-subtractions", "Specifies whether to disable the boolean subtraction of " "IfcOpeningElement Representations from their RelatingElements.") @@ -173,7 +169,6 @@ int main(int argc, char** argv) { const bool convert_back_units = vmap.count("convert-back-units") != 0; const bool sew_shells = vmap.count("sew-shells") != 0; const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0; - const bool force_ccw_face_orientation = vmap.count("force-ccw-face-orientation") != 0; const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0; const bool include_entities = vmap.count("include") != 0; const bool include_plan = vmap.count("plan") != 0; @@ -240,7 +235,6 @@ int main(int argc, char** argv) { settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, sew_shells); settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, convert_back_units); settings.set(IfcGeom::IteratorSettings::FASTER_BOOLEANS, merge_boolean_operands); - settings.set(IfcGeom::IteratorSettings::FORCE_CCW_FACE_ORIENTATION, force_ccw_face_orientation); settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, disable_opening_subtractions); settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, include_plan); settings.set(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES, !include_model); diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 49a8bf8ef4..78b51bf33a 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -81,9 +81,6 @@ public: // that consist of many faces is really detrimental for the performance. // Default: 1000 GV_MAX_FACES_TO_SEW, - // By default singular faces have no explicitly defined orientation, to - // force faces to be defined CounterClockWise, set this value greater than zero. - GV_FORCE_CCW_FACE_ORIENTATION, // The length unit used the creation of TopoDS_Shapes, primarily affects the // interpretation of IfcCartesianPoints and IfcVector magnitudes // DefaultL 1.0 diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp index 1730e8ffd5..6d9a66d24e 100644 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ b/src/ifcgeom/IfcGeomFaces.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ #include #include #include +#include #include @@ -91,7 +93,6 @@ #include #include -#include #include #endif @@ -99,116 +100,181 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds(); - IfcSchema::IfcFaceBound::list::it it = bounds->begin(); - IfcSchema::IfcLoop* loop = (*it)->Bound(); - // FIXME: The assumption that the first bound is - // the outer bound is not necessarily correct. - TopoDS_Wire outer_wire; - if ( ! convert_wire(loop,outer_wire) ) return false; - BRepBuilderAPI_MakeFace mf (outer_wire); - BRepBuilderAPI_FaceError er = mf.Error(); - if ( er == BRepBuilderAPI_NotPlanar ) { - ShapeFix_ShapeTolerance FTol; - FTol.SetTolerance(outer_wire, 0.01, TopAbs_WIRE); - mf.~BRepBuilderAPI_MakeFace(); - new (&mf) BRepBuilderAPI_MakeFace(outer_wire); - er = mf.Error(); - } - if ( er != BRepBuilderAPI_FaceDone ) return false; - if ( bounds->size() == 1 ) { - face = mf.Face(); - } else { - for( ++it; it != bounds->end(); ++ it) { - IfcSchema::IfcLoop* loop = (*it)->Bound(); - TopoDS_Wire wire; - if ( ! convert_wire(loop,wire) ) return false; - mf.Add(wire); - } - if ( mf.IsDone() ) { - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - TopoDS_Shape sfs_shape = sfs.Shape(); - bool is_face = sfs_shape.ShapeType() == TopAbs_FACE; - // This assertion is not strictly necessary. The schema - // does suggest there to be only one outer bound, but - // not all models comply with this. - if ( is_face ) { - face = TopoDS::Face(sfs_shape); - } else { - face = sfs_shape; - } - } else { - return false; - } - } - if ( getValue(GV_FORCE_CCW_FACE_ORIENTATION)>0 ) { - // Check the orientation of the face by comparing the - // normal of the topological surface to the Newell's Method's - // normal. Newell's Method is used for the normal calculation - // as a simple edge cross product can give opposite results - // for a concave face boundary. - // Reference: Graphics Gems III p. 231 - BRepGProp_Face prop(TopoDS::Face(face)); - gp_Vec normal_direction; - gp_Pnt center; - double u1,u2,v1,v2; - prop.Bounds(u1,u2,v1,v2); - prop.Normal((u1+u2)/2.0,(v1+v2)/2.0,center,normal_direction); - gp_Dir face_normal1 = gp_Dir(normal_direction.XYZ()); + Handle(Geom_Surface) face_surface; + bool reversed_face_surface = false; + const bool is_face_surface = l->is(IfcSchema::Type::IfcFaceSurface); - double x = 0, y = 0, z = 0; - gp_Pnt current, previous, first; - int n = 0; - // Iterate over the vertices of the outer wire (discarding - // any potential holes) - for ( TopExp_Explorer exp(outer_wire,TopAbs_VERTEX);; exp.Next()) { - unsigned has_more = exp.More(); - if ( has_more ) { - const TopoDS_Vertex& v = TopoDS::Vertex(exp.Current()); - current = BRep_Tool::Pnt(v); - } else { - current = first; - } - if ( n ) { - const double& xn = previous.X(); - const double& yn = previous.Y(); - const double& zn = previous.Z(); - const double& xn1 = current.X(); - const double& yn1 = current.Y(); - const double& zn1 = current.Z(); - x += (yn-yn1)*(zn+zn1); - y += (xn+xn1)*(zn-zn1); - z += (xn-xn1)*(yn+yn1); - } else { - first = current; - } - if ( !has_more ) { - break; - } - previous = current; - ++n; - } + if (is_face_surface) { + IfcSchema::IfcFaceSurface* fs = (IfcSchema::IfcFaceSurface*) l; + fs->FaceSurface(); + // FIXME: Surfaces are interpreted as a TopoDS_Shape + TopoDS_Shape surface_shape; + if (!convert_shape(fs->FaceSurface(), surface_shape)) return false; - // If Newell's normal does not point in the same direction - // as the topological face normal the face orientation is - // reversed - gp_Vec face_normal2(x,y,z); + // FIXME: Assert this obtaines the only face + TopExp_Explorer exp(surface_shape, TopAbs_FACE); + if (!exp.More()) return false; - if (face_normal2.Magnitude() > ALMOST_ZERO) { - if ( face_normal1.Dot(face_normal2) < 0 ) { - TopAbs_Orientation o = face.Orientation(); - face.Orientation(o == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD); - } - } + TopoDS_Face surface = TopoDS::Face(exp.Current()); + face_surface = BRep_Tool::Surface(surface); } - // It might be a good idea to globally discard faces - // smaller than a certain treshold value. But for now - // only when processing IfcConnectedFacesets the small - // faces are skipped. - // return face_area(face) > 0.0001; - return true; + const int num_bounds = bounds->size(); + int num_outer_bounds = 0; + + for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { + IfcSchema::IfcFaceBound* bound = *it; + if (bound->is(IfcSchema::Type::IfcFaceOuterBound)) num_outer_bounds ++; + } + + // The number of outer bounds should be one according to the schema. Also Open Cascade + // expects this, but it is not strictly checked. Regardless, if the number is greater, + // the face will still be processed as long as there are no holes. A compound of faces + // is returned in that case. + if (num_bounds > 1 && num_outer_bounds > 1 && num_bounds != num_outer_bounds) { + Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", l->entity); + return false; + } + + TopoDS_Compound compound; + BRep_Builder builder; + if (num_outer_bounds > 1) { + builder.MakeCompound(compound); + } + + // The builder is initialized on the heap because of the various different moments + // of initialization depending on the configuration of surfaces and boundaries. + BRepBuilderAPI_MakeFace* mf = 0; + + bool success = false; + int processed = 0; + + for (int process_interior = 0; process_interior <= 1; ++process_interior) { + for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { + IfcSchema::IfcFaceBound* bound = *it; + IfcSchema::IfcLoop* loop = bound->Bound(); + + const bool same_sense = bound->Orientation(); + const bool is_interior = + !bound->is(IfcSchema::Type::IfcFaceOuterBound) && + (num_bounds > 1) && + (num_outer_bounds < num_bounds); + + // The exterior face boundary is processed first + if (is_interior == !process_interior) continue; + + TopoDS_Wire wire; + if (!convert_wire(loop, wire)) break; + + /* + The approach below does not result in a significant speed-up + if (loop->is(IfcSchema::Type::IfcPolyLoop) && processed == 0 && face_surface.IsNull()) { + IfcSchema::IfcPolyLoop* polyloop = (IfcSchema::IfcPolyLoop*) loop; + IfcSchema::IfcCartesianPoint::list::ptr points = polyloop->Polygon(); + + if (points->size() == 3) { + // Help Open Cascade by finding the plane more efficiently + IfcSchema::IfcCartesianPoint::list::it point_iterator = points->begin(); + gp_Pnt a, b, c; + convert(*point_iterator++, a); + convert(*point_iterator++, b); + convert(*point_iterator++, c); + const gp_XYZ ab = (b.XYZ() - a.XYZ()); + const gp_XYZ ac = (c.XYZ() - a.XYZ()); + const gp_Vec cross = ab.Crossed(ac); + if (cross.SquareMagnitude() > ALMOST_ZERO) { + const gp_Dir n = cross; + face_surface = new Geom_Plane(a, n); + } + } + } + */ + + if (!same_sense) { + wire.Reverse(); + } + + ShapeFix_ShapeTolerance FTol; + FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); + + if (!mf) { + if (face_surface.IsNull()) { + mf = new BRepBuilderAPI_MakeFace(wire); + } else { + mf = new BRepBuilderAPI_MakeFace(face_surface, wire); + } + + /* BRepBuilderAPI_FaceError er = mf->Error(); + if (er == BRepBuilderAPI_NotPlanar) { + ShapeFix_ShapeTolerance FTol; + FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); + delete mf; + mf = new BRepBuilderAPI_MakeFace(wire); + } */ + + if (mf->IsDone()) { + TopoDS_Face outer_face_bound = mf->Face(); + + // If the wires are reversed the face needs to be reversed as well in order + // to maintain the counter-clock-wise ordering of the bounding wire's vertices. + bool all_reversed = true; + TopoDS_Iterator it(outer_face_bound, false); + for (; it.More(); it.Next()) { + const TopoDS_Wire& w = TopoDS::Wire(it.Value()); + if ((w.Orientation() != TopAbs_REVERSED) == same_sense) { + all_reversed = false; + } + } + + if (all_reversed) { + outer_face_bound.Reverse(); + } + + if (num_outer_bounds > 1) { + builder.Add(compound, outer_face_bound); + delete mf; mf = 0; + } else if (num_bounds > 1) { + // Reinitialize the builder to the outer face + // bound in order to add holes more robustly. + delete mf; + mf = new BRepBuilderAPI_MakeFace(outer_face_bound); + } else { + face = outer_face_bound; + success = true; + } + } else { + break; + } + + } else { + mf->Add(wire); + } + processed ++; + } + } + + if (!success) { + success = processed == num_bounds; + if (success) { + if (num_outer_bounds > 1) { + face = compound; + } else { + success = success && mf->IsDone(); + if (success) { + face = mf->Face(); + } + } + } + } + + if (success) { + ShapeFix_ShapeTolerance FTol; + FTol.SetTolerance(face, getValue(GV_PRECISION), TopAbs_FACE); + } + + delete mf; + return success; } bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, TopoDS_Shape& face) { diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 317f2dd601..bfeac77032 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -503,7 +503,6 @@ static double point_equality_tolerance = 0.00001; static double max_faces_to_sew = -1.0; static double ifc_length_unit = 1.0; static double ifc_planeangle_unit = -1.0; -static double force_ccw_face_orientation = -1.0; static double modelling_precision = 0.00001; static double dimensionality = 1; @@ -530,9 +529,6 @@ void IfcGeom::Kernel::setValue(GeomValue var, double value) { case GV_PLANEANGLE_UNIT: ifc_planeangle_unit = value; break; - case GV_FORCE_CCW_FACE_ORIENTATION: - force_ccw_face_orientation = value; - break; case GV_PRECISION: modelling_precision = value; break; @@ -562,9 +558,6 @@ double IfcGeom::Kernel::getValue(GeomValue var) { case GV_PLANEANGLE_UNIT: return ifc_planeangle_unit; break; - case GV_FORCE_CCW_FACE_ORIENTATION: - return force_ccw_face_orientation; - break; case GV_PRECISION: return modelling_precision; break; diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index e0d51c22a8..64f1d89972 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -450,7 +450,6 @@ namespace IfcGeom { unit_magnitude = 1.f; kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.sew_shells() ? 1000 : -1); - kernel.setValue(IfcGeom::Kernel::GV_FORCE_CCW_FACE_ORIENTATION, settings.force_ccw_face_orientation() ? 1 : -1); kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.include_curves() ? (settings.exclude_solids_and_surfaces() ? -1. : 0.) : +1.)); } public: diff --git a/src/ifcgeom/IfcGeomIteratorSettings.h b/src/ifcgeom/IfcGeomIteratorSettings.h index 7b59ea8bbb..7adc5e9463 100644 --- a/src/ifcgeom/IfcGeomIteratorSettings.h +++ b/src/ifcgeom/IfcGeomIteratorSettings.h @@ -55,9 +55,6 @@ namespace IfcGeom { // Specifies whether to compose IfcOpeningElements into a single compound // in order to speed up the processing of opening subtractions. static const int FASTER_BOOLEANS = 6; - // By default singular faces have no explicitly defined orientation, to - // force faces to be defined CounterClockWise set this to true. - static const int FORCE_CCW_FACE_ORIENTATION = 7; // Disables the subtraction of IfcOpeningElement representations from // the related building element representations. static const int DISABLE_OPENING_SUBTRACTIONS = 8; @@ -74,7 +71,7 @@ namespace IfcGeom { // End of settings enumeration. private: - bool _weld_vertices, _use_world_coords, _convert_back_units, _use_brep_data, _sew_shells, _faster_booleans, _force_ccw_face_orientation, _disable_opening_subtractions, _disable_triangulation, _apply_default_materials, _include_curves, _exclude_solids_and_surfaces; + bool _weld_vertices, _use_world_coords, _convert_back_units, _use_brep_data, _sew_shells, _faster_booleans, _disable_opening_subtractions, _disable_triangulation, _apply_default_materials, _include_curves, _exclude_solids_and_surfaces; double _deflection_tolerance; public: IteratorSettings() @@ -84,7 +81,6 @@ namespace IfcGeom { , _use_brep_data(false) , _sew_shells(false) , _faster_booleans(false) - , _force_ccw_face_orientation(false) , _disable_opening_subtractions(false) , _disable_triangulation(false) , _apply_default_materials(false) @@ -107,8 +103,6 @@ namespace IfcGeom { bool& sew_shells() { return _sew_shells; } const bool& faster_booleans() const { return _faster_booleans; } bool& faster_booleans() { return _faster_booleans; } - const bool& force_ccw_face_orientation() const { return _force_ccw_face_orientation; } - bool& force_ccw_face_orientation() { return _force_ccw_face_orientation; } const bool& disable_opening_subtractions() const { return _disable_opening_subtractions; } bool& disable_opening_subtractions() { return _disable_opening_subtractions; } const bool& disable_triangulation() const { return _disable_triangulation; } @@ -143,9 +137,6 @@ namespace IfcGeom { case SEW_SHELLS: _sew_shells = value; break; - case FORCE_CCW_FACE_ORIENTATION: - _force_ccw_face_orientation = value; - break; case DISABLE_OPENING_SUBTRACTIONS: _disable_opening_subtractions = value; break; diff --git a/src/ifcgeom/IfcGeomWires.cpp b/src/ifcgeom/IfcGeomWires.cpp index 48e5166723..c212ad6422 100644 --- a/src/ifcgeom/IfcGeomWires.cpp +++ b/src/ifcgeom/IfcGeomWires.cpp @@ -408,30 +408,15 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeLoop* l, TopoDS_Wire& resu IfcSchema::IfcOrientedEdge::list::ptr li = l->EdgeList(); BRepBuilderAPI_MakeWire mw; for (IfcSchema::IfcOrientedEdge::list::it it = li->begin(); it != li->end(); ++it) { - IfcSchema::IfcOrientedEdge* e = *it; - - IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) e->EdgeStart())->VertexGeometry(); - IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) e->EdgeEnd())->VertexGeometry(); - if (!pnt1->is(IfcSchema::Type::IfcCartesianPoint) || !pnt2->is(IfcSchema::Type::IfcCartesianPoint)) { - Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l->entity); - return false; - } - - gp_Pnt p1, p2; - if (!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) || - !IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2)) - { - return false; - } - - mw.Add(BRepBuilderAPI_MakeEdge(p1, p2)); - continue; - - IfcSchema::IfcEdge* base = e->EdgeElement(); TopoDS_Wire w; - if (convert_wire(e->EdgeElement(), w)) { - if (!e->Orientation()) w.Reverse(); - mw.Add(w); + if (convert_wire(*it, w)) { + if (!(*it)->Orientation()) w.Reverse(); + TopoDS_Iterator it(w, false); + for (; it.More(); it.Next()) { + const TopoDS_Edge& e = TopoDS::Edge(it.Value()); + mw.Add(e); + } + // mw.Add(w); } } result = mw; @@ -466,7 +451,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdge* l, TopoDS_Wire& result) } bool IfcGeom::Kernel::convert(const IfcSchema::IfcOrientedEdge* l, TopoDS_Wire& result) { - if (convert(l->EdgeElement(), result)) { + if (convert_wire(l->EdgeElement(), result)) { if (!l->Orientation()) { result.Reverse(); } @@ -478,7 +463,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcOrientedEdge* l, TopoDS_Wire& bool IfcGeom::Kernel::convert(const IfcSchema::IfcSubedge* l, TopoDS_Wire& result) { TopoDS_Wire temp; - if (convert(l->ParentEdge(), result) && convert((IfcSchema::IfcEdge*) l, temp)) { + if (convert_wire(l->ParentEdge(), result) && convert((IfcSchema::IfcEdge*) l, temp)) { TopExp_Explorer exp(result, TopAbs_EDGE); TopoDS_Edge edge = TopoDS::Edge(exp.Current()); Standard_Real u1, u2; diff --git a/src/ifcgeom/IfcRegister.h b/src/ifcgeom/IfcRegister.h index 3ce2edf403..648bb70699 100644 --- a/src/ifcgeom/IfcRegister.h +++ b/src/ifcgeom/IfcRegister.h @@ -93,6 +93,7 @@ FACE(IfcEllipseProfileDef); FACE(IfcCenterLineProfileDef); FACE(IfcCompositeProfileDef); FACE(IfcDerivedProfileDef); +// IfcFaceSurface included FACE(IfcFace); WIRE(IfcEdgeCurve); diff --git a/src/ifcgeomserver/IfcGeomServer.cpp b/src/ifcgeomserver/IfcGeomServer.cpp index d543f60ac6..22eaca9844 100644 --- a/src/ifcgeomserver/IfcGeomServer.cpp +++ b/src/ifcgeomserver/IfcGeomServer.cpp @@ -321,7 +321,6 @@ int main (int argc, char** argv) { settings.use_world_coords() = false; settings.weld_vertices() = false; settings.convert_back_units() = true; - settings.force_ccw_face_orientation() = true; settings.include_curves() = true; iterator = new IfcGeom::Iterator(settings, data, len); diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index a00e003ae0..666ee342ef 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -95,7 +95,7 @@ %extend IfcGeom::IteratorSettings { %pythoncode %{ - attrs = ("convert_back_units", "deflection_tolerance", "disable_opening_subtractions", "disable_triangulation", "faster_booleans", "force_ccw_face_orientation", "sew_shells", "use_brep_data", "use_world_coords", "weld_vertices") + attrs = ("convert_back_units", "deflection_tolerance", "disable_opening_subtractions", "disable_triangulation", "faster_booleans", "sew_shells", "use_brep_data", "use_world_coords", "weld_vertices") def __repr__(self): return "%s(%s)"%(self.__class__.__name__, ",".join(tuple("%s=%r"%(a, getattr(self, a)()) for a in self.attrs))) %} @@ -218,7 +218,6 @@ IfcGeom::Kernel kernel; kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.sew_shells() ? 1000 : -1); - kernel.setValue(IfcGeom::Kernel::GV_FORCE_CCW_FACE_ORIENTATION, settings.force_ccw_face_orientation() ? 1 : -1); kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.include_curves() ? (settings.exclude_solids_and_surfaces() ? -1. : 0.) : +1.)); IfcSchema::IfcProduct* product = (IfcSchema::IfcProduct*) instance; diff --git a/test/input/faces.ifc b/test/input/faces.ifc new file mode 100644 index 0000000000..edbe3a65ff --- /dev/null +++ b/test/input/faces.ifc @@ -0,0 +1,164 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1'); +FILE_NAME('faces.ifc','2015-05-20T10:50:06',(),(),'IfcOpenShell 0.5.0-dev','IfcOpenShell 0.5.0-dev',''); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCCARTESIANPOINT((-400.,-400.,0.)); +#2=IFCCARTESIANPOINT((400.,-400.,0.)); +#3=IFCCARTESIANPOINT((400.,400.,0.)); +#4=IFCCARTESIANPOINT((-400.,400.,0.)); +#5=IFCPERSON($,$,'',$,$,$,$,$); +#6=IFCORGANIZATION($,'IfcOpenShell',$,$,$); +#7=IFCPERSONANDORGANIZATION(#5,#6,$); +#8=IFCAPPLICATION(#6,'0.5.0-dev','IfcOpenShell','IfcOpenShell'); +#9=IFCOWNERHISTORY(#7,#8,$,.ADDED.,1432111806,#7,#8,1432111806); +#10=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0); +#11=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#12=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.); +#14=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453293),#12); +#15=IFCCONVERSIONBASEDUNIT(#10,.PLANEANGLEUNIT.,'Degrees',#14); +#16=IFCUNITASSIGNMENT((#11,#15)); +#17=IFCPROJECT('1HQX190X58kvRECR6voyfc',#9,$,$,$,$,$,(#51),#16); +#18=IFCDIRECTION((1.,0.,0.)); +#19=IFCDIRECTION((0.,0.,1.)); +#20=IFCCARTESIANPOINT((0.,0.,0.)); +#21=IFCAXIS2PLACEMENT3D(#20,#19,#18); +#22=IFCLOCALPLACEMENT($,#21); +#23=IFCSITE('0eyqmWf4z3dANo5Kaef3tu',#9,$,$,$,#22,$,$,.ELEMENT.,$,$,$,$,$); +#24=IFCRELAGGREGATES('18QNJy24z3FB9H1omtBuo_',#9,$,$,#17,(#23)); +#25=IFCDIRECTION((1.,0.,0.)); +#26=IFCDIRECTION((0.,0.,1.)); +#27=IFCCARTESIANPOINT((0.,0.,0.)); +#28=IFCAXIS2PLACEMENT3D(#27,#26,#25); +#29=IFCLOCALPLACEMENT(#22,#28); +#30=IFCBUILDING('3jWWZTz7n64v5lXI7dcOG2',#9,$,$,$,#29,$,$,.ELEMENT.,$,$,$); +#31=IFCRELAGGREGATES('3I_AMlVAjD7AJyIfSH193g',#9,$,$,#23,(#30)); +#32=IFCDIRECTION((1.,0.,0.)); +#33=IFCDIRECTION((0.,0.,1.)); +#34=IFCCARTESIANPOINT((0.,0.,0.)); +#35=IFCAXIS2PLACEMENT3D(#34,#33,#32); +#36=IFCLOCALPLACEMENT(#29,#35); +#37=IFCBUILDINGSTOREY('1_ImA5b7HELAI8raWFYphY',#9,$,$,$,#36,$,$,.ELEMENT.,$); +#38=IFCRELAGGREGATES('29hL9yBXj6lvk6BXb$wnMF',#9,$,$,#30,(#37)); +#39=IFCBUILDINGELEMENTPROXY('344_rTMFT6RuKWcRH19W6g',#9,'polyloop',$,$,#45,#58,$,$); +#40=IFCRELCONTAINEDINSPATIALSTRUCTURE('2RQ$SfpKfBYuSRATxRwC0V',#9,$,$,(#39),#37); +#41=IFCDIRECTION((1.,0.,0.)); +#42=IFCDIRECTION((0.,0.,1.)); +#43=IFCCARTESIANPOINT((0.,0.,0.)); +#44=IFCAXIS2PLACEMENT3D(#43,#42,#41); +#45=IFCLOCALPLACEMENT($,#44); +#46=IFCDIRECTION((0.,1.)); +#47=IFCDIRECTION((1.,0.,0.)); +#48=IFCDIRECTION((0.,0.,1.)); +#49=IFCCARTESIANPOINT((0.,0.,0.)); +#50=IFCAXIS2PLACEMENT3D(#49,#48,#47); +#51=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-005,#50,#46); +#52=IFCPOLYLOOP((#1,#2,#3,#4)); +#53=IFCFACEOUTERBOUND(#52,.T.); +#54=IFCFACE((#53)); +#55=IFCOPENSHELL((#54)); +#56=IFCFACEBASEDSURFACEMODEL((#55)); +#57=IFCSHAPEREPRESENTATION(#51,'Body','SurfaceModel',(#56)); +#58=IFCPRODUCTDEFINITIONSHAPE($,$,(#57)); +#59=IFCCARTESIANPOINT((400.,0.,0.)); +#60=IFCCARTESIANPOINT((-400.,0.,0.)); +#61=IFCDIRECTION((1.,0.)); +#62=IFCCARTESIANPOINT((0.,0.)); +#63=IFCAXIS2PLACEMENT2D(#62,#61); +#64=IFCBUILDINGELEMENTPROXY('35crzRJnnE9Akf8211DDa3',#9,'circle',$,$,#70,#84,$,$); +#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('3gBsuCqwb3me5EkC8o3Mpn',#9,$,$,(#64),#37); +#66=IFCDIRECTION((1.,0.,0.)); +#67=IFCDIRECTION((0.,0.,1.)); +#68=IFCCARTESIANPOINT((1000.,0.,0.)); +#69=IFCAXIS2PLACEMENT3D(#68,#67,#66); +#70=IFCLOCALPLACEMENT($,#69); +#71=IFCVERTEXPOINT(#59); +#72=IFCVERTEXPOINT(#60); +#73=IFCCIRCLE(#63,400.); +#74=IFCEDGECURVE(#71,#72,#73,.T.); +#75=IFCORIENTEDEDGE(*,*,#74,.T.); +#76=IFCEDGECURVE(#72,#71,#73,.T.); +#77=IFCORIENTEDEDGE(*,*,#76,.T.); +#78=IFCEDGELOOP((#75,#77)); +#79=IFCFACEOUTERBOUND(#78,.T.); +#80=IFCFACE((#79)); +#81=IFCOPENSHELL((#80)); +#82=IFCFACEBASEDSURFACEMODEL((#81)); +#83=IFCSHAPEREPRESENTATION(#51,'Body','SurfaceModel',(#82)); +#84=IFCPRODUCTDEFINITIONSHAPE($,$,(#83)); +#85=IFCCARTESIANPOINT((-400.,-400.,0.)); +#86=IFCCARTESIANPOINT((400.,-400.,0.)); +#87=IFCCARTESIANPOINT((400.,400.,0.)); +#88=IFCCARTESIANPOINT((-400.,400.,0.)); +#89=IFCCARTESIANPOINT((-300.,-300.,0.)); +#90=IFCCARTESIANPOINT((-100.,-300.,0.)); +#91=IFCCARTESIANPOINT((-100.,300.,0.)); +#92=IFCCARTESIANPOINT((-300.,300.,0.)); +#93=IFCCARTESIANPOINT((100.,300.,0.)); +#94=IFCCARTESIANPOINT((300.,300.,0.)); +#95=IFCCARTESIANPOINT((300.,-300.,0.)); +#96=IFCCARTESIANPOINT((100.,-300.,0.)); +#97=IFCBUILDINGELEMENTPROXY('3w09rKcILCYAnKVxlLSjOP',#9,'polyloop with holes',$,$,#103,#114,$,$); +#98=IFCRELCONTAINEDINSPATIALSTRUCTURE('2QRyhTXwH8WPAUUlchhb7x',#9,$,$,(#97),#37); +#99=IFCDIRECTION((1.,0.,0.)); +#100=IFCDIRECTION((0.,0.,1.)); +#101=IFCCARTESIANPOINT((2000.,0.,0.)); +#102=IFCAXIS2PLACEMENT3D(#101,#100,#99); +#103=IFCLOCALPLACEMENT($,#102); +#104=IFCPOLYLOOP((#89,#90,#91,#92)); +#105=IFCFACEBOUND(#104,.F.); +#106=IFCPOLYLOOP((#85,#86,#87,#88)); +#107=IFCFACEOUTERBOUND(#106,.T.); +#108=IFCPOLYLOOP((#93,#94,#95,#96)); +#109=IFCFACEBOUND(#108,.T.); +#110=IFCFACE((#105,#107,#109)); +#111=IFCOPENSHELL((#110)); +#112=IFCFACEBASEDSURFACEMODEL((#111)); +#113=IFCSHAPEREPRESENTATION(#51,'Body','SurfaceModel',(#112)); +#114=IFCPRODUCTDEFINITIONSHAPE($,$,(#113)); +#115=IFCCARTESIANPOINT((-400.,-400.,0.)); +#116=IFCCARTESIANPOINT((-100.,-400.,0.)); +#117=IFCCARTESIANPOINT((-100.,400.,0.)); +#118=IFCCARTESIANPOINT((-400.,400.,0.)); +#119=IFCCARTESIANPOINT((100.,400.,0.)); +#120=IFCCARTESIANPOINT((400.,400.,0.)); +#121=IFCCARTESIANPOINT((400.,-400.,0.)); +#122=IFCCARTESIANPOINT((100.,-400.,0.)); +#123=IFCBUILDINGELEMENTPROXY('0cxEkTWf97T8imCiw2hTqD',#9,'multiple outer boundaries (invalid)',$,$,#129,#138,$,$); +#124=IFCRELCONTAINEDINSPATIALSTRUCTURE('0$orfSvG18vfnhm9zOB5l5',#9,$,$,(#123),#37); +#125=IFCDIRECTION((1.,0.,0.)); +#126=IFCDIRECTION((0.,0.,1.)); +#127=IFCCARTESIANPOINT((3000.,0.,0.)); +#128=IFCAXIS2PLACEMENT3D(#127,#126,#125); +#129=IFCLOCALPLACEMENT($,#128); +#130=IFCPOLYLOOP((#115,#116,#117,#118)); +#131=IFCFACEOUTERBOUND(#130,.T.); +#132=IFCPOLYLOOP((#119,#120,#121,#122)); +#133=IFCFACEOUTERBOUND(#132,.F.); +#134=IFCFACE((#131,#133)); +#135=IFCOPENSHELL((#134)); +#136=IFCFACEBASEDSURFACEMODEL((#135)); +#137=IFCSHAPEREPRESENTATION(#51,'Body','SurfaceModel',(#136)); +#138=IFCPRODUCTDEFINITIONSHAPE($,$,(#137)); +#139=IFCCARTESIANPOINT((-400.,-400.,1.E-006)); +#140=IFCCARTESIANPOINT((400.,-400.,0.)); +#141=IFCCARTESIANPOINT((400.,400.,0.)); +#142=IFCCARTESIANPOINT((-400.,400.,0.)); +#143=IFCBUILDINGELEMENTPROXY('1u73lgHiH3IesmFCYzbd$l',#9,'imprecise polyloop',$,$,#149,#156,$,$); +#144=IFCRELCONTAINEDINSPATIALSTRUCTURE('0AV1k6Kl19x9ot0jkkQV8h',#9,$,$,(#143),#37); +#145=IFCDIRECTION((1.,0.,0.)); +#146=IFCDIRECTION((0.,0.,1.)); +#147=IFCCARTESIANPOINT((4000.,0.,0.)); +#148=IFCAXIS2PLACEMENT3D(#147,#146,#145); +#149=IFCLOCALPLACEMENT($,#148); +#150=IFCPOLYLOOP((#139,#140,#141,#142)); +#151=IFCFACEOUTERBOUND(#150,.T.); +#152=IFCFACE((#151)); +#153=IFCOPENSHELL((#152)); +#154=IFCFACEBASEDSURFACEMODEL((#153)); +#155=IFCSHAPEREPRESENTATION(#51,'Body','SurfaceModel',(#154)); +#156=IFCPRODUCTDEFINITIONSHAPE($,$,(#155)); +ENDSEC; +END-ISO-10303-21;