bool IfcGeom::Kernel::convert(const IfcSchema::IfcFacetedBrep* l, IfcRepresentationShapeItems& shape) { TopoDS_Shape s; const SurfaceStyle* collective_style = get_style(l); if (convert_shape(l->Outer(),s) ) { const SurfaceStyle* indiv_style = get_style(l->Outer()); shape.push_back(IfcRepresentationShapeItem(s, indiv_style ? indiv_style : collective_style)); return true; } return false; }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces(); const SurfaceStyle* collective_style = get_style(l); for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) { TopoDS_Shape s; const SurfaceStyle* shell_style = get_style(*it); if (convert_shape(*it,s)) { shapes.push_back(IfcRepresentationShapeItem(s, shell_style ? shell_style : collective_style)); } } return true; }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { IfcEntityList::ptr shells = l->SbsmBoundary(); const SurfaceStyle* collective_style = get_style(l); for( IfcEntityList::it it = shells->begin(); it != shells->end(); ++ it ) { TopoDS_Shape s; const SurfaceStyle* shell_style = 0; if ((*it)->is(IfcSchema::Type::IfcRepresentationItem)) { shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it); } if (convert_shape(*it,s)) { shapes.push_back(IfcRepresentationShapeItem(s, shell_style ? shell_style : collective_style)); } } return true; }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, IfcRepresentationShapeItems& shapes) { IfcSchema::IfcRepresentationItem::list::ptr items = l->Items(); bool part_succes = false; if ( items->size() ) { for ( IfcSchema::IfcRepresentationItem::list::it it = items->begin(); it != items->end(); ++ it ) { IfcSchema::IfcRepresentationItem* representation_item = *it; if ( shape_type(representation_item) == ST_SHAPELIST ) { part_succes |= convert_shapes(*it, shapes); } else { TopoDS_Shape s; if (convert_shape(representation_item,s)) { shapes.push_back(IfcRepresentationShapeItem(s, get_style(representation_item))); part_succes |= true; } } } } return part_succes; }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresentationShapeItems& shapes) { IfcEntityList::ptr elements = l->Elements(); if ( !elements->size() ) return false; bool part_succes = false; const IfcGeom::SurfaceStyle* parent_style = get_style(l); for ( IfcEntityList::it it = elements->begin(); it != elements->end(); ++ it ) { IfcSchema::IfcGeometricSetSelect* element = *it; TopoDS_Shape s; if (convert_shape(element, s)) { part_succes = true; const IfcGeom::SurfaceStyle* style = 0; if (element->is(IfcSchema::Type::IfcPoint)) { style = get_style((IfcSchema::IfcPoint*) element); } else if (element->is(IfcSchema::Type::IfcCurve)) { style = get_style((IfcSchema::IfcCurve*) element); } else if (element->is(IfcSchema::Type::IfcSurface)) { style = get_style((IfcSchema::IfcSurface*) element); } shapes.push_back(IfcRepresentationShapeItem(s, style ? style : parent_style)); } } return part_succes; }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCsgSolid* l, TopoDS_Shape& shape) { return convert_shape(l->TreeRootExpression(), shape); }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { TopoDS_Shape s1, s2; IfcRepresentationShapeItems items1, items2; TopoDS_Wire boundary_wire; IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand(); IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand(); bool is_halfspace = operand2->is(IfcSchema::Type::IfcHalfSpaceSolid); if ( shape_type(operand1) == ST_SHAPELIST ) { if (!(convert_shapes(operand1, items1) && flatten_shape_list(items1, s1, true))) { return false; } } else if ( shape_type(operand1) == ST_SHAPE ) { if ( ! convert_shape(operand1, s1) ) { return false; } { TopoDS_Solid temp_solid; s1 = ensure_fit_for_subtraction(s1, temp_solid); } } else { Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand1->entity); return false; } const double first_operand_volume = shape_volume(s1); if ( first_operand_volume <= ALMOST_ZERO ) Logger::Message(Logger::LOG_WARNING,"Empty solid for:",l->FirstOperand()->entity); bool shape2_processed = false; if ( shape_type(operand2) == ST_SHAPELIST ) { shape2_processed = convert_shapes(operand2, items2) && flatten_shape_list(items2, s2, true); } else if ( shape_type(operand2) == ST_SHAPE ) { shape2_processed = convert_shape(operand2,s2); if (shape2_processed && !is_halfspace) { TopoDS_Solid temp_solid; s2 = ensure_fit_for_subtraction(s2, temp_solid); } } else { Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand2->entity); } if (!shape2_processed) { shape = s1; Logger::Message(Logger::LOG_ERROR,"Failed to convert SecondOperand of:",l->entity); return true; } if (!is_halfspace) { const double second_operand_volume = shape_volume(s2); if ( second_operand_volume <= ALMOST_ZERO ) Logger::Message(Logger::LOG_WARNING,"Empty solid for:",operand2->entity); } const IfcSchema::IfcBooleanOperator::IfcBooleanOperator op = l->Operator(); if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { bool valid_cut = false; BRepAlgoAPI_Cut brep_cut(s1,s2); if ( brep_cut.IsDone() ) { TopoDS_Shape result = brep_cut; ShapeFix_Shape fix(result); try { fix.Perform(); result = fix.Shape(); } catch (...) { Logger::Message(Logger::LOG_WARNING, "Shape healing failed on boolean result", l->entity); } bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; if ( is_valid ) { shape = result; valid_cut = true; } } if ( valid_cut ) { const double volume_after_subtraction = shape_volume(shape); if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l->entity); } else { Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l->entity); shape = s1; } return true; } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { BRepAlgoAPI_Fuse brep_fuse(s1,s2); if ( brep_fuse.IsDone() ) { TopoDS_Shape result = brep_fuse; ShapeFix_Shape fix(result); fix.Perform(); result = fix.Shape(); bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; if ( is_valid ) { shape = result; return true; } } } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { BRepAlgoAPI_Common brep_common(s1,s2); if ( brep_common.IsDone() ) { TopoDS_Shape result = brep_common; ShapeFix_Shape fix(result); fix.Perform(); result = fix.Shape(); bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; if ( is_valid ) { shape = result; return true; } } } return false; }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds(); Handle(Geom_Surface) face_surface; const bool is_face_surface = l->is(IfcSchema::Type::IfcFaceSurface); 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; // FIXME: Assert this obtaines the only face TopExp_Explorer exp(surface_shape, TopAbs_FACE); if (!exp.More()) return false; TopoDS_Face surface = TopoDS::Face(exp.Current()); face_surface = BRep_Tool::Surface(surface); } 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(); 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)) { Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop->entity); delete mf; return false; } /* 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(); } bool flattened_wire = false; if (!mf) { process_wire: if (face_surface.IsNull()) { mf = new BRepBuilderAPI_MakeFace(wire); } else { /// @todo check necessity of false here mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false); } /* 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(); // In case of (non-planar) face surface, p-curves need to be computed. // For planar faces, Open Cascade generates p-curves on the fly. if (!face_surface.IsNull()) { TopExp_Explorer exp(outer_face_bound, TopAbs_EDGE); for (; exp.More(); exp.Next()) { const TopoDS_Edge& edge = TopoDS::Edge(exp.Current()); ShapeFix_Edge fix_edge; fix_edge.FixAddPCurve(edge, outer_face_bound, false, getValue(GV_PRECISION)); } } if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) { wire.Reverse(); same_sense = !same_sense; delete mf; if (face_surface.IsNull()) { mf = new BRepBuilderAPI_MakeFace(wire); } else { mf = new BRepBuilderAPI_MakeFace(face_surface, wire); } ShapeFix_Face fix(mf->Face()); fix.FixOrientation(); fix.Perform(); outer_face_bound = fix.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 jt(outer_face_bound, false); for (; jt.More(); jt.Next()) { const TopoDS_Wire& w = TopoDS::Wire(jt.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; // TODO: What about the face_surface? mf = new BRepBuilderAPI_MakeFace(outer_face_bound); } else { face = outer_face_bound; success = true; } } else { const bool non_planar = mf->Error() == BRepBuilderAPI_NotPlanar; delete mf; if (!non_planar || flattened_wire || !flatten_wire(wire)) { Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound->entity); return false; } else { Logger::Message(Logger::LOG_ERROR, "Flattening face boundary", bound->entity); flattened_wire = true; goto process_wire; } } } 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; }