/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" #include "BrepBuilderFillerModule.h" #include "BrepBuilderInitialData.h" #include "IMaterialAndColorHelper.h" // br #include "Br/BrBrep.h" #include "Br/BrComplex.h" #include "Br/BrShell.h" #include "Br/BrFace.h" #include "Br/BrLoop.h" #include "Br/BrEdge.h" #include "Br/BrBrepComplexTraverser.h" #include "Br/BrComplexShellTraverser.h" #include "Br/BrShellFaceTraverser.h" #include "Br/BrFaceLoopTraverser.h" #include "Br/BrLoopEdgeTraverser.h" #include "Br/BrLoopVertexTraverser.h" #include "Br/BrBrepFaceTraverser.h" // bb #include "BrepBuilder/BrepBuilder.h" // ge #include "Ge/GeExt.h" #include "Ge/GePoint3d.h" #include "Ge/GePoint2d.h" #include "Ge/GeExtents2d.h" #include "Ge/GePoint2dArray.h" #include "Ge/GeCurveCurveInt2d.h" // curve #include "Ge/GeCurve2d.h" #include "Ge/GeLineSeg2d.h" #include "Ge/GeLineSeg3d.h" #include "Ge/GeCircArc3d.h" #include "Ge/GeEllipArc3d.h" #include "Ge/GeNurbCurve2d.h" #include "Ge/GeNurbCurve3d.h" #include "Ge/GeSurfaceCurve2dTo3d.h" #include "Ge/GeExternalCurve3d.h" // surf #include "Ge/GeEllipCone.h" #include "Ge/GeEllipCylinder.h" #include "Ge/GePlane.h" #include "Ge/GeBoundedPlane.h" #include "Ge/GeExternalSurface.h" #include "Ge/GeExternalBoundedSurface.h" // stl #define STL_USING_VECTOR #define STL_USING_ALGORITHM #define STL_USING_UTILITY #define STL_USING_SET #define STL_USING_MAP #define STL_USING_LIMITS #define STL_USING_MEMORY #include "OdaSTL.h" // #include "OdHashSet.h" #include "OdHashMap.h" typedef std::unique_ptr OdGeTempCurve3dPtr; typedef std::unique_ptr OdGeTempCurve2dPtr; typedef std::unique_ptr OdGeTempNurbCurve3dPtr; typedef std::unique_ptr OdGeTempNurbCurve2dPtr; typedef std::unique_ptr OdGeTempSurfacePtr; #ifdef OD_DBG_SEE_THE_INVISIBLE # include "GeInvisible.h" #endif #define FIX_NURBS_CIRCLES // Helper functions namespace { #if 0 #ifndef ODA_DIAGNOSTICS # error "disable debug code" # endif struct DumpPoints { OdGePoint3dArray origin_points3d; OdGePoint3dArray origin_points3dSurf; OdGePoint2dArray origin_uvPoints; OdGePoint3dArray result_points3dSurf; OdGePoint2dArray result_uvPoints; }; struct DumpPointsData { static constexpr int pointCount = 20; OdGePoint3dArray surfPoints; std::vector dumpPoints; void addSurf(OdGeSurface& surf) { int pointCountU = 10; int pointCountV = 10; if (OdGe::kNurbSurface == surf.type()) { const OdGeNurbSurface& pNurbs = static_cast(surf); pointCountU = pNurbs.numControlPointsInU() * 3; pointCountV = pNurbs.numControlPointsInV() * 3; } OdGeUvBox surfBox; surf.getEnvelope(surfBox); surfPoints.reserve(pointCountU * pointCountV); for (int iv = 0; iv < pointCountV; ++iv) for (int iu = 0; iu < pointCountU; ++iu) surfPoints.append(surf.evalPoint(surfBox.eval( iv % 2 ? iu / double(pointCountU - 1) : (pointCountU - iu - 1) / double(pointCountU - 1), iv / double(pointCountV - 1) ))); } void addOriginCoedge(const OdGeSurface& surf, const OdGeCurve3d& curve3d, const OdGeCurve2d* pCurve2d) { dumpPoints.emplace_back(); DumpPoints& points = dumpPoints.back(); curve3d.appendSamplePoints(pointCount, points.origin_points3d); if (pCurve2d) { pCurve2d->appendSamplePoints(pointCount, points.origin_uvPoints); std::for_each(points.origin_uvPoints.begin(), points.origin_uvPoints.end(), [&points, &surf](const OdGePoint2d& param) { points.origin_points3dSurf.push_back(surf.evalPoint(param)); }); } } void addFinalCoedges(const OdGeSurface& surf, const BrepBuilderInitialLoop& loopData) { if (loopData.coedges.size() != dumpPoints.size()) throw OdError(eInvalidIndex); for (OdArrayBuffer::size_type iCoedge = 0; iCoedge < loopData.coedges.size(); ++iCoedge) { DumpPoints& points = dumpPoints[iCoedge]; const OdGeCurve2d* pCurve2d = loopData.coedges[iCoedge].curve; if (pCurve2d) { pCurve2d->appendSamplePoints(pointCount, points.result_uvPoints); std::for_each(points.result_uvPoints.begin(), points.result_uvPoints.end(), [&points, &surf](const OdGePoint2d& param) { points.result_points3dSurf.push_back(surf.evalPoint(param)); }); } } } }; #else struct DumpPointsData { void addSurf(OdGeSurface&) {} void addOriginCoedge(const OdGeSurface&, const OdGeCurve3d&, const OdGeCurve2d*) {} void addFinalCoedges(const OdGeSurface&, const BrepBuilderInitialLoop&) {} }; #endif // DUMP_POINTS_TEST OdGePoint2d getStartPoint(const OdGeCurve2d& curve) { OdGePoint2d res; ODA_ASSERT_VAR(bool rc = ) curve.hasStartPoint(res); ODA_ASSERT_ONCE(rc); return res; } OdGePoint2d getEndPoint(const OdGeCurve2d& curve) { OdGePoint2d res; ODA_ASSERT_VAR(bool rc = ) curve.hasEndPoint(res); ODA_ASSERT_ONCE(rc); return res; } OdGePoint3d getStartPoint(const OdGeCurve3d& curve) { OdGePoint3d res; ODA_ASSERT_VAR(bool rc = ) curve.hasStartPoint(res); ODA_ASSERT_ONCE(rc); return res; } OdGePoint3d getEndPoint(const OdGeCurve3d& curve) { OdGePoint3d res; ODA_ASSERT_VAR(bool rc = ) curve.hasEndPoint(res); ODA_ASSERT_ONCE(rc); return res; } template void addUnique(const T& item, A& arr) { if (std::find(arr.begin(), arr.end(), item) != arr.end()) return; arr.push_back(item); } } class OdBrepBuilderFillerHelper { BrepBuilderInitialData& m_initialData; OdIMaterialAndColorHelper* m_pMaterialHelper; const OdBrepBuilderFillerParams& m_params; OdHashMap m_edges; OdHashMap m_vertices; double m_toleranceInterval; // Points are different when distance is greater than m_toleranceDiffPoints. // Otherwise, more calculations are needed double m_toleranceDiffPoints; double m_toleranceRestore2dCurve; double m_toleranceCoincide; bool m_loopsSplit; mutable OdGePoint3dArray m_testPoints; // Structure for addFaceExplicitLoop struct EdgeCurveCompareData { const OdGeCurve3d* pCurve; const OdUInt32 idEdge; const OdGePoint3dArray aSamplePts; double dDist; EdgeCurveCompareData(const double distance = 0.0) : pCurve(NULL), idEdge(0), dDist(distance) {} EdgeCurveCompareData(const OdGeCurve3d* curve, const OdUInt32 id, const OdGePoint3dArray& points, const double distance) : pCurve(curve), idEdge(id), aSamplePts(points), dDist(distance) {} bool operator<(const EdgeCurveCompareData& other) const { return dDist < other.dDist; } }; // Field for addFaceExplicitLoop std::multiset m_edgesAdded; public: static inline OdResult checkRet(OdResult status) { #ifdef ODA_DIAGNOSTICS if (eOk != status) { return status; } #endif return status; } static inline void assertGsMarkerValue(OdGsMarker marker) { ODA_VERIFY_ONCE(marker >= 0 && marker <= UINT_MAX); } explicit OdBrepBuilderFillerHelper( BrepBuilderInitialData& initialData, const OdBrepBuilderFiller& filler, OdIMaterialAndColorHelper* pMaterialHelper = NULL ) : m_initialData(initialData) , m_pMaterialHelper(pMaterialHelper) , m_params(filler.params()) , m_toleranceInterval(1e-9)// double d2dParamTol = 1e-9;//TODO: possible investigation needed , m_toleranceDiffPoints(1e-2) , m_toleranceRestore2dCurve(1e-6) , m_toleranceCoincide(1e-6) , m_loopsSplit(false) {} double toleranceInterval() const { return m_toleranceInterval; } // Interval bool areIntervalsEqual(const OdGeInterval& frstInt, const OdGeInterval& scndInt) const; // Edge OdGeCurve3dPtr getEdgeCurve(const OdBrEdge& edge) const; bool fixEllipse(OdGeCurve3dPtr& pCurve, const OdBrEdge& edge) const; bool fixCircle(OdGeCurve3dPtr& pCurve, const OdBrEdge& edge) const; void fixEllipseRadiusRatio(OdGeCurve3d* pCurve) const; bool fixNurb(OdGeCurve3dPtr& pCurve, const OdBrEdge& edge) const; OdResult getEdgeCurveFixed(const OdBrEdge& edge, OdGeCurve3dPtr& pCurve) const; enum class FixCurveStatus { Success, Skip, Fail }; FixCurveStatus getCurveParams(const OdBrEdge& edge, const OdGeCurve3d* pCurve, OdGePoint3d& pntStart, OdGePoint3d& pntEnd, OdGeInterval& interval) const; void setArcInterval(OdGeCurve3d* pCurve, OdGeInterval& interval, OdGePoint3d& pntStart, OdGePoint3d& center, OdGeVector3d& major, OdGeVector3d& normal) const; // Coedge OdGeCurve2dPtr getParamCurve(const OdBrLoopEdgeTraverser& loEdTrav) const; bool checkCurve2dTo3d(const OdGeSurface& surf, const OdGeCurve3d& curve3d, OdGeCurve2d& curve2d, OdGeTol& tol) const; void moveParamCurveNurbInterval(const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const; OdResult moveParamCurveInterval(const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const; bool checkCurveOnSurf(const OdGeSurface& surf, const OdGeCurve3d& curve3d, OdGeCurve2d& curve2d, const OdGeTol& tol) const; bool checkCurveOnSurf(const OdGeSurface& surf, OdGeCurve2d& curve2d) const; void moveParamIntoExtents(const OdGeSurface* pSurf, const OdGeCurve2d* pCurve2d, OdGePoint2d &pnt) const; //this routine compare a 2d curve direction with 3d curve direction bool checkParamCurve(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d, bool goodUvLoop, bool forceUvLoop, OdGeTol& tol) const; OdResult fixParamCurve(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const; OdResult fixParamCurveInterval(const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const; OdResult createParamCurve(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2dPtr& pCurve2d, OdGeTol tol = -1.) const; OdGeTol calcEdgeTol(const OdGeSurface& surf, const OdGeCurve3d& curve3d) const; OdResult fixSingleCoedgeLoopByEdgeEnd(OdGeSurface& surf, OdGeCurve3d& curve3d, OdGeCurve2d& curve2d); OdResult getParamCurveFixed(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2dPtr& pCurve2d, bool goodUvLoop, bool forceUvLoop) const; OdResult addFaceExplicitLoop(BrepBuilderInitialSurface& surfData); bool askParamCurve(const OdGeSurface* pSurf) const; bool needToProjectParamCurve(const OdGeSurface* pSurf) const; // Face OdGeSurfacePtr checkExtSurface(const OdGeSurface* resSurf, const OdBrFace& face) const; OdGeSurfacePtr getFaceSurface(const OdBrFace& face) const; OdResult fixFaceSurface(OdGeSurface& surf) const; void fixEllipConeRRatio(OdGeEllipCone& ellipCone) const; void fixEllipCylRRatio(OdGeEllipCylinder& ellipCylinder) const; template OdResult fixPlaneUV(PlaneClass& plane) const; // Vertex BrepBuilderInitialEdge::VertexIndex addVertex(const OdBrVertex& vertex); // Loop OdResult performLoopWithApex(const OdBrLoop& loop, OdGeCurve3dPtr& pCurve3d, OdGeCurve2dPtr& pCurve2d, OdBrVertex* vertex = NULL) const; OdResult splitOuterLoops(BrepBuilderInitialSurfaceArray& arrSurfaces); double calcCoedgeCurveTol(const OdGeCurve2d& curve0, const OdGeCurve2d& curve1) const; bool checkCoedgeLoop(OdGeSurface& surf, BrepBuilderInitialLoop& loopData); OdResult splitEdgesByPole(); // Topology OdResult performBrep(const OdBrBrep& brep); OdResult performComplex(const OdBrComplex& complex); OdResult performShell(const OdBrShell& shell, BrepBuilderShellsArray& arrShells); OdResult performFace(const OdBrFace& face, BrepBuilderInitialSurfaceArray& arrSurfaces); OdResult performLoop(const OdBrLoop& loop, BrepBuilderInitialSurface& surfData); OdResult performEdge(const OdBrEdge& loop, OdUInt32& edgeIdx, BrepBuilderInitialEdge*& pEdgeData); static OdResult initFromImpl(OdBrepBuilderFiller& filler, OdBrepBuilder& builder, const BrepBuilderInitialData& data); void groupFaces(BrepBuilderInitialSurfaceArray& arrFaces, OdArray& groupedFaces); void findAdjacentFaces(BrepBuilderInitialSurfaceArray& arrFaces, OdHashSet& usedFaces, const OdHashMap& edgesFaces, OdUInt32 iCurrFace, OdUInt32Array& facesIndexes); OdResult fixFaceRegionsConnections(BrepBuilderInitialSurfaceArray& arrFaces); static OdResult getDataFrom( BrepBuilderInitialData& data, const OdBrepBuilderFiller& filler, const OdBrBrep& brep, OdIMaterialAndColorHelper* materialHelper); //----------------------------------- //BimRv specific. Face regions struct CoedgeIndex { unsigned faceIdx; unsigned loopIdx; unsigned coedgeIdx; CoedgeIndex(unsigned faceIdx, unsigned loopIdx, unsigned coedgeIdx) : faceIdx(faceIdx), loopIdx(loopIdx), coedgeIdx(coedgeIdx) {} CoedgeIndex() : faceIdx(unsigned(-1)), loopIdx(unsigned(-1)), coedgeIdx(unsigned(-1)) {} }; typedef OdUInt32 EdgeIndex; typedef std::pair EdgeCoedgeIndex; OdResult getFaceEdges(const OdBrFace& face, OdArray& faceEdgesIdx); OdResult findParentEdge(BrepBuilderInitialSurfaceArray& arrFaces, EdgeIndex edgeIdx, CoedgeIndex& coedgeIndex, const OdArray& parentEdgeIndices, EdgeIndex& parentIdx, std::map>& edgesCoedges); OdResult sortEdges(const BrepBuilderInitialSurfaceArray& arrFaces, OdArray& coedges); inline OdResult fixCoedgeIdx(const BrepBuilderInitialSurfaceArray& arrFaces, OdUInt32 edgeIdx, CoedgeIndex& coedgeIndex); OdArray> m_parentFacesEdgesIdx; //Arrays of edges indices in m_initialData.edges; each array corresponds to OdBrFace that has face regions. //----------------------------------- }; // Interval bool OdBrepBuilderFillerHelper::areIntervalsEqual(const OdGeInterval& frstInt, const OdGeInterval& scndInt) const { return OdEqual(frstInt.lowerBound(), scndInt.lowerBound(), m_toleranceInterval) && OdEqual(frstInt.upperBound(), scndInt.upperBound(), m_toleranceInterval); } // Edge OdGeCurve3dPtr OdBrepBuilderFillerHelper::getEdgeCurve(const OdBrEdge& edge) const { OdGeCurve3dPtr curve3d(edge.getCurve()); if (!curve3d) { OdGeNurbCurve3d nurbCurve3d; if (edge.getCurveAsNurb(nurbCurve3d)) { return static_cast(nurbCurve3d.copy()); } return NULL; } OdGe::EntityId entType = curve3d->type(); if (OdGe::kExternalCurve3d == entType) { OdGeCurve3d* resCurve3d = NULL; const OdGeExternalCurve3d* extCurve = static_cast(curve3d.get()); if (extCurve->isNativeCurve(resCurve3d)) curve3d = resCurve3d; } return curve3d; } OdBrepBuilderFillerHelper::FixCurveStatus OdBrepBuilderFillerHelper::getCurveParams(const OdBrEdge& edge, const OdGeCurve3d* pCurve, OdGePoint3d& pntStart, OdGePoint3d& pntEnd, OdGeInterval& interval) const { OdBrVertex startVertex; OdBrVertex endVertex; if (!edge.getVertex1(startVertex) || !edge.getVertex2(endVertex)) { // seems only parasolid may be without vertices return FixCurveStatus::Skip; //can't get vertexes - skip this curve } if (edge.getOrientToCurve()) { pntStart = startVertex.getPoint(); pntEnd = endVertex.getPoint(); } else { pntEnd = startVertex.getPoint(); pntStart = endVertex.getPoint(); } OdGePoint3d curveStart; OdGePoint3d curveEnd; if (!pCurve->hasStartPoint(curveStart) || !pCurve->hasEndPoint(curveEnd)) { return FixCurveStatus::Fail;//Can't get vert points - this is error } if (pntStart.isEqualTo(curveStart, m_toleranceDiffPoints)) { return FixCurveStatus::Skip;//points are equal } pCurve->getInterval(interval); return FixCurveStatus::Success;//ok, we got params } void OdBrepBuilderFillerHelper::setArcInterval(OdGeCurve3d* pCurve, OdGeInterval& interval, OdGePoint3d& pntStart, OdGePoint3d& center, OdGeVector3d& major, OdGeVector3d& normal) const { OdGeVector3d centerToStart = pntStart - center; if (major.isCodirectionalTo(centerToStart)) { if (OdNegative(interval.lowerBound())) pCurve->setInterval(OdGeInterval(0., interval.length())); } else { double angle = major.angleTo(centerToStart, normal); if (!OdZero(angle)) { pCurve->rotateBy(angle, normal, center); if (OdNegative(interval.lowerBound())) pCurve->setInterval(OdGeInterval(0., interval.length())); } } #ifdef ODA_DIAGNOSTICS OdGePoint3d curveStart = getStartPoint(*pCurve); ODA_ASSERT_ONCE(pntStart.isEqualTo(curveStart, 1e-05)); #endif // ODA_DIAGNOSTICS } bool OdBrepBuilderFillerHelper::fixEllipse(OdGeCurve3dPtr& pCurve, const OdBrEdge& edge) const { OdGeEllipArc3d& geEllipse = static_cast(*pCurve); OdGePoint3d startPoint; OdGePoint3d endPoint; OdGeInterval interval; FixCurveStatus res = getCurveParams(edge, pCurve, startPoint, endPoint, interval); if (FixCurveStatus::Success != res) { return res == FixCurveStatus::Skip; } if (!startPoint.isEqualTo(endPoint, m_toleranceDiffPoints)) { double newStartParam = geEllipse.paramOf(startPoint); #ifdef FIX_NURBS_CIRCLES double newEndParam = pCurve->paramOf(endPoint); geEllipse.setInterval(OdGeInterval(newStartParam, newEndParam)); #else geEllipse.setInterval(OdGeInterval(newStartParam, newStartParam + interval.length())); #endif pCurve = new OdGeNurbCurve3d(geEllipse); ODA_ASSERT_ONCE(static_cast(pCurve.get())->startPoint().isEqualTo(startPoint, 1e-04)); return true; } // closed if (!geEllipse.isCircular()) { double newStartParam = geEllipse.paramOf(startPoint); geEllipse.setInterval(OdGeInterval(newStartParam, newStartParam + interval.length())); return true; } // closed circle OdGePoint3d center = geEllipse.center(); OdGeVector3d major = geEllipse.majorAxis(); OdGeVector3d normal = geEllipse.normal(); ODA_ASSERT_ONCE(OdEqual(startPoint.distanceTo(center), geEllipse.majorRadius(), 1e-05)); setArcInterval(pCurve, interval, startPoint, center, major, normal); return true; } bool OdBrepBuilderFillerHelper::fixCircle(OdGeCurve3dPtr& pCurve, const OdBrEdge& edge) const { OdGePoint3d startPoint; OdGePoint3d endPoint; OdGeInterval interval; FixCurveStatus res = getCurveParams(edge, pCurve, startPoint, endPoint, interval); if (FixCurveStatus::Success != res) { return res == FixCurveStatus::Skip; } OdGeCircArc3d& geCircle = static_cast(*pCurve); if (!startPoint.isEqualTo(endPoint, m_toleranceDiffPoints)) { double newStartParam = pCurve->paramOf(startPoint); #ifdef FIX_NURBS_CIRCLES double newEndParam = pCurve->paramOf(endPoint); pCurve->setInterval(OdGeInterval(newStartParam, newEndParam)); #else pCurve->setInterval(OdGeInterval(newStartParam, newStartParam + interval.length())); #endif pCurve = new OdGeNurbCurve3d(geCircle); ODA_ASSERT_ONCE(static_cast(pCurve.get())->startPoint().isEqualTo(startPoint, 1e-04)); return true; } OdGePoint3d center = geCircle.center(); OdGeVector3d major = geCircle.refVec(); OdGeVector3d normal = geCircle.normal(); ODA_ASSERT_ONCE(OdEqual(startPoint.distanceTo(center), geCircle.radius(), 1e-05)); setArcInterval(pCurve, interval, startPoint, center, major, normal); return true; } void OdBrepBuilderFillerHelper::fixEllipseRadiusRatio(OdGeCurve3d* pCurve) const { if (!m_params.isMakeEllipMajorGreaterMinor()) { return; } //major radius should be >= minor for acis OdGeEllipArc3d& geEllipse = static_cast(*pCurve); OdGeVector3d vecMajAxis = geEllipse.majorAxis(); OdGeVector3d vecMinAxis = geEllipse.minorAxis(); const double majorRadius = geEllipse.majorRadius(); const double minorRadius = geEllipse.minorRadius(); if (OdZero(majorRadius, m_toleranceInterval) || !vecMajAxis.isPerpendicularTo(vecMinAxis, OdGeContext::gTol)) { throw OdError(eNotImplementedYet); } if (!OdGreater(minorRadius / majorRadius, 1, m_toleranceInterval)) { return; } geEllipse.set(geEllipse.center(), vecMinAxis, vecMajAxis.negate(), minorRadius, majorRadius, geEllipse.startAng() - OdaPI2, geEllipse.endAng() - OdaPI2); } static bool isNurbLineSeg(const OdGeNurbCurve3d* ipCurve) { const int deg = ipCurve->degree(); const OdGeKnotVector& knots = ipCurve->knots(); const int NurbLineSegMinKnotsNum = 2 * deg + 2; const int NurbLineSegMinPointsNum = deg + 1; if (knots.length() != NurbLineSegMinKnotsNum || ipCurve->numControlPoints() != NurbLineSegMinPointsNum) { return false; } OdGeDoubleArray distinctKnots; OdGeIntArray knotMults; knots.getDistinctKnots(distinctKnots, &knotMults); if (distinctKnots.size() != 2 || knotMults.size() != 2 || knotMults[0] != knotMults[1] || knotMults[1] != NurbLineSegMinPointsNum) { return false; } return true; } bool OdBrepBuilderFillerHelper::fixNurb(OdGeCurve3dPtr& pCurve, const OdBrEdge& edge) const { OdGeNurbCurve3d& geNurb = static_cast(*pCurve); OdGePoint3d startPoint; OdGePoint3d endPoint; OdGeInterval interval; FixCurveStatus res = getCurveParams(edge, pCurve, startPoint, endPoint, interval); if (FixCurveStatus::Success != res) { return res == FixCurveStatus::Skip; } bool isClosedCurve = startPoint.isEqualTo(endPoint, m_toleranceDiffPoints); if (isClosedCurve) { // TODO: remove this check after CORE-19622, check DGN PS->ACIS conversion tests if (isNurbLineSeg(&geNurb)) { isClosedCurve = false; } } if (isClosedCurve) { // Closed curve // isOn not implemented double splitParam = geNurb.paramOf(startPoint); OdGeInterval curve3dInt; geNurb.getInterval(curve3dInt); if (OdEqual(splitParam, curve3dInt.lowerBound(), m_toleranceDiffPoints) || OdEqual(splitParam, curve3dInt.upperBound(), m_toleranceDiffPoints)) { // it seems that point is not on curve return true; } OdGeCurve3d* piece1 = NULL; OdGeCurve3d* piece2 = NULL; geNurb.getSplitCurves(splitParam, piece1, piece2); OdGeTempCurve3dPtr pPiece1(piece1); OdGeTempCurve3dPtr pPiece2(piece2); if (pPiece1.get() && pPiece2.get()) { ODA_ASSERT_ONCE(OdGe::kNurbCurve3d == pPiece1->type()); ODA_ASSERT_ONCE(OdGe::kNurbCurve3d == pPiece2->type()); OdGeNurbCurve3d& geNurb1 = static_cast(*pPiece1); OdGeNurbCurve3d& geNurb2 = static_cast(*pPiece2); geNurb = geNurb2.joinWith(geNurb1); ODA_ASSERT_ONCE(geNurb.startPoint().isEqualTo(startPoint, 1e-02) && geNurb.endPoint().isEqualTo(endPoint, 1e-02)); } } else { double startParamNew = geNurb.paramOf(startPoint); double endParamNew = geNurb.paramOf(endPoint); OdGeInterval curve3dInt; geNurb.getInterval(curve3dInt); if ((OdEqual(startParamNew, curve3dInt.lowerBound(), m_toleranceDiffPoints) && OdEqual(endParamNew, curve3dInt.upperBound(), m_toleranceDiffPoints))) { // it seems that point is not on curve return true; } double startParam = geNurb.startParam(); double endParam = geNurb.endParam(); if ((startParam <= startParamNew && startParamNew <= endParam) && (startParam <= endParamNew && endParamNew <= endParam)) { if ((startParamNew > endParamNew) && geNurb.isClosed()) { // remove middle of curve and merge second part with first double paramTol = odmax(geNurb.knots().tolerance(), curve3dInt.tolerance()); OdGeTempNurbCurve3dPtr pNurb1(static_cast(geNurb.copy())); OdGeTempNurbCurve3dPtr pNurb2(static_cast(geNurb.copy())); if (pNurb1.get() && pNurb2.get()) { bool skipCurveTrim[2] = { fabs(endParam - startParamNew) <= paramTol, fabs(endParamNew - startParam) <= paramTol }; if (skipCurveTrim[0] && skipCurveTrim[1]) { if ((startParamNew < endParamNew) && (fabs(endParamNew - startParamNew) > paramTol)) { // trim curve geNurb.hardTrimByParams(startParamNew, endParamNew); } } else if (skipCurveTrim[0]) { geNurb.hardTrimByParams(startParam, endParamNew); } else if (skipCurveTrim[1]) { geNurb.hardTrimByParams(startParamNew, endParam); } else { pNurb1->hardTrimByParams(startParamNew, endParam); pNurb2->hardTrimByParams(startParam, endParamNew); geNurb = pNurb1->joinWith(*pNurb2); } } } else if (startParamNew < endParamNew) { // trim curve geNurb.hardTrimByParams(startParamNew, endParamNew); } } } return true; } OdResult OdBrepBuilderFillerHelper::getEdgeCurveFixed(const OdBrEdge& edge, OdGeCurve3dPtr& pCurve) const { pCurve = getEdgeCurve(edge); if (pCurve.isNull()) { return checkRet(eNullEdgeCurve); } bool result = true; OdGe::EntityId curveType = pCurve->type(); switch (curveType) { case OdGe::kEllipArc3d: { result = fixEllipse(pCurve, edge); curveType = pCurve->type(); // become useless if fixEllipse doesn't change curve type if (OdGe::kEllipArc3d == curveType) { fixEllipseRadiusRatio(pCurve); } break; } case OdGe::kCircArc3d: result = fixCircle(pCurve, edge); break; case OdGe::kNurbCurve3d: result = fixNurb(pCurve, edge); break; } return checkRet(result ? eOk : eCurveEndsMissed); } // Coedge OdGeCurve2dPtr OdBrepBuilderFillerHelper::getParamCurve(const OdBrLoopEdgeTraverser& loEdTrav) const { if (OdBrepBuilderFillerParams::kBrepAcisDgn != m_params.destinationBrepType() && OdBrepBuilderFillerParams::kBrepAcisDwg != m_params.destinationBrepType()) { //return as ge curve OdGeTempCurve2dPtr paramCurve(loEdTrav.getParamCurve()); if (!paramCurve.get()) return nullptr; if (paramCurve->type() != OdGe::kExternalCurve2d) return paramCurve.release(); } //return as nurb curve OdGeNurbCurve2d nurbCurve2d; if (odbrOK == loEdTrav.getParamCurveAsNurb(nurbCurve2d)) { return nurbCurve2d.copy(); } OdGeTempCurve2dPtr paramCurve(loEdTrav.getParamCurve()); if (!paramCurve.get()) return nullptr; if (OdGe::kNurbCurve2d == paramCurve->type()) return paramCurve.release(); return OdGeNurbCurve2d::convertFrom(paramCurve.get(), m_toleranceRestore2dCurve, true); } bool OdBrepBuilderFillerHelper::checkCurve2dTo3d(const OdGeSurface& surf, const OdGeCurve3d& curve3d, OdGeCurve2d& curve2d, OdGeTol& tol) const { OdGePoint3d curve3dStart; OdGePoint3d curve3dEnd; OdGePoint2d curve2dStart = getStartPoint(curve2d); OdGePoint2d curve2dEnd = getEndPoint(curve2d); if (!curve3d.hasStartPoint(curve3dStart) || !curve3d.hasEndPoint(curve3dEnd)) { return false; } OdGePoint3d surf3dStart = surf.evalPoint(curve2dStart); OdGePoint3d surf3dEnd = surf.evalPoint(curve2dEnd); ODA_ASSERT_ONCE(tol.equalPoint() < 0.); tol = calcEdgeTol(surf, curve3d); if ((surf3dStart.isEqualTo(curve3dStart, tol) && surf3dEnd.isEqualTo(curve3dEnd, tol)) || (surf3dStart.isEqualTo(curve3dEnd, tol) && surf3dEnd.isEqualTo(curve3dStart, tol))) { return true; } const OdGeSurfaceCurve2dTo3d surfCurve(&curve2d, &surf, OdGeSurfaceCurve2dTo3d::kReference); double paramNewStart; double paramNewEnd; // surface curve should intersect edge end points if (!surfCurve.isOn(curve3dStart, paramNewStart, tol) || !surfCurve.isOn(curve3dEnd, paramNewEnd, tol)) return false; OdGeInterval interval; curve2d.getInterval(interval); double paramStart = interval.lowerBound(); double paramEnd = interval.upperBound(); if (paramNewStart > paramNewEnd) { double tmp = paramNewStart; paramNewStart = paramNewEnd; paramNewEnd = tmp; } if (paramNewStart <= paramStart && paramNewEnd >= paramEnd) return true; if (surf3dStart.isEqualTo(surf3dEnd, tol)) { // closed surface bool resFull = false; if (paramNewEnd - paramNewStart < m_toleranceInterval) { if (OdEqual(paramStart, paramNewStart, m_toleranceInterval) || OdEqual(paramEnd, paramNewEnd, m_toleranceInterval)) return true; resFull = true; paramNewEnd = paramNewStart; } if (resFull || (curve3d.isOn(surf3dStart, tol) && !OdEqual(paramStart, paramNewStart, m_toleranceInterval) && !OdEqual(paramEnd, paramNewEnd, m_toleranceInterval))) { if (curve2d.type() != OdGe::kNurbCurve2d) { ODA_FAIL_ONCE(); //do we need to consider this case? return false; } OdGeNurbCurve2d& geNurb2d = static_cast(curve2d); OdGeTempNurbCurve2dPtr pNurb2dCopy(static_cast(geNurb2d.copy())); pNurb2dCopy->hardTrimByParams(paramStart, paramNewStart); geNurb2d.hardTrimByParams(paramNewEnd, paramEnd); geNurb2d.transformBy(OdGeMatrix2d::translation(curve2dStart - curve2dEnd)); geNurb2d.joinWith(*pNurb2dCopy); return true; } } // result is part of uv-curve if (paramNewStart < paramStart) paramNewStart = paramStart; if (paramNewEnd > paramEnd) paramNewEnd = paramEnd; if (paramNewStart == paramNewEnd) return false; ODA_VERIFY_ONCE(curve2d.setInterval(OdGeInterval(paramNewStart, paramNewEnd))); return true; } void OdBrepBuilderFillerHelper::moveParamCurveNurbInterval(const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const { OdGeNurbCurve2d& geNurb2d = static_cast(*pCurve2d); OdGeInterval curv3dInt, curv2dInt; pCurve3d->getInterval(curv3dInt); geNurb2d.getInterval(curv2dInt); //OdGeNurbCurve2d can have interval < knots. This code scales knots so that curv2dInt will be equal to curv3dInt and 2d geometry won't change const OdGeKnotVector& aKt = geNurb2d.knots(); ODA_ASSERT_ONCE(!aKt.isEmpty()); double a = (curv3dInt.upperBound() - curv3dInt.lowerBound()) / (curv2dInt.upperBound() - curv2dInt.lowerBound()); double b = curv3dInt.lowerBound() - a * curv2dInt.lowerBound(); for (int i = 0; i < aKt.length(); ++i) { geNurb2d.setKnotAt(i, a * aKt[i] + b); } //clamp knots (due to machine epsilon) if (aKt.startParam() > curv3dInt.lowerBound()) { for (int i = 0; i <= geNurb2d.degree(); ++i) { ODA_ASSERT_ONCE(OdEqual(curv3dInt.lowerBound(), aKt[i], odmax(aKt.tolerance(), 1e-12))); geNurb2d.setKnotAt(i, curv3dInt.lowerBound()); } } if (aKt.endParam() < curv3dInt.upperBound()) { for (int i = geNurb2d.numControlPoints(); i < aKt.length(); ++i) { ODA_ASSERT_ONCE(OdEqual(curv3dInt.upperBound(), aKt[i], odmax(aKt.tolerance(), 1e-12))); geNurb2d.setKnotAt(i, curv3dInt.upperBound()); } } bool intervalSet = geNurb2d.setInterval(curv3dInt); ODA_VERIFY_ONCE(intervalSet); } void OdBrepBuilderFillerHelper::moveParamIntoExtents(const OdGeSurface* pSurf, const OdGeCurve2d* pCurve2d, OdGePoint2d& pnt) const { bool bClosedInU = pSurf->isClosedInU(); bool bClosedInV = pSurf->isClosedInV(); if (!bClosedInU && !bClosedInV) { return; } OdGeInterval iu, iv; pSurf->getEnvelope(iu, iv); OdGeExtents2d extents; OdGePoint2dArray pnts; OdGeInterval intCur; pCurve2d->getInterval(intCur); pCurve2d->getSamplePoints(intCur.lowerBound(), intCur.upperBound(), 0., pnts); extents.addPoints(pnts); if (bClosedInU) { double dI = iu.length(); while (pnt.x > extents.maxPoint().x + m_toleranceInterval) pnt.x -= dI; while (pnt.x < extents.minPoint().x - m_toleranceInterval) pnt.x += dI; // uv should be as close to curveExtents as possible if (pnt.x > extents.maxPoint().x + m_toleranceInterval && pnt.x - extents.maxPoint().x > extents.minPoint().x - (pnt.x - dI)) { pnt.x -= dI; } } if (bClosedInV) { double dI = iv.length(); while (pnt.y > extents.maxPoint().y + m_toleranceInterval) pnt.y -= dI; while (pnt.y < extents.minPoint().y - m_toleranceInterval) pnt.y += dI; // uv should be as close to curveExtents as possible if (pnt.y > extents.maxPoint().y + m_toleranceInterval && pnt.y - extents.maxPoint().y > extents.minPoint().y - (pnt.y - dI)) { pnt.y -= dI; } } } OdResult OdBrepBuilderFillerHelper::moveParamCurveInterval(const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const { OdGe::EntityId curve2dType = pCurve2d->type(); if (OdGe::kNurbCurve2d == curve2dType) { moveParamCurveNurbInterval(pCurve3d, pCurve2d); return checkRet(eOk); } // TODO not nurb curve return checkRet(eNotImplemented); } bool OdBrepBuilderFillerHelper::checkCurveOnSurf(const OdGeSurface& surf, const OdGeCurve3d& curve3d, OdGeCurve2d& curve2d, const OdGeTol& tol) const { OdGeInterval curve2dInt; curve2d.getInterval(curve2dInt); const int pointsNum = 4; const double testParams[pointsNum] = { 0., 1 / 3., 2 / 3., 1. }; OdGeExtents2d pointsBox; for (int iPoint = 0; iPoint < pointsNum; ++iPoint) { OdGePoint2d paramPoint = curve2d.evalPoint(curve2dInt.eval(testParams[iPoint])); pointsBox.addPoint(paramPoint); OdGePoint3d surfPoint = surf.evalPoint(paramPoint); if (!curve3d.isOn(surfPoint, tol)) return false; } // Simple test that uv-curve lies inside surface domain. // Maybe need something for other surfaces. if (OdGe::kNurbSurface == surf.type()) { const OdGeNurbSurface& nurbsSurf = static_cast(surf); OdGeUvBox uvBox; nurbsSurf.getEnvelope(uvBox); for (int idx = 0; idx < 2; ++idx) uvBox[idx].setTolerance(m_toleranceInterval); double tmp; return (nurbsSurf.isPeriodicInU(tmp) || (uvBox.u().contains(pointsBox.minPoint().x) && uvBox.u().contains(pointsBox.maxPoint().x))) && (nurbsSurf.isPeriodicInV(tmp) || (uvBox.v().contains(pointsBox.minPoint().y) && uvBox.v().contains(pointsBox.maxPoint().y))); } return true; } bool OdBrepBuilderFillerHelper::checkCurveOnSurf(const OdGeSurface& surf, OdGeCurve2d& curve2d) const { if (OdGe::kNurbSurface != surf.type()) return true; OdGeInterval curve2dInt; curve2d.getInterval(curve2dInt); const int pointsNum = 4; const double testParams[pointsNum] = { 0., 1 / 3., 2 / 3., 1. }; OdGeExtents2d pointsBox; for (int iPoint = 0; iPoint < pointsNum; ++iPoint) pointsBox.addPoint(curve2d.evalPoint(curve2dInt.eval(testParams[iPoint]))); const OdGeNurbSurface& nurbsSurf = static_cast(surf); OdGeUvBox uvBox; nurbsSurf.getEnvelope(uvBox); for (int idx = 0; idx < 2; ++idx) uvBox[idx].setTolerance(m_toleranceInterval); double tmp; return (nurbsSurf.isPeriodicInU(tmp) || (uvBox.u().contains(pointsBox.minPoint().x) && uvBox.u().contains(pointsBox.maxPoint().x))) && (nurbsSurf.isPeriodicInV(tmp) || (uvBox.v().contains(pointsBox.minPoint().y) && uvBox.v().contains(pointsBox.maxPoint().y))); } bool OdBrepBuilderFillerHelper::checkParamCurve(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d, bool goodUvLoop, bool forceUvLoop, OdGeTol& tol) const { if (m_params.isOldUvCurveHandling()) { if (!checkCurve2dTo3d(*pSurf, *pCurve3d, *pCurve2d, tol)) return false; } else { if (!goodUvLoop) { if (!checkCurve2dTo3d(*pSurf, *pCurve3d, *pCurve2d, tol)) return false; if (!checkCurveOnSurf(*pSurf, *pCurve3d, *pCurve2d, tol)) return false; } else if (!forceUvLoop) { if (!checkCurveOnSurf(*pSurf, *pCurve2d)) return false; } } return eOk == fixParamCurve(pSurf, pCurve3d, pCurve2d) || goodUvLoop; } OdResult OdBrepBuilderFillerHelper::fixParamCurve(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const { ODA_ASSERT_ONCE(pSurf && pCurve3d); if (!pCurve2d) { return checkRet(eNullPtr); } bool bCoincide(true); OdGeTol coincideTol(m_toleranceCoincide, OdGeContext::gTol.equalVector()); if (geIsDir2dOnSurfCoincide3d(pSurf, pCurve3d, pCurve2d, bCoincide, coincideTol) == eOk) { if (!bCoincide) pCurve2d->reverseParam(); } else return checkRet(eInvalidCurve); return checkRet(fixParamCurveInterval(pCurve3d, pCurve2d)); } OdResult OdBrepBuilderFillerHelper::fixParamCurveInterval(const OdGeCurve3d* pCurve3d, OdGeCurve2d* pCurve2d) const { if (m_params.isMake2dIntervalInclude3d()) { OdGeInterval curv3dInt, curv2dInt; pCurve3d->getInterval(curv3dInt); pCurve2d->getInterval(curv2dInt); if (!areIntervalsEqual(curv2dInt, curv3dInt)) { OdResult status = moveParamCurveInterval(pCurve3d, pCurve2d); if (eOk != status) return checkRet(status); } } return checkRet(eOk); } static OdGeCurve2d* restoreUvCurveAsNurb(const OdGeCurve3d* pCurve3d, const OdGeSurface* pSurf, const OdGeTol& tol) { OdGeTempCurve2dPtr pProjCurve(OdGeCurve2d::restoreUvCurve(pCurve3d, pSurf, tol)); if (!pProjCurve.get()) return NULL; if (OdGe::kNurbCurve2d == pProjCurve->type()) return pProjCurve.release(); return OdGeNurbCurve2d::convertFrom(pProjCurve.get(), tol); } OdResult OdBrepBuilderFillerHelper::createParamCurve(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2dPtr& pCurve2d, OdGeTol tol) const { ODA_ASSERT_ONCE(pSurf && pCurve3d); if (tol.equalPoint() < 0.) tol = calcEdgeTol(*pSurf, *pCurve3d); const int maxProj = 3; for (int tryProj = 0; tryProj < maxProj; ++tryProj) { pCurve2d = restoreUvCurveAsNurb(pCurve3d, pSurf, tol); if (!pCurve2d.isNull() || tol.equalPoint() > 1.) break; tol.setEqualPoint(sqrt(tol.equalPoint())); } if (!pCurve2d) { return checkRet(eCreateFailed); } return checkRet(eOk); } OdGeTol OdBrepBuilderFillerHelper::calcEdgeTol(const OdGeSurface& surf, const OdGeCurve3d& curve3d) const { const int pointsCount = 10; m_testPoints.clear(); curve3d.getSamplePoints(pointsCount, m_testPoints); double calcTol = 0.; for (OdGePoint3dArray::size_type iPoint = 0; iPoint < m_testPoints.size(); ++iPoint) { double dist = surf.distanceTo(m_testPoints.getAt(iPoint)); if (calcTol < dist) calcTol = dist; } calcTol *= 1.1; double tolExtDiag = 0.; try { tolExtDiag = curve3d.getGeomExtents().diagonal().length() * 1e-4; } catch (const OdError& err) { if (eInvalidInput != err.code()) throw; } double tolExtDiagLimited = odmin(m_toleranceDiffPoints, tolExtDiag); double resTol = odmax(calcTol, tolExtDiagLimited); return OdGeTol(odmax(resTol, m_toleranceRestore2dCurve), OdGeContext::gTol.equalVector()); } OdResult OdBrepBuilderFillerHelper::fixSingleCoedgeLoopByEdgeEnd(OdGeSurface& surf, OdGeCurve3d& curve3d, OdGeCurve2d& curve2d) { OdGePoint3d curve3dStart = getStartPoint(curve3d); OdGePoint3d curve3dEnd = getEndPoint(curve3d); OdGePoint2d curve2dStart = getStartPoint(curve2d); OdGePoint2d curve2dEnd = getEndPoint(curve2d); OdGePoint3d surf3dStart = surf.evalPoint(curve2dStart); OdGePoint3d surf3dEnd = surf.evalPoint(curve2dEnd); if (!curve3dStart.isEqualTo(curve3dEnd, m_toleranceDiffPoints) || !surf3dStart.isEqualTo(surf3dEnd, m_toleranceDiffPoints)) return eOk; if (surf3dStart.isEqualTo(curve3dStart, m_toleranceDiffPoints)) return eOk; const OdGeSurfaceCurve2dTo3d surfCurve(&curve2d, &surf, OdGeSurfaceCurve2dTo3d::kReference); double paramSplit = surfCurve.paramOf(curve3dStart); OdGeInterval interval; curve2d.getInterval(interval); double paramStart = interval.lowerBound(); double paramEnd = interval.upperBound(); if (OdEqual(paramSplit, paramStart, m_toleranceInterval) || OdEqual(paramSplit, paramEnd, m_toleranceInterval)) return eOk; if (curve2d.type() != OdGe::kNurbCurve2d) return eNotImplemented; OdGeNurbCurve2d& geNurb2d = static_cast(curve2d); OdGeTempNurbCurve2dPtr pNurb2dCopy(static_cast(geNurb2d.copy())); pNurb2dCopy->hardTrimByParams(paramStart, paramSplit); geNurb2d.hardTrimByParams(paramSplit, paramEnd); geNurb2d.transformBy(OdGeMatrix2d::translation(curve2dStart - curve2dEnd)); geNurb2d.joinWith(*pNurb2dCopy); return eOk; } OdResult OdBrepBuilderFillerHelper::getParamCurveFixed(const OdGeSurface* pSurf, const OdGeCurve3d* pCurve3d, OdGeCurve2dPtr& pCurve2d, bool goodUvLoop, bool forceUvLoop) const { OdGeTol tol(-1.); if (!pCurve2d.isNull() && !checkParamCurve(pSurf, pCurve3d, pCurve2d, goodUvLoop, forceUvLoop, tol)) pCurve2d = NULL; if (pCurve2d.isNull() && needToProjectParamCurve(pSurf)) { //we have rejected input parametric curve but if output data is ACIS, then parametric curves should be used in some cases //so we create a new one OdResult eStatus = createParamCurve(pSurf, pCurve3d, pCurve2d, tol); if (eOk != eStatus) return checkRet(eStatus); } return checkRet(eOk); } static bool arePointsEqual(const OdGePoint3dArray& pts1, const OdGePoint3dArray& pts2, double& dDist, const OdGeTol& tol = OdGeContext::gTol) { dDist = 0.0; if (pts1.length() != pts2.length()) { return false; } for (unsigned int k = 0; k < pts1.length(); k++) { double dCurDist = (pts1[k] - pts2[k]).lengthSqrd(); if (dCurDist > dDist) { dDist = dCurDist; } if (dCurDist > tol.equalPoint() * tol.equalPoint()) { return false; } } return true; } OdResult OdBrepBuilderFillerHelper::addFaceExplicitLoop(BrepBuilderInitialSurface& surfData) { OdGeInterval ivU, ivV; surfData.pSurf->getEnvelope(ivU, ivV); if (surfData.pSurf->isKindOf(OdGe::kSphere) && surfData.pSurf->isClosedInV(m_toleranceInterval)) { // Do nothing for closed sphere return checkRet(eOk); } else if (surfData.pSurf->isKindOf(OdGe::kTorus) && surfData.pSurf->isClosedInU(m_toleranceInterval) && surfData.pSurf->isClosedInV(m_toleranceInterval)) { // Do nothing for closed torus return checkRet(eOk); } else if (!surfData.pSurf->isNormalReversed() && ivU.isBounded() && ivV.isBounded()) // Add new loop by envelope borders { const unsigned int iBorders = 4; const unsigned int iPtsCnt = 7; OdGePoint2dArray aParams(iBorders); OdGeCurve3dPtrArray aIsoparamCurves(iBorders); OdGeCurve2dPtrArray aParamCurves(iBorders); OdBrepBuilder::EntityDirection aDirs[iBorders] = { OdBrepBuilder::kForward, OdBrepBuilder::kForward, OdBrepBuilder::kReversed, OdBrepBuilder::kReversed }; aParams.resize(iBorders); aParamCurves.resize(iBorders); aIsoparamCurves.resize(iBorders); // Create borders curves aIsoparamCurves[0] = surfData.pSurf->makeIsoparamCurve(false, ivV.lowerBound()); aIsoparamCurves[1] = surfData.pSurf->makeIsoparamCurve(true, ivU.upperBound()); aIsoparamCurves[2] = surfData.pSurf->makeIsoparamCurve(false, ivV.upperBound()); aIsoparamCurves[3] = surfData.pSurf->makeIsoparamCurve(true, ivU.lowerBound()); // Check makeIsoparamCurve results (can be NULL) if (aIsoparamCurves[0].isNull() || aIsoparamCurves[1].isNull() || aIsoparamCurves[2].isNull() || aIsoparamCurves[3].isNull()) { return checkRet(eNullEdgeCurve); } // Change loop direction for reversed case if (surfData.direction == OdBrepBuilder::kReversed) { aIsoparamCurves.reverse(); aParamCurves.reverse(); ODA_FAIL_M_ONCE("Reversed face without loops."); } // Create corner parametric points aParams[0] = OdGePoint2d(ivU.lowerBound(), ivV.lowerBound()); aParams[1] = OdGePoint2d(ivU.upperBound(), ivV.lowerBound()); aParams[2] = OdGePoint2d(ivU.upperBound(), ivV.upperBound()); aParams[3] = OdGePoint2d(ivU.lowerBound(), ivV.upperBound()); if (needToProjectParamCurve(surfData.pSurf)) { for (unsigned int i = 0; i < iBorders; i++) { aParamCurves[i] = new OdGeNurbCurve2d(OdGeLineSeg2d(aParams[i], aParams[(i + 1) % iBorders])); OdResult eStatus = fixParamCurve(surfData.pSurf, aIsoparamCurves[i], aParamCurves[i]); if (eStatus != eOk) { return checkRet(eStatus); } } } // Create new loop BrepBuilderInitialLoop loopData; for (unsigned int i = 0; i < iBorders; i++) { // Degenerate edge OdGe::EntityId iType; if (aIsoparamCurves[i]->isDegenerate(iType, 1.e-10/*, m_toleranceCoincide*/)) { continue; } // Create coedge BrepBuilderInitialCoedge& coedgeData = *loopData.coedges.append(); // Reverse direction of two coedges coedgeData.direction = aDirs[i]; coedgeData.curve = aParamCurves[i]; // Create sample points array OdGePoint3dArray aSmplPts; aIsoparamCurves[i]->getSamplePoints(iPtsCnt, aSmplPts); // Calculate distance from first to last points. OdGeInterval iv; aIsoparamCurves[i]->getInterval(iv); double dIvLength = iv.length(); std::multiset::iterator start = m_edgesAdded.lower_bound(EdgeCurveCompareData(dIvLength - m_toleranceInterval)); std::multiset::iterator end = m_edgesAdded.upper_bound(EdgeCurveCompareData(dIvLength + m_toleranceInterval)); if (start != end) { double dMinDist = std::numeric_limits::max(), dCurDist; std::multiset::iterator thebest; bool bFound = false; for (std::multiset::iterator it = start; it != end; ++it) { if (arePointsEqual(aSmplPts, it->aSamplePts, dCurDist, m_toleranceInterval) && dCurDist < dMinDist) { dMinDist = dCurDist; thebest = it; bFound = true; } } if (bFound) { coedgeData.edgeIndex = thebest->idEdge; continue; } } // Create edge BrepBuilderInitialEdge& edgeData = *m_initialData.edges.append(); edgeData.curve = aIsoparamCurves[i]; // Fill edge visual edgeData.hasColor = surfData.hasColor; if (edgeData.hasColor) edgeData.color = surfData.color; // Here we can't get OdGsMarker. There is no edge to getGsMarker. coedgeData.edgeIndex = m_initialData.edges.size() - 1; m_edges[(OdUInt64)edgeData.curve.get()] = coedgeData.edgeIndex; m_edgesAdded.insert(EdgeCurveCompareData(edgeData.curve.get(), coedgeData.edgeIndex, aSmplPts, dIvLength)); } surfData.loops.append(loopData); } else { return checkRet(eNotApplicable); } return checkRet(eOk); } static bool isAcisAnalytic(const OdGeSurface* pSurf) { OdGe::EntityId sType = pSurf->type(); return sType == OdGe::kPlane || sType == OdGe::kBoundedPlane || sType == OdGe::kPlanarEnt || sType == OdGe::kSphere || sType == OdGe::kCylinder || sType == OdGe::kCone || sType == OdGe::kEllipCylinder || sType == OdGe::kEllipCone || sType == OdGe::kTorus; } bool OdBrepBuilderFillerHelper::askParamCurve(const OdGeSurface* pSurf) const { if (m_params.isSkipCoedge2dCurve()) return false; if (OdBrepBuilderFillerParams::kBrepAcisDgn == m_params.destinationBrepType() || OdBrepBuilderFillerParams::kBrepAcisDwg == m_params.destinationBrepType()) { return !isAcisAnalytic(pSurf); } return true; } bool OdBrepBuilderFillerHelper::needToProjectParamCurve(const OdGeSurface* pSurf) const { if (OdBrepBuilderFillerParams::kBrepPS == m_params.destinationBrepType()) return true; if (OdBrepBuilderFillerParams::kBrepAcisDgn == m_params.destinationBrepType() || OdBrepBuilderFillerParams::kBrepAcisDwg == m_params.destinationBrepType()) { return !isAcisAnalytic(pSurf); } return false; } // Face OdGeSurfacePtr OdBrepBuilderFillerHelper::checkExtSurface(const OdGeSurface* resSurf, const OdBrFace &face) const { const OdGeExternalSurface* extSurf = static_cast(resSurf); OdGeSurface* pSurf = NULL; if (extSurf->isNativeSurface(pSurf)) return pSurf; OdGeNurbSurface nurbFace; OdBrErrorStatus err = face.getSurfaceAsNurb(nurbFace); if (odbrOK != err) { return NULL; } return new OdGeNurbSurface(nurbFace); } OdGeSurfacePtr OdBrepBuilderFillerHelper::getFaceSurface(const OdBrFace& face) const { OdGeSurfacePtr surf(face.getSurface()); if (!surf) { OdGeNurbSurface nurbSurf; try { if (odbrOK == face.getSurfaceAsNurb(nurbSurf)) { return static_cast(nurbSurf.copy()); } } catch (const OdError& err) { if (err.code() != eNotImplemented) { throw err; } } return NULL; } OdGe::EntityId entType = surf->type(); if (OdGe::kExternalBoundedSurface == entType) { OdGeSurface* tResSurf = NULL; const OdGeExternalBoundedSurface* extSurf = static_cast(surf.get()); extSurf->getBaseSurface(tResSurf); OdGeTempSurfacePtr resSurf(tResSurf); if (resSurf.get() && resSurf->type() != OdGe::kExternalSurface) surf = resSurf.release(); else if (resSurf.get() && resSurf->type() == OdGe::kExternalSurface) surf = checkExtSurface(resSurf.get(), face); } else if (OdGe::kExternalSurface == entType) surf = checkExtSurface(surf, face); return surf; } OdResult OdBrepBuilderFillerHelper::fixFaceSurface(OdGeSurface& surf) const { OdGe::EntityId surfType = surf.type(); switch (surfType) { case OdGe::kEllipCone: fixEllipConeRRatio(static_cast(surf)); break; case OdGe::kEllipCylinder: fixEllipCylRRatio(static_cast(surf)); break; case OdGe::kPlane: return checkRet(fixPlaneUV(static_cast(surf))); case OdGe::kBoundedPlane: return checkRet(fixPlaneUV(static_cast(surf))); } return checkRet(eOk); } void OdBrepBuilderFillerHelper::fixEllipConeRRatio(OdGeEllipCone& ellipCone) const { if (!m_params.isMakeEllipMajorGreaterMinor()) return; OdGeVector3d vecMajAxis = ellipCone.majorAxis(); OdGeVector3d vecMinAxis = ellipCone.minorAxis(); const double majorRadius = ellipCone.majorRadius(); const double minorRadius = ellipCone.minorRadius(); if (OdZero(majorRadius, m_toleranceInterval) || !vecMajAxis.isPerpendicularTo(vecMinAxis, OdGeContext::gTol)) { throw OdError(eNotImplementedYet); } double dRaduisRatio = minorRadius / majorRadius; if (OdGreater(dRaduisRatio, 1, m_toleranceDiffPoints)) { double dSinAngle, dCosAngle; ellipCone.getHalfAngle(dCosAngle, dSinAngle); //this code need for determine sign of sin if (!(ellipCone.isNormalReversed() ^ ellipCone.isOuterNormal())) { dCosAngle *= -1; } if (!(ellipCone.baseCenter() - ellipCone.axisOfSymmetry() * (majorRadius * dCosAngle / dSinAngle)).isEqualTo(ellipCone.apex())) { dSinAngle *= -1; } double dStartAngle, dEndAngle; ellipCone.getAngles(dStartAngle, dEndAngle); dStartAngle -= OdaPI2; dEndAngle -= OdaPI2; OdGeInterval height; ellipCone.getHeight(height); ellipCone.set(dCosAngle, dSinAngle, ellipCone.baseCenter(), minorRadius, majorRadius, ellipCone.axisOfSymmetry(), vecMinAxis, height, dStartAngle, dEndAngle); } } void OdBrepBuilderFillerHelper::fixEllipCylRRatio(OdGeEllipCylinder& ellipCylinder) const { if (!m_params.isMakeEllipMajorGreaterMinor()) return; OdGeVector3d vecMajAxis = ellipCylinder.majorAxis(); OdGeVector3d vecMinAxis = ellipCylinder.minorAxis(); const double majorRadius = ellipCylinder.majorRadius(); const double minorRadius = ellipCylinder.minorRadius(); if (OdZero(majorRadius, m_toleranceInterval) || !vecMajAxis.isPerpendicularTo(vecMinAxis, OdGeContext::gTol)) { throw OdError(eNotImplementedYet); } double dRaduisRatio = minorRadius / majorRadius; if (OdGreater(dRaduisRatio, 1, m_toleranceDiffPoints)) { double dStartAngle, dEndAngle; ellipCylinder.getAngles(dStartAngle, dEndAngle); dStartAngle -= OdaPI2; dEndAngle -= OdaPI2; OdGeInterval height; ellipCylinder.getHeight(height); ellipCylinder.set(majorRadius, minorRadius, ellipCylinder.origin(), ellipCylinder.axisOfSymmetry(), vecMinAxis.negate(), height, dStartAngle, dEndAngle); } } template OdResult OdBrepBuilderFillerHelper::fixPlaneUV(PlaneClass& plane) const { bool isConvToAcis = OdBrepBuilderFillerParams::kBrepMd != m_params.sourceBrepType() && (OdBrepBuilderFillerParams::kBrepAcisDwg == m_params.destinationBrepType() || OdBrepBuilderFillerParams::kBrepAcisDgn == m_params.destinationBrepType()); if (!isConvToAcis) return checkRet(eOk); OdGePoint3d origin; OdGeVector3d uAxis, vAxis; plane.getCoordSystem(origin, uAxis, vAxis); if (!OdZero(uAxis.dotProduct(vAxis), OdGeContext::gTol.equalVector())) { bool isNormalReversed = plane.isNormalReversed(); vAxis = plane.normal().crossProduct(uAxis); OdGeError geErr = OdGe::kOk; vAxis.normalize(OdGeContext::gTol, geErr); if (OdGe::kOk != geErr) return checkRet(eInvalidInput); plane.set(origin, uAxis, vAxis); if (isNormalReversed) plane.reverseNormal(); } return checkRet(eOk); } //Vertex BrepBuilderInitialEdge::VertexIndex OdBrepBuilderFillerHelper::addVertex(const OdBrVertex& vertex) { auto pIt = m_vertices.find(vertex.getUniqueId()); if (pIt != m_vertices.end()) { return pIt->second; } BrepBuilderInitialVertex& initVertex = *m_initialData.vertices.append(); initVertex.point = vertex.getPoint(); if (m_params.isSetVertexGsMarkersTags()) initVertex.marker.first = odbrOK == vertex.getGsMarker(initVertex.marker.second); BrepBuilderInitialEdge::VertexIndex vertexIndex = m_initialData.vertices.size() - 1; m_vertices[vertex.getUniqueId()] = vertexIndex; return vertexIndex; } // Loop OdResult OdBrepBuilderFillerHelper::performLoopWithApex(const OdBrLoop& loop, OdGeCurve3dPtr& pCurve3d, OdGeCurve2dPtr& pCurve2d, OdBrVertex* vertex) const { OdBrLoopVertexTraverser loopVertTrav; if (odbrOK != loopVertTrav.setLoop(loop)) { return checkRet(eBadApexLoop); } OdBrVertex loopVertex = loopVertTrav.getVertex(); if (vertex) *vertex = loopVertex; OdGePoint3d point = loopVertex.getPoint(); if ((odbrOK != loopVertTrav.next()) || !loopVertTrav.done()) { // unexpected: more then one point return checkRet(eBadApexLoop); } pCurve3d = new OdGeLineSeg3d(point, point); pCurve2d = NULL; return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::splitOuterLoops(BrepBuilderInitialSurfaceArray& arrSurfaces) { ODA_ASSERT_ONCE(arrSurfaces.size() > 0); OdArrayBuffer::size_type currentFaceIdx = arrSurfaces.size() - 1; BrepBuilderInitialSurface* pSurfData = &arrSurfaces.last(); if (pSurfData->loops.size() <= 1) return checkRet(eOk); OdResult eStatus = eOk; const OdGeTol tol(m_toleranceRestore2dCurve, OdGeContext::gTol.equalVector()); OdArray loopIndices; struct CurveStore { std::vector data; ~CurveStore() { for (size_t iCurve = 0; iCurve < data.size(); ++iCurve) delete data[iCurve]; } } store; OdArray> loops; OdArray> revFlags; loops.reserve(pSurfData->loops.size()); revFlags.reserve(pSurfData->loops.size()); for (OdArrayBuffer::size_type iLoop = 0; iLoop < pSurfData->loops.size(); ++iLoop) { const BrepBuilderInitialCoedgeArray& coedges = pSurfData->loops.getAt(iLoop).coedges; ODA_ASSERT_ONCE(!coedges.isEmpty()); OdArray& loopCurves = *loops.append(); OdArray& loopRevFlags = *revFlags.append(); loopCurves.reserve(coedges.size()); loopRevFlags.reserve(coedges.size()); for (OdArrayBuffer::size_type iCoedge = 0; iCoedge < coedges.size(); ++iCoedge) { const BrepBuilderInitialCoedge& coedge = coedges.getAt(iCoedge); const OdGeCurve2d* pCurve = coedge.curve.get(); if (!pCurve) { store.data.push_back(nullptr); pCurve = store.data.back() = OdGeCurve2d::restoreUvCurve(m_initialData.edges.getAt(coedge.edgeIndex).curve, pSurfData->pSurf, tol); } loopCurves.append(pCurve); loopRevFlags.append(OdBrepBuilder::kForward != coedge.direction); } } //BIM-6275: in bimrv loops may be oriented incorrectly. We reverse them. // Unfortunately, coedges may have invalid orientations which we don't fix. // Note: in bimrv if face has 2 outer loops (1 outer and an "island"), there may be another face duplicating an "island". // Should we remove "islands" to avoid duplication? I think no. OdArray loopReverseFlags; eStatus = OdGeExt::calcLoopIncludeOrder(*pSurfData->pSurf, OdBrepBuilder::kForward != pSurfData->direction, loops, revFlags, loopIndices, loopReverseFlags, tol); if (eOk != eStatus) return checkRet(eStatus); for (OdArrayBuffer::size_type iLoopIdx = 0; iLoopIdx < loopReverseFlags.size(); ++iLoopIdx) { if (loopReverseFlags[iLoopIdx]) { pSurfData->loops[iLoopIdx].coedges.reverse(); for (OdArrayBuffer::size_type iCoedgeIdx = 0; iCoedgeIdx < pSurfData->loops[iLoopIdx].coedges.size(); ++iCoedgeIdx) { OdBrepBuilder::EntityDirection& dir = pSurfData->loops[iLoopIdx].coedges[iCoedgeIdx].direction; dir = (dir == OdBrepBuilder::kForward ? OdBrepBuilder::kReversed : OdBrepBuilder::kForward); } } } if (loopIndices.size() == 1) return checkRet(eOk); m_loopsSplit = true; for (OdArrayBuffer::size_type iLoopSeq = 1; iLoopSeq < loopIndices.size(); ++iLoopSeq) { // create a new face and move loop into to it BrepBuilderInitialSurface& surfDataNew = *arrSurfaces.append(); pSurfData = &arrSurfaces[currentFaceIdx]; surfDataNew.copyFaceExceptLoops(*pSurfData); const OdIntArray& loopIds = loopIndices.getAt(iLoopSeq); surfDataNew.loops.reserve(loopIds.size()); for (OdArrayBuffer::size_type iLoop = 0; iLoop < loopIds.size(); ++iLoop) { BrepBuilderInitialLoop& loop = pSurfData->loops[loopIds.getAt(iLoop)]; surfDataNew.loops.append()->coedges = std::move(loop.coedges); } } // remove extra loops from the old face for (OdArrayBuffer::size_type iLoop = 0; iLoop < pSurfData->loops.size(); ++iLoop) { if (pSurfData->loops.getAt(iLoop).coedges.isEmpty()) pSurfData->loops.removeAt(iLoop--); } return checkRet(eOk); } double OdBrepBuilderFillerHelper::calcCoedgeCurveTol(const OdGeCurve2d& curve0, const OdGeCurve2d& curve1) const { double tolExtDiag = 0.5 * 1e-4 * (curve0.getGeomExtents().diagonal().length() + curve1.getGeomExtents().diagonal().length()); return odmax(m_toleranceRestore2dCurve, tolExtDiag); } namespace { struct PointsDists { OdGePoint3d currPoints[4]; double dists[4]; bool isClosed; int minIdx; int minJdx; PointsDists() : isClosed(false), minIdx(0), minJdx(0) {} void init(const OdGeSurface& surf, const OdGeCurve2d& currCurve, const OdGeCurve2d& nextCurve, double tol) { currPoints[0] = surf.evalPoint(getStartPoint(currCurve)); currPoints[1] = surf.evalPoint(getEndPoint(currCurve)); add(surf, nextCurve, tol); } void add(const OdGeSurface& surf, const OdGeCurve2d& nextCurve, double tol) { currPoints[2] = surf.evalPoint(getStartPoint(nextCurve)); currPoints[3] = surf.evalPoint(getEndPoint(nextCurve)); isClosed = currPoints[0].isEqualTo(currPoints[1], tol) || currPoints[2].isEqualTo(currPoints[3], tol); dists[0] = currPoints[0].distanceSqrdTo(currPoints[2]); dists[1] = currPoints[0].distanceSqrdTo(currPoints[3]); dists[2] = currPoints[1].distanceSqrdTo(currPoints[2]); dists[3] = currPoints[1].distanceSqrdTo(currPoints[3]); minIdx = 0; minJdx = 1; for (int iDist = 1; iDist < 4; ++iDist) { if (dists[iDist] < dists[minIdx]) { minJdx = minIdx; minIdx = iDist; } else if (dists[iDist] < dists[minJdx]) minJdx = iDist; } } bool representsSeam(double tol2) const { return dists[minIdx] < tol2 && dists[minJdx] < tol2 && minIdx / 2 != minJdx / 2 && minIdx % 2 != minJdx % 2; } bool areEndsIntersecting(double tol2) const { return !isClosed && dists[minIdx] < tol2; } void step() { currPoints[0] = currPoints[2]; currPoints[1] = currPoints[3]; } bool calcCoedgeIntersections(const OdBrepBuilderFillerHelper& self, double currInt0, bool& currFull, bool& nextFull, double& currInt1, double& nextInt0, OdGeCurve2d* pCurrCurve, OdGeCurve2d* pNextCurve, double tol2) const { if (representsSeam(tol2)) { OdGeInterval currInt; pCurrCurve->getInterval(currInt); if (!OdEqual(currInt0, currInt.lowerBound(), self.toleranceInterval()) && !OdEqual(currInt0, currInt.upperBound(), self.toleranceInterval())) return false; currInt1 = currInt.upperBound(); currFull = true; nextFull = true; pNextCurve->getInterval(currInt); nextInt0 = currInt.lowerBound(); } else if (areEndsIntersecting(tol2)) { OdGeInterval currInt; pCurrCurve->getInterval(currInt); currInt1 = minIdx / 2 == 0 ? currInt.lowerBound() : currInt.upperBound(); pNextCurve->getInterval(currInt); nextInt0 = minIdx % 2 == 0 ? currInt.lowerBound() : currInt.upperBound(); } else { double curvesTol = self.calcCoedgeCurveTol(*pCurrCurve, *pNextCurve); try { OdGeCurveCurveInt2d curveInt(*pCurrCurve, *pNextCurve, OdGeTol(curvesTol, OdGeContext::gTol.equalVector())); if (1 != curveInt.numIntPoints() || 0 != curveInt.overlapCount()) return false; curveInt.getIntParams(0, currInt1, nextInt0); if (currFull) { OdGeInterval currInt; pCurrCurve->getInterval(currInt); if (!OdEqual(currInt1, currInt.lowerBound(), self.toleranceInterval()) && !OdEqual(currInt1, currInt.upperBound(), self.toleranceInterval())) return false; } } catch (...) { return false; } } return true; } }; } bool OdBrepBuilderFillerHelper::checkCoedgeLoop(OdGeSurface& surf, BrepBuilderInitialLoop& loopData) { // Try to use uv-curves for loop if (0 == loopData.coedges.size()) return true; if (1 == loopData.coedges.size()) { BrepBuilderInitialCoedge& coedgeData = loopData.coedges[0]; OdGePoint3d currPoints[2] = { surf.evalPoint(getStartPoint(*coedgeData.curve)), surf.evalPoint(getEndPoint(*coedgeData.curve)) }; return currPoints[0].isEqualTo(currPoints[1], m_toleranceDiffPoints); } double tol2 = m_toleranceDiffPoints * m_toleranceDiffPoints; if (2 == loopData.coedges.size()) { OdGeCurve2d* curves[2] = { loopData.coedges[0].curve, loopData.coedges[1].curve }; PointsDists points; points.init(surf, *curves[0], *curves[1], m_toleranceDiffPoints); if (points.currPoints[0].isEqualTo(points.currPoints[1], m_toleranceDiffPoints) || points.currPoints[2].isEqualTo(points.currPoints[3], m_toleranceDiffPoints)) { return points.currPoints[0].isEqualTo(points.currPoints[1], m_toleranceDiffPoints) && points.currPoints[0].isEqualTo(points.currPoints[2], m_toleranceDiffPoints) && points.currPoints[0].isEqualTo(points.currPoints[3], m_toleranceDiffPoints); } if (points.representsSeam(tol2)) return true; double params[4]; double curvesTol = calcCoedgeCurveTol(*curves[0], *curves[1]); try { OdGeCurveCurveInt2d curveInt(*curves[0], *curves[1], OdGeTol(curvesTol, OdGeContext::gTol.equalVector())); if (2 != curveInt.numIntPoints() || 0 != curveInt.overlapCount()) return false; for (int iInt = 0; iInt < 2; ++iInt) curveInt.getIntParams(iInt, params[0 + iInt], params[2 + iInt]); } catch (...) { return false; } for (int iCurve = 0; iCurve < 2; ++iCurve) { if (params[iCurve * 2 + 0] > params[iCurve * 2 + 1]) std::swap(params[iCurve * 2 + 0], params[iCurve * 2 + 1]); curves[iCurve]->setInterval(OdGeInterval(params[iCurve * 2 + 0], params[iCurve * 2 + 1])); } return true; } double lastInt1; bool lastFull = false; double currInt0; bool currFull = false; OdGeCurve2d* pCurrCurve; PointsDists points; { pCurrCurve = loopData.coedges.last().curve; OdGeCurve2d* pNextCurve = loopData.coedges[0].curve; points.init(surf, *pCurrCurve, *pNextCurve, m_toleranceDiffPoints); OdGeInterval currInt; pCurrCurve->getInterval(currInt); bool rc = points.calcCoedgeIntersections(*this, currInt.lowerBound(),// makes no sense here lastFull, currFull, lastInt1, currInt0, pCurrCurve, pNextCurve, tol2); if (!rc) return false; pCurrCurve = pNextCurve; points.step(); } for (OdArrayBuffer::size_type iCoedge = 1; iCoedge < loopData.coedges.size(); ++iCoedge) { OdGeCurve2d* pNextCurve = loopData.coedges[iCoedge].curve; points.add(surf, *pNextCurve, m_toleranceDiffPoints); double currInt1; double nextInt0; bool nextFull = false; bool rc = points.calcCoedgeIntersections(*this, currInt0, currFull, nextFull, currInt1, nextInt0, pCurrCurve, pNextCurve, tol2); if (!rc) return false; if (!currFull) { if (OdEqual(currInt0, currInt1, m_toleranceInterval)) return false; if (currInt0 > currInt1) std::swap(currInt0, currInt1); pCurrCurve->setInterval(OdGeInterval(currInt0, currInt1)); } pCurrCurve = pNextCurve; currInt0 = nextInt0; currFull = nextFull; points.step(); } if (!lastFull) { if (!currFull) { if (OdEqual(currInt0, lastInt1, m_toleranceInterval)) return false; if (currInt0 > lastInt1) std::swap(currInt0, lastInt1); pCurrCurve->setInterval(OdGeInterval(currInt0, lastInt1)); } else { OdGeInterval currInt; pCurrCurve->getInterval(currInt); if (!OdEqual(lastInt1, currInt.lowerBound(), m_toleranceInterval) && !OdEqual(lastInt1, currInt.upperBound(), m_toleranceInterval)) return false; } } else { OdGeInterval currInt; pCurrCurve->getInterval(currInt); if (!OdEqual(currInt0, currInt.lowerBound(), m_toleranceInterval) && !OdEqual(currInt0, currInt.upperBound(), m_toleranceInterval)) return false; } return true; } OdResult OdBrepBuilderFillerHelper::findParentEdge(BrepBuilderInitialSurfaceArray& arrFaces, EdgeIndex edgeIdx, CoedgeIndex& coedgeIndex, const OdArray& parentEdgeIndices, EdgeIndex& parentIdx, std::map>& edgesCoedges) { OdGeCurve3d* childCurve = m_initialData.edges[edgeIdx].curve; OdGeTol tol(m_toleranceCoincide, OdGeContext::gTol.equalVector()); OdGePoint3d childStartPoint, childEndPoint; if (!childCurve->hasStartPoint(childStartPoint) || !childCurve->hasEndPoint(childEndPoint)) return checkRet(eInvalidCurve); //find curve which contains childCurve for (unsigned i = 0; i < parentEdgeIndices.size(); ++i) { const OdGeCurve3d* parentCurve = m_initialData.edges[parentEdgeIndices[i]].curve; if (childCurve->type() != parentCurve->type() || !parentCurve->isOn(childStartPoint, tol) || !parentCurve->isOn(childEndPoint, tol)) continue; if (childCurve->type() != OdGe::kLineSeg3d) { const int numSamples = childCurve->type() == OdGe::kCircArc3d ? 1 : 11; for (int idxSample = 1; idxSample < numSamples; ++idxSample) if (!parentCurve->isOn(childCurve->midPoint(double(idxSample) / numSamples), tol)) continue; } parentIdx = parentEdgeIndices[i]; return checkRet(eOk); } //BIM-6275: parent edges may be united into one child edge in face regions. In this case we split child edge for (unsigned i = 0; i < parentEdgeIndices.size(); ++i) { const OdGeCurve3d* parentCurve = m_initialData.edges[parentEdgeIndices[i]].curve; if (childCurve->type() != parentCurve->type()) continue; OdGePoint3d parentStartPoint, parentEndPoint; if (!parentCurve->hasStartPoint(parentStartPoint) || !parentCurve->hasEndPoint(parentEndPoint)) return checkRet(eInvalidCurve); double splitParam; if (!parentStartPoint.isEqualTo(childStartPoint, m_toleranceCoincide) && !parentStartPoint.isEqualTo(childEndPoint, m_toleranceCoincide) && childCurve->isOn(parentStartPoint, splitParam, tol) || !parentEndPoint.isEqualTo(childStartPoint, m_toleranceCoincide) && !parentEndPoint.isEqualTo(childEndPoint, m_toleranceCoincide) && childCurve->isOn(parentEndPoint, splitParam, tol)) { OdGeInterval oldInterval; childCurve->getInterval(oldInterval); OdGeInterval newInterval = oldInterval; BrepBuilderInitialCoedge& coedge = arrFaces[coedgeIndex.faceIdx].loops[coedgeIndex.loopIdx].coedges[coedgeIndex.coedgeIdx]; oldInterval.setUpper(splitParam); newInterval.setLower(splitParam); childCurve->setInterval(oldInterval); //TODO: trim nurbs? coedge.curve = nullptr; BrepBuilderInitialCoedge newCoedge = coedge; m_initialData.edges.push_back(m_initialData.edges[edgeIdx]); m_initialData.edges.last().curve = m_initialData.edges.last().curve->copy(); m_initialData.edges.last().curve->setInterval(newInterval); //TODO: trim nurbs? EdgeIndex newEdgeIdx = m_initialData.edges.size() - 1; newCoedge.edgeIndex = newEdgeIdx; CoedgeIndex newCoedgeIndex = coedgeIndex; arrFaces[coedgeIndex.faceIdx].loops[coedgeIndex.loopIdx].coedges.insertAt( coedge.direction == OdBrepBuilder::kForward ? coedgeIndex.coedgeIdx + 1 : coedgeIndex.coedgeIdx++, newCoedge); newCoedgeIndex.coedgeIdx = coedge.direction == OdBrepBuilder::kForward ? coedgeIndex.coedgeIdx + 1 : coedgeIndex.coedgeIdx - 1; edgesCoedges.insert(std::make_pair(newEdgeIdx, std::make_pair(1, newCoedgeIndex))); if (needToProjectParamCurve(arrFaces[coedgeIndex.faceIdx].pSurf)) { OdResult eStatus = createParamCurve(arrFaces[coedgeIndex.faceIdx].pSurf, m_initialData.edges[edgeIdx].curve, coedge.curve); if (eOk != eStatus) return checkRet(eStatus); eStatus = createParamCurve(arrFaces[coedgeIndex.faceIdx].pSurf, m_initialData.edges[newEdgeIdx].curve, newCoedge.curve); if (eOk != eStatus) return checkRet(eStatus); } return findParentEdge(arrFaces, edgeIdx, coedgeIndex, parentEdgeIndices, parentIdx, edgesCoedges); } } return checkRet(eGeneralModelingFailure); } OdResult OdBrepBuilderFillerHelper::fixFaceRegionsConnections(BrepBuilderInitialSurfaceArray& arrFaces) { if (!m_params.isSkipCoedge2dCurve()) return checkRet(eNotImplemented); //edge idx -> (num coedges, one coedge index) std::map> edgesCoedges; for (unsigned iFace = 0; iFace < arrFaces.size(); ++iFace) { for (unsigned iLoop = 0; iLoop < arrFaces[iFace].loops.size(); ++iLoop) { const BrepBuilderInitialLoop& loop = arrFaces[iFace].loops[iLoop]; for (unsigned iCoedge = 0; iCoedge < loop.coedges.size(); ++iCoedge) { const BrepBuilderInitialCoedge& coedge = loop.coedges[iCoedge]; std::pair& edgeInfo = edgesCoedges[coedge.edgeIndex]; ++edgeInfo.first; edgeInfo.second = CoedgeIndex(iFace, iLoop, iCoedge); } } } //map: parent edge idx -> (parent face idx, array of child's (edge idx, CoedgeIndex)) typedef std::pair> ParentFaceAndChildEdges; OdHashMap parentEdgesNotConnectedEdges[2]; //connect parent edges with all child coedges, distributed to 2 parent faces for (auto itEdge = edgesCoedges.begin(); itEdge != edgesCoedges.end(); ++itEdge) { EdgeIndex edgeIdx = itEdge->first; unsigned numCoedges = itEdge->second.first; //non manifold edges are forbidden if (numCoedges > 2) return checkRet(eNotApplicable); //fix only edges with 1 coedge if (numCoedges == 2) continue; CoedgeIndex& coedgeIndex = itEdge->second.second; OdResult res = fixCoedgeIdx(arrFaces, edgeIdx, coedgeIndex); if (res != eOk) return checkRet(res); //parse only face regions OdUInt32 parentFaceIdx = arrFaces[coedgeIndex.faceIdx].parentFaceIdx; if (parentFaceIdx == -1) continue; const OdArray& parentEdgeIndices = m_parentFacesEdgesIdx[parentFaceIdx]; //TODO: it would be nice to find parent edge using GStep history, but it is complicated EdgeIndex parentEdgeIdx; res = findParentEdge(arrFaces, edgeIdx, coedgeIndex, parentEdgeIndices, parentEdgeIdx, edgesCoedges); if (eOk != res) return checkRet(res); for (int idx = 0; idx < 2; ++idx) { auto it = parentEdgesNotConnectedEdges[idx].find(parentEdgeIdx); if (it == parentEdgesNotConnectedEdges[idx].end()) { ParentFaceAndChildEdges parentFaceAndChildEdges; parentFaceAndChildEdges.first = parentFaceIdx; parentFaceAndChildEdges.second.push_back(std::make_pair(edgeIdx, coedgeIndex)); parentEdgesNotConnectedEdges[idx][parentEdgeIdx] = parentFaceAndChildEdges; break; } else if (it->second.first == parentFaceIdx) { it->second.second.push_back(std::make_pair(edgeIdx, coedgeIndex)); break; } else if (idx == 1) { //edge has more than 2 adjacent faces? return checkRet(eGeneralModelingFailure); } } } //split and connect edges while (!parentEdgesNotConnectedEdges[0].empty()) { OdHashMap::iterator it[2]; it[0] = parentEdgesNotConnectedEdges[0].begin(); EdgeIndex parentEdgeIdx = it[0]->first; OdArray& edges = it[0]->second.second; it[1] = parentEdgesNotConnectedEdges[1].find(parentEdgeIdx); if (it[1] == parentEdgesNotConnectedEdges[1].end() && edgesCoedges.at(parentEdgeIdx).first == 0) { //boundary edge parentEdgesNotConnectedEdges[0].erase(it[0]); continue; } OdResult res = sortEdges(arrFaces, edges); if (eOk != res) return checkRet(res); OdArray parentEdgeArr; OdArray* edgesOther; if (it[1] == parentEdgesNotConnectedEdges[1].end()) { CoedgeIndex& parentIdx = edgesCoedges.at(parentEdgeIdx).second; parentEdgeArr.emplace_back(parentEdgeIdx, parentIdx); edgesOther = &parentEdgeArr; } else edgesOther = &it[1]->second.second; res = sortEdges(arrFaces, *edgesOther); if (eOk != res) return checkRet(res); OdArray* arrEdges[2] = { &edges, edgesOther }; #ifdef ODA_DIAGNOSTICS OdGePoint3d endPoints[2][2]; for (int side = 0; side < 2; ++side) { for (int end = 0; end < 2; ++end) { CoedgeIndex idx = end == 0 ? arrEdges[side]->first().second : arrEdges[side]->last().second; const BrepBuilderInitialCoedge& endCoedge = arrFaces[idx.faceIdx].loops[idx.loopIdx].coedges[idx.coedgeIdx]; const OdGeCurve3d* curve = m_initialData.edges[endCoedge.edgeIndex].curve; bool succ = ((endCoedge.direction == OdBrepBuilder::kForward) ^ (end == 1)) ? curve->hasStartPoint(endPoints[side][end]) : curve->hasEndPoint(endPoints[side][end]); ODA_ASSERT(succ); } } ODA_ASSERT(endPoints[0][0].isEqualTo(endPoints[1][1], m_toleranceDiffPoints)); ODA_ASSERT(endPoints[0][1].isEqualTo(endPoints[1][0], m_toleranceDiffPoints)); #endif int currEdgeIdx[2] = { 0, (int)edgesOther->size() - 1 }; while (currEdgeIdx[0] < (int)edges.size()) { EdgeCoedgeIndex* curEdge[2] = { &(*arrEdges[0])[currEdgeIdx[0]], &(*arrEdges[1])[currEdgeIdx[1]] }; if (currEdgeIdx[1] < 0) return checkRet(eGeneralModelingFailure); for (int side = 0; side < 2; ++side) { res = fixCoedgeIdx(arrFaces, curEdge[side]->first, curEdge[side]->second); if (res != eOk) return checkRet(res); } CoedgeIndex idx[2] = { curEdge[0]->second, curEdge[1]->second }; BrepBuilderInitialLoop* currLoop[2] = { &arrFaces[idx[0].faceIdx].loops[idx[0].loopIdx], &arrFaces[idx[1].faceIdx].loops[idx[1].loopIdx] }; BrepBuilderInitialCoedge* currCoedge[2] = { &currLoop[0]->coedges[idx[0].coedgeIdx], &currLoop[1]->coedges[idx[1].coedgeIdx] }; OdGeCurve3d* currCurve[2] = { m_initialData.edges[currCoedge[0]->edgeIndex].curve, m_initialData.edges[currCoedge[1]->edgeIndex].curve }; OdGePoint3d currEndPoints[2]; if (currCoedge[0]->direction == OdBrepBuilder::kForward ? !currCurve[0]->hasEndPoint(currEndPoints[0]) : !currCurve[0]->hasStartPoint(currEndPoints[0])) return checkRet(eInvalidCurve); if (currCoedge[1]->direction == OdBrepBuilder::kForward ? !currCurve[1]->hasStartPoint(currEndPoints[1]) : !currCurve[1]->hasEndPoint(currEndPoints[1])) return checkRet(eInvalidCurve); #ifdef ODA_DIAGNOSTICS //start points should be equal OdGePoint3d currStartPoints[2]; bool succ = currCoedge[0]->direction == OdBrepBuilder::kForward ? currCurve[0]->hasStartPoint(currStartPoints[0]) : currCurve[0]->hasEndPoint(currStartPoints[0]); ODA_ASSERT(succ); succ = currCoedge[1]->direction == OdBrepBuilder::kForward ? currCurve[1]->hasEndPoint(currStartPoints[1]) : currCurve[1]->hasStartPoint(currStartPoints[1]); ODA_ASSERT(succ); ODA_ASSERT(currStartPoints[0].isEqualTo(currStartPoints[1], m_toleranceDiffPoints)); #endif if (currEndPoints[0].isEqualTo(currEndPoints[1], m_toleranceDiffPoints)) { //same edges, union them currCoedge[1]->edgeIndex = currCoedge[0]->edgeIndex; currCoedge[1]->direction = currCoedge[0]->direction == OdBrepBuilder::kForward ? OdBrepBuilder::kReversed : OdBrepBuilder::kForward; if (needToProjectParamCurve(arrFaces[idx[1].faceIdx].pSurf)) { OdResult eStatus = createParamCurve(arrFaces[idx[1].faceIdx].pSurf, m_initialData.edges[currCoedge[1]->edgeIndex].curve, currCoedge[1]->curve); if (eOk != eStatus) { return checkRet(eStatus); } } ++currEdgeIdx[0]; --currEdgeIdx[1]; } else { bool split = false; for (int side = 0; side < 2; ++side) { double splitParam; if (currCurve[side]->isOn(currEndPoints[1 - side], splitParam, OdGeTol(m_toleranceDiffPoints, OdGeContext::gTol.equalVector()))) { //'side' edge is longer than '1 - side', split 'side' into two //common part will get shorter edge, longer part will get its own edge with reduced interval OdGeInterval interval; currCurve[side]->getInterval(interval); if ((currCoedge[side]->direction == OdBrepBuilder::kForward) ^ (side == 1)) interval.setLower(splitParam); else interval.setUpper(splitParam); currCurve[side]->setInterval(interval); //TODO: trim nurbs? currCoedge[side]->curve = nullptr; BrepBuilderInitialCoedge newCoedge = *currCoedge[1 - side]; newCoedge.direction = currCoedge[1 - side]->direction == OdBrepBuilder::kForward ? OdBrepBuilder::kReversed : OdBrepBuilder::kForward; if (needToProjectParamCurve(arrFaces[idx[side].faceIdx].pSurf)) { OdResult eStatus = createParamCurve(arrFaces[idx[side].faceIdx].pSurf, m_initialData.edges[currCoedge[side]->edgeIndex].curve, currCoedge[side]->curve); if (eOk != eStatus) return checkRet(eStatus); eStatus = createParamCurve(arrFaces[idx[side].faceIdx].pSurf, m_initialData.edges[newCoedge.edgeIndex].curve, newCoedge.curve); if (eOk != eStatus) return checkRet(eStatus); } currLoop[side]->coedges.insertAt(side == 0 ? idx[side].coedgeIdx : idx[side].coedgeIdx + 1, newCoedge); side == 0 ? --currEdgeIdx[1] : ++currEdgeIdx[0]; split = true; break; } } if (!split) return checkRet(eGeneralModelingFailure); } } if (currEdgeIdx[1] != -1) return checkRet(eGeneralModelingFailure); parentEdgesNotConnectedEdges[0].erase(it[0]); } return checkRet(eOk); } //coedgeIndex.coedgeIdx may be invalid as we may have split some coedges, thus shifting coedgeIdx, so we are fixing it OdResult OdBrepBuilderFillerHelper::fixCoedgeIdx(const BrepBuilderInitialSurfaceArray& arrFaces, OdUInt32 edgeIdx, CoedgeIndex& coedgeIndex) { const BrepBuilderInitialLoop& loop = arrFaces[coedgeIndex.faceIdx].loops[coedgeIndex.loopIdx]; while (loop.coedges[coedgeIndex.coedgeIdx].edgeIndex != edgeIdx) { ++coedgeIndex.coedgeIdx; if (coedgeIndex.coedgeIdx >= loop.coedges.size()) return checkRet(eGeneralModelingFailure); } return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::sortEdges(const BrepBuilderInitialSurfaceArray& arrFaces, OdArray& edges) { OdResult res; for (unsigned i = 0; i < edges.size(); ++i) { res = fixCoedgeIdx(arrFaces, edges[i].first, edges[i].second); if (res != eOk) return checkRet(res); } int endIdx = -1; for (int dir = 1; dir >= -1; dir -= 2) { //connect end points with start points for (int i = (dir == 1 ? 0 : endIdx); dir == 1 ? (i < (int)edges.size() - 1) : (i > 0); i += dir) { const CoedgeIndex& iCoedgeIdx = edges[i].second; const BrepBuilderInitialCoedge& iCoedge = arrFaces[iCoedgeIdx.faceIdx].loops[iCoedgeIdx.loopIdx].coedges[iCoedgeIdx.coedgeIdx]; const OdGeCurve3d* iCurve = m_initialData.edges[edges[i].first].curve; OdGePoint3d endPoint; if (((iCoedge.direction == OdBrepBuilder::kForward) ^ (dir == -1)) ? !iCurve->hasEndPoint(endPoint) : !iCurve->hasStartPoint(endPoint)) return checkRet(eInvalidCurve); bool connected = false; for (int j = i + dir; dir == 1 ? (j < (int)edges.size()) : (j >= 0); j += dir) { const CoedgeIndex& jCoedgeIdx = edges[j].second; const BrepBuilderInitialCoedge& jCoedge = arrFaces[jCoedgeIdx.faceIdx].loops[jCoedgeIdx.loopIdx].coedges[jCoedgeIdx.coedgeIdx]; const OdGeCurve3d* jCurve = m_initialData.edges[edges[j].first].curve; OdGePoint3d startPoint; if (((jCoedge.direction == OdBrepBuilder::kForward) ^ (dir == -1)) ? !jCurve->hasStartPoint(startPoint) : !jCurve->hasEndPoint(startPoint)) return checkRet(eInvalidCurve); if (startPoint.isEqualTo(endPoint, m_toleranceDiffPoints)) { std::swap(edges[i + dir], edges[j]); connected = true; break; } } if (!connected) { if (endIdx != -1) return checkRet(eGeneralModelingFailure); //can't create a coedges chain else endIdx = i; } } if (endIdx == -1 || dir == -1) break; OdArray shiftedEdges; shiftedEdges.resize(edges.size()); for (int i = 0; i <= endIdx; ++i) shiftedEdges[i + edges.size() - (endIdx + 1)] = edges[i]; for (int i = endIdx + 1; i < (int)edges.size(); ++i) shiftedEdges[i - (endIdx + 1)] = edges[i]; edges = std::move(shiftedEdges); endIdx = edges.size() - (endIdx + 1); } return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::splitEdgesByPole() { const double scaleEdgeTol = 1e-8; typedef std::pair LoopSurf; typedef OdArray> EdgeLoopSurfs; OdArray edgeLoopSurfsArr; edgeLoopSurfsArr.setLogicalLength(m_initialData.edges.size()); for (BrepBuilderComplexArray::iterator complexIt = m_initialData.complexes.begin(); complexIt != m_initialData.complexes.end(); ++complexIt) { for (BrepBuilderShellsArray::iterator shellIt = complexIt->begin(); shellIt != complexIt->end(); ++shellIt) { for (BrepBuilderInitialSurfaceArray::iterator surfIt = shellIt->begin(); surfIt != shellIt->end(); ++surfIt) { BrepBuilderInitialLoopArray& loops = surfIt->loops; for (BrepBuilderInitialLoopArray::iterator loopIt = loops.begin(); loopIt != loops.end(); ++loopIt) { BrepBuilderInitialCoedgeArray& coedges = loopIt->coedges; for (BrepBuilderInitialCoedgeArray::iterator coedgeIt = coedges.begin(); coedgeIt != coedges.end(); ++coedgeIt) { addUnique(LoopSurf(&coedges, surfIt->pSurf), edgeLoopSurfsArr[coedgeIt->edgeIndex]); } } } } } std::vector params; for (auto complexIt = m_initialData.complexes.begin(); complexIt != m_initialData.complexes.end(); ++complexIt) { for (auto shellIt = complexIt->begin(); shellIt != complexIt->end(); ++shellIt) { for (auto surfIt = shellIt->begin(); surfIt != shellIt->end(); ++surfIt) { m_testPoints.clear(); if (!surfIt->pSurf->getPoles(nullptr, nullptr, &m_testPoints, &m_testPoints)) continue; BrepBuilderInitialLoopArray& loops = surfIt->loops; for (BrepBuilderInitialLoop& loop : loops) { BrepBuilderInitialCoedgeArray& loopCoedges = loop.coedges; for (OdUInt32 iLoopCoedge = 0; iLoopCoedge < loopCoedges.size(); ++iLoopCoedge) { OdUInt32 edgeIndex = loopCoedges[iLoopCoedge].edgeIndex; BrepBuilderInitialEdge& edgeData = m_initialData.edges[edgeIndex]; OdGeTol tol(edgeData.approxLength() * scaleEdgeTol, OdGeContext::gTol.equalVector()); params.clear(); { OdGeInterval edgeInterval; edgeData.curve->getInterval(edgeInterval); OdGePoint3d edgeEnds[2] = { getStartPoint(*edgeData.curve), getEndPoint(*edgeData.curve) }; for (OdArrayBuffer::size_type iPole = 0; iPole < m_testPoints.size(); ++iPole) { OdGePoint3d pole = m_testPoints.getAt(iPole); if (pole.isEqualTo(edgeEnds[0], tol) || pole.isEqualTo(edgeEnds[1], tol)) continue; double param; if (!edgeData.curve->isOn(pole, param, tol)) continue; if (!edgeInterval.contains(param)) continue; if (OdEqual(edgeInterval.lowerBound(), param, tol.equalVector()) || OdEqual(edgeInterval.upperBound(), param, tol.equalVector())) continue; params.push_back(param); } std::sort(params.begin(), params.end()); } if (params.empty()) continue; edgeData.resetApproxLength(); BrepBuilderInitialEdge* pCurrEdgeData = &edgeData; EdgeLoopSurfs& edgeLoopSurfs = edgeLoopSurfsArr[edgeIndex]; for (std::size_t iParam = 0; iParam < params.size(); ++iParam) { OdGeInterval edgeInterval; pCurrEdgeData->curve->getInterval(edgeInterval); BrepBuilderInitialEdge* pNextEdgeData = m_initialData.edges.append(); pCurrEdgeData = &m_initialData.edges[edgeIndex]; pNextEdgeData->curve = static_cast(pCurrEdgeData->curve->copy()); pNextEdgeData->vertexIndex[1] = pCurrEdgeData->vertexIndex[1]; pCurrEdgeData->vertexIndex[1] = BrepBuilderInitialEdge::kInvalidIndex; pNextEdgeData->marker = pCurrEdgeData->marker; pNextEdgeData->hasColor = pCurrEdgeData->hasColor; pNextEdgeData->color = pCurrEdgeData->color; OdGeInterval edgeIntervalStart(edgeInterval.lowerBound(), params[iParam]); pCurrEdgeData->curve->setInterval(edgeIntervalStart); edgeInterval.setLower(params[iParam]); pNextEdgeData->curve->setInterval(edgeInterval); OdUInt32 newEdgeIndex = m_initialData.edges.size() - 1; for (LoopSurf& loopSurf : edgeLoopSurfs) { BrepBuilderInitialCoedgeArray& coedges = *loopSurf.first; for (OdArrayBuffer::size_type iCoedge = 0; iCoedge < coedges.size(); ++iCoedge) { BrepBuilderInitialCoedge& coedgeData = coedges[iCoedge]; if (coedgeData.edgeIndex != edgeIndex) continue; coedgeData.curve = nullptr; BrepBuilderInitialCoedge newCoedge(nullptr, coedgeData.direction, newEdgeIndex); bool isReversed = OdBrepBuilder::kReversed == coedgeData.direction; if (!coedgeData.curve.isNull()) { // 2d curve that intersects pole may be discontinued if (needToProjectParamCurve(loopSurf.second)) { OdGeTol tmpTol(-1.); OdResult eStatus = createParamCurve(loopSurf.second, pCurrEdgeData->curve, coedgeData.curve, tmpTol); if (eOk != eStatus) return checkRet(eStatus); if (params.size() == iParam + 1) { tmpTol.setEqualPoint(-1.); eStatus = createParamCurve(loopSurf.second, pNextEdgeData->curve, newCoedge.curve, tmpTol); if (eOk != eStatus) return checkRet(eStatus); } } } coedges.insertAt(isReversed ? iCoedge : iCoedge + 1, newCoedge);// dangling reference coedgeData ++iCoedge; } } pCurrEdgeData = pNextEdgeData; edgeIndex = newEdgeIndex; } edgeLoopSurfsArr.insert(edgeLoopSurfsArr.end(), OdUInt32(params.size()), edgeLoopSurfs); iLoopCoedge += OdUInt32(params.size()); } } } } } return checkRet(eOk); } // Topology OdResult OdBrepBuilderFillerHelper::performBrep(const OdBrBrep& brep) { ODA_ASSERT_ONCE(brep.isValid()); OdBrBrepComplexTraverser complxTrav; OdBrErrorStatus errStatus = complxTrav.setBrep(brep); if (odbrOK != errStatus) { return checkRet(eBrComplexMissed); } while (!complxTrav.done()) { OdBrComplex complex = complxTrav.getComplex(); OdResult res = performComplex(complex); if (eOk != res) { return checkRet(res); } if (odbrOK != complxTrav.next()) { return checkRet(eNullIterator); } } if (m_params.isSplitEdgeByPole()) { OdResult res = splitEdgesByPole(); if (eOk != res) return checkRet(res); } if (m_params.isGlobalBrepTransform()) { OdGeMatrix3d transformation; if (brep.getTransformation(transformation)) m_initialData.transformation = new OdGeMatrix3d(transformation); } return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::performComplex(const OdBrComplex& complex) { OdBrComplexShellTraverser complxShellTrav; OdBrErrorStatus errStatus = complxShellTrav.setComplex(complex); if (odbrUnsuitableTopology == errStatus) { return checkRet(eOk); } if (odbrOK != errStatus) { return checkRet(eBrComplexMissed); } BrepBuilderShellsArray shells; while (!complxShellTrav.done()) { OdBrShell shell = complxShellTrav.getShell(); OdResult res = performShell(shell, shells); if (eOk != res) { return checkRet(res); } if (odbrOK != complxShellTrav.next()) { return checkRet(eNullIterator); } } m_initialData.complexes.append(shells); return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::performShell(const OdBrShell& shell, BrepBuilderShellsArray& arrShells) { OdBrShellFaceTraverser shellFaceTrav; OdBrErrorStatus errStatus = shellFaceTrav.setShell(shell); if (odbrUnsuitableTopology == errStatus) { return checkRet(eOk); } if (odbrOK != errStatus) { return checkRet(eBrShellMissed); } m_edgesAdded.clear(); BrepBuilderInitialSurfaceArray arrSurfaces; unsigned int oldParentFaceIdx = m_parentFacesEdgesIdx.size(); m_loopsSplit = false; while (!shellFaceTrav.done()) { OdBrFace face = shellFaceTrav.getFace(); unsigned int numFacesOld = arrSurfaces.length(); unsigned int parentFaceIdx = m_parentFacesEdgesIdx.size(); OdResult res = performFace(face, arrSurfaces); if (eOk != res) { return checkRet(res); } if (m_params.isFixFaceRegionsConnections() && m_parentFacesEdgesIdx.size() != parentFaceIdx) for (unsigned i = numFacesOld; i < arrSurfaces.length(); ++i) arrSurfaces[i].parentFaceIdx = parentFaceIdx; if (odbrOK != shellFaceTrav.next()) { return checkRet(eNullIterator); } } if (m_params.isFixFaceRegionsConnections() && m_parentFacesEdgesIdx.size() != oldParentFaceIdx) { OdResult res = fixFaceRegionsConnections(arrSurfaces); if (eOk != res) return checkRet(res); } if (m_loopsSplit || m_params.isCheckShellsConnectivity()) { //detect connected faces and move it to logical groups. Each of this group should be in separate shell OdArray groupedFaces; groupFaces(arrSurfaces, groupedFaces); for (unsigned int i = 0; i < groupedFaces.length(); ++i) { BrepBuilderInitialSurfaceArray arrNewSurfaces; for (unsigned int j = 0; j < groupedFaces[i].length(); ++j) { arrNewSurfaces.append(arrSurfaces[groupedFaces[i][j]]); } arrShells.append(arrNewSurfaces); } return checkRet(eOk); } arrShells.append(arrSurfaces); return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::getFaceEdges(const OdBrFace& face, OdArray& faceEdgesIdx) { OdBrFaceLoopTraverser faceLoopTrav; OdBrErrorStatus err = faceLoopTrav.setFace(face); if (err != odbrOK) { return checkRet(eNotApplicable); } while (!faceLoopTrav.done()) { OdBrLoop loop = faceLoopTrav.getLoop(); OdBrLoopEdgeTraverser loopEdgeTrav; err = loopEdgeTrav.setLoop(loop); if (odbrOK != err) { return checkRet(eNotApplicable); } while (!loopEdgeTrav.done()) { OdBrEdge edge = loopEdgeTrav.getEdge(); OdUInt32 edgeIdx; BrepBuilderInitialEdge* pEdgeData = nullptr; OdResult res = performEdge(edge, edgeIdx, pEdgeData); if (eOk != res) { return checkRet(res); } faceEdgesIdx.push_back(edgeIdx); if (odbrOK != loopEdgeTrav.next()) { return checkRet(eNullIterator); } } if (odbrOK != faceLoopTrav.next()) { return checkRet(eNullIterator); } } return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::performFace(const OdBrFace& face, BrepBuilderInitialSurfaceArray& arrSurfaces) { if (m_params.isUseFaceRegions()) { OdBrFace faceRegion; if (face.getFirstFaceRegion(faceRegion)) { if (m_params.isFixFaceRegionsConnections()) { getFaceEdges(face, *m_parentFacesEdgesIdx.append()); } do { //can face regions have their own sub regions? OdResult res = performFace(faceRegion, arrSurfaces); if (eOk != res) { return checkRet(res); } } while (face.getNextFaceRegion(faceRegion)); return checkRet(eOk); } } OdResult eStatus; BrepBuilderInitialSurface surfData; surfData.pSurf = getFaceSurface(face); if (surfData.pSurf.isNull()) { // skip face without surface if (m_params.isSkipNullSurface()) { return checkRet(eOk); } return checkRet(eNullFaceSurface); } eStatus = fixFaceSurface(*surfData.pSurf); if (eOk != eStatus) return checkRet(eStatus); // face direction surfData.direction = face.getOrientToSurface() ? OdBrepBuilder::kForward : OdBrepBuilder::kReversed; if (m_params.isSetFaceGsMarkersTags()) surfData.marker.first = odbrOK == face.getGsMarker(surfData.marker.second); // face visual eStatus = surfData.setupVisualInfo(face, m_pMaterialHelper); if (eOk != eStatus) { return checkRet(eStatus); } OdBrFaceLoopTraverser faceLoopTrav; OdBrErrorStatus err = faceLoopTrav.setFace(face); if (odbrUnsuitableTopology == err) { if (m_params.isGenerateExplicitLoops()) { eStatus = addFaceExplicitLoop(surfData); if (eStatus != eOk) { ODA_FAIL_M_ONCE("Face without loops - unsupported case"); } } // Face without loops (sphere, torus) arrSurfaces.append(surfData); return checkRet(eOk); } if (odbrOK != err) { return checkRet(eBrFaceMissed); } while (!faceLoopTrav.done()) { OdBrLoop loop = faceLoopTrav.getLoop(); eStatus = performLoop(loop, surfData); if (eOk != eStatus) { return checkRet(eStatus); } if (odbrOK != faceLoopTrav.next()) { return checkRet(eNullIterator); } } arrSurfaces.append(surfData); if (m_params.isSkipCheckLoopType()) { return checkRet(eOk); } return checkRet(splitOuterLoops(arrSurfaces)); } OdResult OdBrepBuilderFillerHelper::performLoop(const OdBrLoop& loop, BrepBuilderInitialSurface& surfData) { OdBrErrorStatus err; OdBrLoopEdgeTraverser loopEdgeTrav; err = loopEdgeTrav.setLoop(loop); // Loop with apex: if (odbrDegenerateTopology == err) // maybe there should be odbrUnsuitableTopology (see arx) { OdGeCurve3dPtr curve; OdGeCurve2dPtr paramCurve; OdBrVertex vertex; bool isGenerateVertices = m_params.isGenerateVertices(); OdResult resStatus = performLoopWithApex(loop, curve, paramCurve, isGenerateVertices ? &vertex : nullptr); if (eOk == resStatus) { BrepBuilderInitialEdge::VertexIndex vertexIndex = isGenerateVertices ? addVertex(vertex) : BrepBuilderInitialEdge::kInvalidIndex; m_initialData.edges.append(BrepBuilderInitialEdge(curve, vertexIndex, vertexIndex)); surfData.loops.append(BrepBuilderInitialLoop( paramCurve, m_initialData.edges.size() - 1, // stub edge for correct index OdBrepBuilder::kForward )); return checkRet(eOk); } return checkRet(resStatus); } if (odbrOK != err) { return checkRet(eBrEmptyLoop); } // Regular loop: BrepBuilderInitialLoop loopData; DumpPointsData pointsData; pointsData.addSurf(*surfData.pSurf); // Need a lot of calculation to correct edge and coedge geometry. // So we make only simple fixes. bool goodUvLoop = !m_params.isOldUvCurveHandling(); // No need fixes for Md geometry bool forceUvLoop = OdBrepBuilderFillerParams::kBrepMd == m_params.sourceBrepType(); bool askUvCurve = askParamCurve(surfData.pSurf); while (!loopEdgeTrav.done()) { OdBrEdge edge = loopEdgeTrav.getEdge(); OdUInt32 edgeIdx; BrepBuilderInitialEdge* pEdgeData = nullptr; OdResult res = performEdge(edge, edgeIdx, pEdgeData); if (eOk != res) return checkRet(res); bool edgeCodirLoop = loopEdgeTrav.getEdgeOrientToLoop(); bool curveCodirLoop = edge.getOrientToCurve() == edgeCodirLoop; BrepBuilderInitialCoedge& coedgeData = *loopData.coedges.append(); coedgeData.edgeIndex = edgeIdx; if (askUvCurve) coedgeData.curve = getParamCurve(loopEdgeTrav); coedgeData.direction = curveCodirLoop ? OdBrepBuilder::kForward : OdBrepBuilder::kReversed; goodUvLoop = goodUvLoop && !coedgeData.curve.isNull(); pointsData.addOriginCoedge(*surfData.pSurf, *pEdgeData->curve, coedgeData.curve); if (odbrOK != loopEdgeTrav.next()) return checkRet(eNullIterator); } if (!forceUvLoop && goodUvLoop) goodUvLoop = checkCoedgeLoop(*surfData.pSurf, loopData); if (!forceUvLoop && goodUvLoop && 1 == loopData.coedges.size()) { BrepBuilderInitialCoedge& coedgeData = loopData.coedges.first(); ODA_ASSERT_ONCE(!coedgeData.curve.isNull()); OdResult eStatus = fixSingleCoedgeLoopByEdgeEnd(*surfData.pSurf, *m_initialData.edges[coedgeData.edgeIndex].curve, *coedgeData.curve); if (eOk != eStatus) return checkRet(eStatus); } for (OdArrayBuffer::size_type iCoedge = 0; iCoedge < loopData.coedges.size(); ++iCoedge) { BrepBuilderInitialCoedge& coedgeData = loopData.coedges[iCoedge]; OdResult eStatus = getParamCurveFixed(surfData.pSurf, m_initialData.edges[coedgeData.edgeIndex].curve, coedgeData.curve, goodUvLoop, forceUvLoop); if (eOk != eStatus) return checkRet(eStatus); } pointsData.addFinalCoedges(*surfData.pSurf, loopData); // skip loop without coedge if (!loopData.coedges.empty()) { surfData.loops.append(loopData); } return checkRet(eOk); } OdResult OdBrepBuilderFillerHelper::performEdge(const OdBrEdge& edge, OdUInt32& edgeIdx, BrepBuilderInitialEdge*& pEdgeData) { auto pIt = m_edges.find(edge.getUniqueId()); if (pIt != m_edges.end()) { edgeIdx = pIt->second; pEdgeData = &m_initialData.edges[edgeIdx]; return checkRet(eOk); } OdGeCurve3dPtr curvePtr; OdResult resStatus = getEdgeCurveFixed(edge, curvePtr); if (eOk != resStatus) { return checkRet(resStatus); } OdBrVertex vertices[2]; bool res[2] = { m_params.isGenerateVertices() && edge.getVertex1(vertices[0]), m_params.isGenerateVertices() && edge.getVertex2(vertices[1]) }; if (!edge.getOrientToCurve()) { std::swap(vertices[0], vertices[1]); } BrepBuilderInitialEdge::VertexIndex vertexIndex[2] = { BrepBuilderInitialEdge::kInvalidIndex, BrepBuilderInitialEdge::kInvalidIndex }; for (int idx = 0; idx < 2; ++idx) { if (res[idx]) vertexIndex[idx] = addVertex(vertices[idx]); } m_initialData.edges.append(BrepBuilderInitialEdge(curvePtr, vertexIndex[0], vertexIndex[1])); pEdgeData = &m_initialData.edges.last(); edgeIdx = m_initialData.edges.size() - 1; m_edges[edge.getUniqueId()] = edgeIdx; // edge visual OdResult eStatus = pEdgeData->setupVisualInfo(edge, m_pMaterialHelper); if (eOk != eStatus) { return checkRet(eStatus); } if (m_params.isSetEdgeGsMarkersTags()) pEdgeData->marker.first = odbrOK == edge.getGsMarker(pEdgeData->marker.second); return checkRet(eOk); } //TODO: we should, probably, union shells with a common vertex, not only a common edge void OdBrepBuilderFillerHelper::groupFaces(BrepBuilderInitialSurfaceArray& arrFaces, OdArray& groupedFaces) { //create map between edges and their faces OdHashMap edgesFaces; for (OdUInt32 faceIdx = 0; faceIdx < arrFaces.length(); ++faceIdx) { const BrepBuilderInitialLoopArray& loops = arrFaces[faceIdx].loops; for (OdUInt32 loopIdx = 0; loopIdx < loops.length(); ++loopIdx) { const BrepBuilderInitialCoedgeArray& coedges = loops[loopIdx].coedges; for (OdUInt32 coedgeIdx = 0; coedgeIdx < coedges.length(); ++coedgeIdx) { edgesFaces[coedges[coedgeIdx].edgeIndex].append(faceIdx); } } } OdHashSet usedFaces; for (OdUInt32 faceIdx = 0; faceIdx < arrFaces.length(); ++faceIdx) { if (usedFaces.find(faceIdx) != usedFaces.end()) { continue; } OdUInt32Array facesIndices; findAdjacentFaces(arrFaces, usedFaces, edgesFaces, faceIdx, facesIndices); groupedFaces.push_back(facesIndices); } } void OdBrepBuilderFillerHelper::findAdjacentFaces(BrepBuilderInitialSurfaceArray& arrFaces, OdHashSet& usedFaces, const OdHashMap& edgesFaces, OdUInt32 iCurrFace, OdUInt32Array& facesIndexes) { usedFaces.insert(iCurrFace); facesIndexes.append(iCurrFace); const BrepBuilderInitialSurface& currFace = arrFaces[iCurrFace]; OdHashSet adjacentEdges; for (OdUInt32 i = 0; i < currFace.loops.length(); ++i) { const BrepBuilderInitialCoedgeArray& arrCurrLoopCoedges = currFace.loops[i].coedges; for (OdUInt32 j = 0; j < arrCurrLoopCoedges.length(); ++j) { adjacentEdges.insert(arrCurrLoopCoedges[j].edgeIndex); } } for (OdHashSet::iterator it = adjacentEdges.begin(); it != adjacentEdges.end(); ++it) { auto facesArr = edgesFaces.find(*it); //no at() in __gnu_cxx::hash_map :( for (OdUInt32 j = 0; j < facesArr->second.length(); ++j) { OdUInt32 faceIdx = facesArr->second[j]; if (usedFaces.find(faceIdx) != usedFaces.end()) { continue; } findAdjacentFaces(arrFaces, usedFaces, edgesFaces, faceIdx, facesIndexes); } } } /*static*/ OdResult OdBrepBuilderFillerHelper::initFromImpl(OdBrepBuilderFiller& filler, OdBrepBuilder& builder, const BrepBuilderInitialData& data) { OdResult status = eOk; OdArray verticesIds(data.vertices.size()); for (BrepBuilderInitialEdge::VertexIndex i = 0; i < data.vertices.size(); ++i) { const BrepBuilderInitialVertex& vertexData = data.vertices[i]; verticesIds.append(builder.addVertex(vertexData.point)); if (vertexData.marker.first) { assertGsMarkerValue(vertexData.marker.second); builder.setTag(verticesIds.last(), static_cast(vertexData.marker.second)); } } filler.m_edges.reserve(data.edges.size()); OdArray edgeIds(data.edges.size()); for (BrepBuilderInitialCoedge::EdgeIndex i = 0; i < data.edges.size(); ++i) { const BrepBuilderInitialEdge& edgeData = data.edges[i]; filler.m_edges.append(edgeData.curve); BRepBuilderGeometryId vertexIndex[2] = { OdBrepBuilder::kDefaultVertexId, OdBrepBuilder::kDefaultVertexId }; for (int idx = 0; idx < 2; ++idx) { if (edgeData.vertexIndex[idx] != BrepBuilderInitialEdge::kInvalidIndex) { vertexIndex[idx] = verticesIds[edgeData.vertexIndex[idx]]; } } edgeIds.append(builder.addEdge(edgeData.curve, vertexIndex[0], vertexIndex[1])); if (edgeData.marker.first) { assertGsMarkerValue(edgeData.marker.second); builder.setTag(edgeIds.last(), static_cast(edgeData.marker.second)); } if (edgeData.hasColor) { status = builder.setEdgeColor(edgeIds.last(), edgeData.color); if (eOk != status) { return checkRet(status); } } } const bool addComplexShell = !filler.m_params.isIgnoreComplexShell(); for (BrepBuilderComplexArray::const_iterator complexIt = data.complexes.begin(); complexIt != data.complexes.end(); ++complexIt) { BRepBuilderGeometryId currentComplexId = 0; if (addComplexShell) { currentComplexId = builder.addComplex(); } for (BrepBuilderShellsArray::const_iterator shellIt = complexIt->begin(); shellIt != complexIt->end(); ++shellIt) { BRepBuilderGeometryId currentShellId(OdBrepBuilder::kDefaultShellId); if (addComplexShell) { currentShellId = builder.addShell(currentComplexId); ODA_ASSERT_ONCE(currentShellId != OdBrepBuilder::kDefaultShellId); } for (BrepBuilderInitialSurfaceArray::const_iterator surfIt = shellIt->begin(); surfIt != shellIt->end(); ++surfIt) { filler.m_surfaces.append(surfIt->pSurf); BRepBuilderGeometryId currentFaceId = builder.addFace(surfIt->pSurf, surfIt->direction, currentShellId); if (surfIt->marker.first) { assertGsMarkerValue(surfIt->marker.second); builder.setTag(currentFaceId, static_cast(surfIt->marker.second)); } if (surfIt->material) { builder.setFacesMaterial(currentFaceId, *surfIt->material); } if (surfIt->hasMaterialMapping) { status = builder.setFaceMaterialMapping(currentFaceId, surfIt->materialMapper); if (eOk != status) { return checkRet(status); } } if (surfIt->hasColor) { status = builder.setFaceColor(currentFaceId, surfIt->color); if (eOk != status) { return checkRet(status); } } const BrepBuilderInitialLoopArray& loops = surfIt->loops; for (BrepBuilderInitialLoopArray::const_iterator loopIt = loops.begin(); loopIt != loops.end(); ++loopIt) { BRepBuilderGeometryId currentLoopId = builder.addLoop(currentFaceId); const BrepBuilderInitialCoedgeArray& coedges = loopIt->coedges; for (BrepBuilderInitialCoedgeArray::const_iterator coedgeIt = coedges.begin(); coedgeIt != coedges.end(); ++coedgeIt) { filler.m_coedges.append(coedgeIt->curve); builder.addCoedge(currentLoopId, edgeIds[coedgeIt->edgeIndex], coedgeIt->direction, coedgeIt->curve); } builder.finishLoop(currentLoopId); } builder.finishFace(currentFaceId); } if (addComplexShell) { builder.finishShell(currentShellId); } } if (addComplexShell) { builder.finishComplex(currentComplexId); } } if (filler.m_params.isGlobalBrepTransform() && !data.transformation.isNull()) status = builder.setTransformation(*data.transformation); return checkRet(status); } /*static*/ OdResult OdBrepBuilderFillerHelper::getDataFrom( BrepBuilderInitialData& data, const OdBrepBuilderFiller& filler, const OdBrBrep& brep, OdIMaterialAndColorHelper* materialHelper) { if (!brep.isValid()) { return OdBrepBuilderFillerHelper::checkRet(eNotInitializedYet); } OdResult status = eOk; try { if (materialHelper) { status = materialHelper->init(filler.params()); if (eOk != status) { return OdBrepBuilderFillerHelper::checkRet(status); } } OdBrepBuilderFillerHelper brepBuilderFillerHelper(data, filler, materialHelper); status = brepBuilderFillerHelper.performBrep(brep); } catch (const OdError& err) { return OdBrepBuilderFillerHelper::checkRet(err.code()); } catch (...) { return OdBrepBuilderFillerHelper::checkRet(eGeneralModelingFailure); } return OdBrepBuilderFillerHelper::checkRet(status); } // OdResult OdBrepBuilderFiller::initFrom(OdBrepBuilder& builder, const BrepBuilderInitialData& data) { if (!builder.isValid()) { return OdBrepBuilderFillerHelper::checkRet(eNotInitializedYet); } // TODO check can add geom (not implemented now) //if (!builder.canAddGeometry()) { // return OdBrepBuilderFillerHelper::checkRet(eInvalidInput); //} clearTempArrays(); OdResult status = eOk; try { status = OdBrepBuilderFillerHelper::initFromImpl(*this, builder, data); } catch (const OdError& err) { return OdBrepBuilderFillerHelper::checkRet(err.code()); } catch (...) { return OdBrepBuilderFillerHelper::checkRet(eGeneralModelingFailure); } return OdBrepBuilderFillerHelper::checkRet(status); } OdResult OdBrepBuilderFiller::initFrom(OdBrepBuilder& builder, const OdBrBrep& brep, OdIMaterialAndColorHelper* materialHelper) { BrepBuilderInitialData initData; OdResult status = OdBrepBuilderFillerHelper::getDataFrom(initData, *this, brep, materialHelper); if (eOk != status) { return OdBrepBuilderFillerHelper::checkRet(status); } return OdBrepBuilderFillerHelper::checkRet(initFrom(builder, initData)); } OdResult OdBrepBuilderFiller::initFromNURBSingleFace(OdBrepBuilder& builder, const OdBrBrep& brep) { clearTempArrays(); try { OdBrErrorStatus err = odbrOK; OdBrBrepFaceTraverser bft; BrepBuilderInitialData ignore; OdBrepBuilderFillerHelper fillerHelper(ignore, *this); if (bft.setBrep(brep) != odbrOK) { return eBrBrepMissed; } while (!bft.done() && (err == odbrOK)) { OdBrFaceLoopTraverser faLoTrav; OdBrFace face = bft.getFace(); OdGeNurbSurface *pNurbSurf = new OdGeNurbSurface; face.getSurfaceAsNurb(*pNurbSurf); m_surfaces.append(pNurbSurf); //add nurbs surface to BB bool bOrientToSurface = face.getOrientToSurface(); BRepBuilderGeometryId complexId = builder.addComplex(); BRepBuilderGeometryId shellId = builder.addShell(complexId); BRepBuilderGeometryId faceId = builder.addFace(pNurbSurf, bOrientToSurface ? OdBrepBuilder::kForward : OdBrepBuilder::kReversed, shellId); //iterate over trimming-loop and create data for BB for (faLoTrav.setFace(face); !faLoTrav.done(); faLoTrav.next()) { BRepBuilderGeometryId LoopId = builder.addLoop(faceId); OdBrLoopEdgeTraverser loEdTrav; OdBrLoop loop = faLoTrav.getLoop(); err = loEdTrav.setLoop(loop); if (odbrDegenerateTopology == err) { OdGeCurve3dPtr pCurve3d; OdGeCurve2dPtr pCurve2d; OdResult resStatus = fillerHelper.performLoopWithApex(loop, pCurve3d, pCurve2d); if (eOk == resStatus) { m_edges.append(pCurve3d); m_coedges.append(pCurve2d); BRepBuilderGeometryId edgeId = builder.addEdge(pCurve3d); builder.addCoedge(LoopId, edgeId, OdBrepBuilder::kForward, pCurve2d); err = odbrOK; continue; } return resStatus; } OdArray arrBrepEdges; OdArray arrBrepEdgesID; for (; !loEdTrav.done(); loEdTrav.next()) { OdBrEdge edge = loEdTrav.getEdge(); unsigned int iFindIndex = 0; bool bFindEdge = false; for (unsigned int k = 0; k < arrBrepEdges.size(); k++) { if (edge.isEqualTo(&arrBrepEdges[k])) { //In case of nurbs cone we have one edge and 2 coedges, but brep returns 2 edges iFindIndex = k; bFindEdge = true; break; } } OdGeCurve3dPtr pCurve3d; OdResult resStatus = fillerHelper.getEdgeCurveFixed(edge, pCurve3d); if (eOk != resStatus) { return resStatus; } m_edges.append(pCurve3d); OdGeCurve2dPtr pCurve2d; OdGeSurfacePtr surface = m_surfaces.first(); resStatus = fillerHelper.getParamCurveFixed(surface, pCurve3d, pCurve2d, false, false); if (eOk != resStatus) return resStatus; m_coedges.append(pCurve2d); BRepBuilderGeometryId edgeId; if (!bFindEdge) { edgeId = builder.addEdge(pCurve3d); builder.addCoedge(LoopId, edgeId, edge.getOrientToCurve() == loEdTrav.getEdgeOrientToLoop() ? OdBrepBuilder::kForward : OdBrepBuilder::kReversed, pCurve2d); arrBrepEdgesID.append(edgeId); arrBrepEdges.append(edge); } else { builder.addCoedge(LoopId, arrBrepEdgesID.at(iFindIndex), edge.getOrientToCurve() == loEdTrav.getEdgeOrientToLoop() ? OdBrepBuilder::kForward : OdBrepBuilder::kReversed, pCurve2d); } } } builder.finishFace(faceId); builder.finishShell(shellId); builder.finishComplex(complexId); break;//Only one face } } catch (const OdError& err) { throw err; } catch (...) { return eInvalidInput; } return eOk; } void OdBrepBuilderFiller::clearTempArrays() { m_edges.clear(); m_coedges.clear(); m_surfaces.clear(); } // Member methods of BrepBuilderInitialData.h classes OdResult BrepBuilderInitialSurface::setupVisualInfo(const OdBrFace& face, OdIMaterialAndColorHelper* pMaterialHelper) { if (!pMaterialHelper) return eOk; return pMaterialHelper->getFaceVisualInfo(face, material, materialMapper, hasMaterialMapping, color, hasColor); } OdResult BrepBuilderInitialEdge::setupVisualInfo(const OdBrEdge& edge, OdIMaterialAndColorHelper* pMaterialHelper) { if (!pMaterialHelper) return eOk; return pMaterialHelper->getEdgeVisualInfo(edge, color, hasColor); } double BrepBuilderInitialEdge::approxLength() { if (curve.isNull()) throw OdError(eGeneralModelingFailure); if (m_approxLength == 0.) { OdGe::EntityId curveType = curve->type(); if (OdGe::kLineSeg3d == curveType || OdGe::kCircArc3d == curveType) m_approxLength = curve->length(); else { OdGePoint3dArray points; points.reserve(7); curve->appendSamplePoints(7, points); OdGePoint3d prevPoint = points[0]; for (OdArrayBuffer::size_type idx = 1; idx < points.size(); ++idx) { OdGePoint3d currPoint = points[idx]; m_approxLength += currPoint.distanceTo(prevPoint); prevPoint = currPoint; } } } return m_approxLength; }