| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <BRepAdaptor_Curve.hxx> |
| | #include <BRepAdaptor_Surface.hxx> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Common.h> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Cut.h> |
| | #include <BRepAlgo_NormalProjection.hxx> |
| | #include <BRepBndLib.hxx> |
| | #include <BRepBuilderAPI_Copy.hxx> |
| | #include <BRepBuilderAPI_MakeEdge.hxx> |
| | #include <BRepBuilderAPI_MakeFace.hxx> |
| | #include <BRepBuilderAPI_MakeWire.hxx> |
| | #include <BRepBuilderAPI_Transform.hxx> |
| | #include <BRepGProp.hxx> |
| | #include <BRepLProp_SLProps.hxx> |
| | #include <BRepLib.hxx> |
| | #include <BRepPrimAPI_MakeBox.hxx> |
| | #include <BRepPrimAPI_MakeHalfSpace.hxx> |
| | #include <BRepPrimAPI_MakePrism.hxx> |
| | #include <BRepTools.hxx> |
| | #include <BRep_Builder.hxx> |
| | #include <Bnd_Box.hxx> |
| | #include <Bnd_OBB.hxx> |
| | #include <GC_MakeArcOfCircle.hxx> |
| | #include <GProp_GProps.hxx> |
| | #include <Geom_Plane.hxx> |
| | #include <HLRAlgo_Projector.hxx> |
| | #include <QFuture> |
| | #include <QFutureWatcher> |
| | #include <QtConcurrentRun> |
| | #include <ShapeExtend_WireData.hxx> |
| | #include <TopExp.hxx> |
| | #include <TopExp_Explorer.hxx> |
| | #include <TopoDS.hxx> |
| | #include <TopoDS_Compound.hxx> |
| | #include <TopoDS_Edge.hxx> |
| | #include <TopoDS_Face.hxx> |
| | #include <TopoDS_Shape.hxx> |
| | #include <TopoDS_Vertex.hxx> |
| | #include <gp_Ax2.hxx> |
| | #include <gp_Ax3.hxx> |
| | #include <gp_Dir.hxx> |
| | #include <gp_Pln.hxx> |
| | #include <gp_Pnt.hxx> |
| |
|
| | #include <GeomAdaptor_Surface.hxx> |
| | #include <BRepBuilderAPI_MakeVertex.hxx> |
| | #include <GeomLib_IsPlanarSurface.hxx> |
| |
|
| | #include <cmath> |
| |
|
| | #include <sstream> |
| |
|
| | #include <App/Application.h> |
| | #include <App/Document.h> |
| | #include <App/Material.h> |
| | #include <Base/BoundBox.h> |
| | #include <Base/Console.h> |
| | #include <Base/Converter.h> |
| | #include <Base/Exception.h> |
| | #include <Base/FileInfo.h> |
| | #include <Base/Interpreter.h> |
| | #include <Base/Parameter.h> |
| | #include <Base/Tools.h> |
| |
|
| | #include <Mod/Part/App/PartFeature.h> |
| |
|
| | #include "DrawComplexSection.h" |
| | #include "DrawUtil.h" |
| | #include "GeometryObject.h" |
| | #include "ShapeUtils.h" |
| |
|
| | using namespace TechDraw; |
| | using namespace std; |
| | using DU = DrawUtil; |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | PROPERTY_SOURCE(TechDraw::DrawComplexSection, TechDraw::DrawViewSection) |
| |
|
| | const char* DrawComplexSection::ProjectionStrategyEnums[] = {"Offset", "Aligned", "NoParallel", |
| | nullptr}; |
| | |
| |
|
| | DrawComplexSection::DrawComplexSection() : |
| | m_waitingForAlign(false) |
| | { |
| | static const char* fgroup = "Cutting Tool"; |
| |
|
| | |
| | ADD_PROPERTY_TYPE(CuttingToolWireObject, (nullptr), fgroup, App::Prop_None, |
| | "A sketch that describes the cutting tool"); |
| | CuttingToolWireObject.setScope(App::LinkScope::Global); |
| | ProjectionStrategy.setEnums(ProjectionStrategyEnums); |
| | ADD_PROPERTY_TYPE(ProjectionStrategy, ((long)0), fgroup, App::Prop_None, |
| | "Make a single cut, or use the profile in pieces"); |
| | |
| | } |
| |
|
| | TopoDS_Shape DrawComplexSection::makeCuttingTool(double dMax) |
| | { |
| | TopoDS_Wire profileWire = makeProfileWire(); |
| | if (profileWire.IsNull()) { |
| | throw Base::RuntimeError("Can not make wire from cutting tool (1)"); |
| | } |
| |
|
| | if (debugSection()) { |
| | BRepTools::Write(profileWire, "DCSmakeCuttingTool_profileWire.brep"); |
| | } |
| |
|
| | |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | |
| | |
| | constexpr double AngleThresholdDeg{5.0}; |
| | |
| | validateOffsetProfile(profileWire, SectionNormal.getValue(), AngleThresholdDeg); |
| | } |
| |
|
| | |
| | |
| | if (CuttingToolWireObject.getValue()->isDerivedFrom(Base::Type::fromName("Sketcher::SketchObject"))) { |
| | if (!validateSketchNormal(CuttingToolWireObject.getValue())) { |
| | Base::Console().warning("cutting object not aligned with section normal in %s\n", Label.getValue()); |
| | } |
| | } |
| |
|
| | if (BRep_Tool::IsClosed(profileWire)) { |
| | return makeCuttingToolFromClosedProfile(profileWire, dMax); |
| | } |
| |
|
| | TopoDS_Shape cuttingTool = cuttingToolFromProfile(profileWire, dMax); |
| | if (debugSection()) { |
| | BRepTools::Write(cuttingTool, "DCSmakeCuttingTool_cuttingToo.brep"); |
| | } |
| |
|
| | |
| | auto extrudeDir = Base::convertTo<gp_Dir>(getReferenceAxis()); |
| | gp_Vec extrudeVec = 2 * dMax * extrudeDir; |
| | m_toolFaceShape = BRepPrimAPI_MakePrism(profileWire, extrudeVec).Shape(); |
| | m_toolFaceShape = ShapeUtils::moveShape(m_toolFaceShape, Base::convertTo<Base::Vector3d>(extrudeDir) * -dMax); |
| | if (debugSection()) { |
| | BRepTools::Write(m_toolFaceShape, "DCSmakeCuttingTool_m_toolFaceShape.brep"); |
| | } |
| |
|
| | if (cuttingTool.ShapeType() == TopAbs_COMPSOLID || |
| | cuttingTool.ShapeType() == TopAbs_COMPOUND) { |
| | |
| | |
| | |
| | return removeEmptyShapes(cuttingTool); |
| | } |
| |
|
| | return cuttingTool; |
| | } |
| |
|
| | TopoDS_Shape DrawComplexSection::getShapeToPrepare() const |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | return DrawViewSection::getShapeToPrepare(); |
| | } |
| | |
| | return m_saveShape; |
| | } |
| |
|
| | |
| | TopoDS_Shape DrawComplexSection::prepareShape(const TopoDS_Shape& cutShape, double shapeSize) |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | return DrawViewSection::prepareShape(cutShape, shapeSize); |
| | } |
| |
|
| | |
| | if (m_alignResult.IsNull()) { |
| | return {}; |
| | } |
| |
|
| | |
| | |
| | m_preparedShape = ShapeUtils::scaleShape(m_alignResult, getScale()); |
| | if (!DrawUtil::fpCompare(Rotation.getValue(), 0.0)) { |
| | m_preparedShape = |
| | ShapeUtils::rotateShape(m_preparedShape, getProjectionCS(), Rotation.getValue()); |
| | } |
| |
|
| | if (debugSection()) { |
| | BRepTools::Write(m_preparedShape, "DCSprepareShape_preparedShape.brep"); |
| | } |
| |
|
| | return m_preparedShape; |
| | } |
| |
|
| |
|
| | void DrawComplexSection::makeSectionCut(const TopoDS_Shape& baseShape) |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | return DrawViewSection::makeSectionCut(baseShape); |
| | } |
| |
|
| | try { |
| | connectAlignWatcher = |
| | QObject::connect(&m_alignWatcher, &QFutureWatcherBase::finished, &m_alignWatcher, |
| | [this] { this->onSectionCutFinished(); }); |
| |
|
| | |
| | |
| | |
| | auto lambda = [this, baseShape]{this->makeAlignedPieces(baseShape);}; |
| | m_alignFuture = QtConcurrent::run(std::move(lambda)); |
| | m_alignWatcher.setFuture(m_alignFuture); |
| | waitingForAlign(true); |
| | } |
| | catch (...) { |
| | Base::Console().warning("%s failed to make alignedPieces\n", Label.getValue()); |
| | return; |
| | } |
| |
|
| | return DrawViewSection::makeSectionCut(baseShape); |
| | } |
| |
|
| |
|
| | void DrawComplexSection::onSectionCutFinished() |
| | { |
| | if (m_cutFuture.isRunning() || |
| | m_alignFuture.isRunning()) { |
| | |
| | return; |
| | } |
| |
|
| | DrawViewSection::onSectionCutFinished(); |
| |
|
| | QObject::disconnect(connectAlignWatcher); |
| | } |
| |
|
| | |
| | |
| | void DrawComplexSection::makeAlignedPieces(const TopoDS_Shape& rawShape) |
| | { |
| | if (!canBuild(getSectionCS(), CuttingToolWireObject.getValue())) { |
| | throw Base::RuntimeError("Profile is parallel to Section Normal"); |
| | } |
| |
|
| | TopoDS_Wire profileWire = makeProfileWire(); |
| | if (profileWire.IsNull()) { |
| | throw Base::RuntimeError("ComplexSection failed to make profileWire"); |
| | } |
| | auto edgesAll = getUniqueEdges(profileWire); |
| | if (edgesAll.empty()) { |
| | |
| | throw Base::RuntimeError("Complex section: profile wire has no edges."); |
| | } |
| |
|
| | |
| | |
| | double horizReverser{1.0}; |
| | double verticalReverser{1.0}; |
| | gp_Vec gProfileVec = makeProfileVector(profileWire); |
| | auto isProfileVertical = getReversers(gProfileVec, horizReverser, verticalReverser); |
| |
|
| | |
| | |
| | std::vector<TopoDS_Shape> pieces(edgesAll.size()); |
| | std::vector<double> pieceXSizeAll(edgesAll.size()); |
| | std::vector<double> pieceYSizeAll(edgesAll.size()); |
| | std::vector<double> pieceZSizeAll(edgesAll.size()); |
| | std::vector<double> pieceVerticalAll(edgesAll.size()); |
| |
|
| | auto uSectionNormal = SectionNormal.getValue(); |
| | uSectionNormal.Normalize(); |
| | auto uRotateAxis = getReferenceAxis(); |
| | uRotateAxis.Normalize(); |
| |
|
| | |
| | |
| | std::vector<std::pair<int, Base::Vector3d>> faceNormals = getSegmentViewDirections(profileWire, uSectionNormal); |
| |
|
| | |
| | TopExp_Explorer expFaces(m_toolFaceShape, TopAbs_FACE); |
| | for (int iPiece = 0; expFaces.More(); expFaces.Next(), iPiece++) { |
| | TopoDS_Face face = TopoDS::Face(expFaces.Current()); |
| | if (!isFacePlanar(face)) { |
| | |
| | continue; |
| | } |
| |
|
| | |
| | |
| | std::pair<int, Base::Vector3d> segmentPair = findNormalForFace(face, faceNormals, edgesAll); |
| | int segmentIndex = segmentPair.first; |
| | Base::Vector3d segmentNormal = segmentPair.second * -1; |
| | segmentNormal.Normalize(); |
| |
|
| | |
| | if (!showSegment(segmentNormal)) { |
| | continue; |
| | } |
| |
|
| | double pieceVertical{0}; |
| | TopoDS_Shape rotatedPiece = cutAndRotatePiece(rawShape, face, segmentIndex, segmentNormal, pieceVertical); |
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSmakeAlignedPieces_cutAndRotatedPiece" << segmentIndex << ".brep"; |
| | BRepTools::Write(rotatedPiece, ss.str().c_str()); |
| | } |
| |
|
| | AlignedSizeResponse sizeResponse = getAlignedSize(rotatedPiece, segmentIndex); |
| | Base::Vector3d pieceSize = sizeResponse.pieceSize; |
| | pieceXSizeAll.at(segmentIndex) = pieceSize.x; |
| | pieceYSizeAll.at(segmentIndex) = pieceSize.y; |
| | pieceZSizeAll.at(segmentIndex) = pieceSize.z; |
| | pieceVerticalAll.at(segmentIndex) = pieceVertical; |
| |
|
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSAlignedPiece" << segmentIndex << ".brep"; |
| | BRepTools::Write(sizeResponse.alignedPiece, ss.str().c_str()); |
| | } |
| |
|
| | auto pieceOnPlane = movePieceToPaperPlane(sizeResponse.alignedPiece, sizeResponse.zMax); |
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSpieceOnPlane" << segmentIndex << ".brep"; |
| | BRepTools::Write(pieceOnPlane, ss.str().c_str()); |
| | } |
| | |
| | pieces.at(segmentIndex) = pieceOnPlane; |
| | } |
| |
|
| | if (pieces.empty()) { |
| | m_alignResult = TopoDS_Compound(); |
| | Base::Console().message("DCS::makeAlignedPieces - no result\n"); |
| | return; |
| | } |
| |
|
| | |
| | double movementReverser = isProfileVertical ? verticalReverser : horizReverser; |
| | auto movementAxis = gp_Vec(gp::OX().Direction()); |
| | auto alignmentAxis = gp_Vec(gp::OY().Direction().Reversed()); |
| | if (isProfileVertical) { |
| | movementAxis = gp_Vec(gp::OY().Direction()); |
| | alignmentAxis = gp_Vec(gp::OX().Direction()); |
| | } |
| | gp_Vec gMovementVector = movementAxis * movementReverser; |
| |
|
| | size_t stopAt = pieces.size(); |
| | double cursorPosition = 0.0; |
| | for (size_t iPiece = 0; iPiece < stopAt; iPiece++) { |
| | double pieceSizeMoveDist = pieceXSizeAll.at(iPiece); |
| | if (isProfileVertical) { |
| | pieceSizeMoveDist = pieceYSizeAll.at(iPiece); |
| | } |
| | auto movedPiece = distributePiece(pieces.at(iPiece), pieceSizeMoveDist, pieceVerticalAll.at(iPiece), |
| | alignmentAxis, gMovementVector, cursorPosition); |
| | pieces.at(iPiece) = movedPiece; |
| | cursorPosition += pieceSizeMoveDist; |
| |
|
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSMovedPiece" << iPiece << ".brep"; |
| | BRepTools::Write(pieces.at(iPiece), ss.str().c_str()); |
| | } |
| | } |
| |
|
| | |
| | BRep_Builder builder; |
| | TopoDS_Compound comp; |
| | builder.MakeCompound(comp); |
| | for (auto& piece : pieces) { |
| | builder.Add(comp, piece); |
| | } |
| |
|
| | |
| | Base::Vector3d centerVector = Base::convertTo<Base::Vector3d>(gMovementVector) * cursorPosition / -2; |
| | TopoDS_Shape centeredCompound = ShapeUtils::moveShape(comp, centerVector); |
| |
|
| | if (debugSection()) { |
| | BRepTools::Write(centeredCompound, "DCSmap40CenteredCompound.brep"); |
| | } |
| |
|
| | |
| | gp_Ax3 stdCS; |
| | gp_Trsf xPieceAlign; |
| | xPieceAlign.SetTransformation(getProjectionCS(), stdCS); |
| | BRepBuilderAPI_Transform mkTransAlign(centeredCompound, xPieceAlign); |
| | TopoDS_Shape alignedCompound = mkTransAlign.Shape(); |
| |
|
| | if (debugSection()) { |
| | BRepTools::Write(alignedCompound, "DCSmap50AlignedCompound.brep"); |
| | } |
| |
|
| | m_alignResult = alignedCompound; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | TopoDS_Compound |
| | DrawComplexSection::findSectionPlaneIntersections(const TopoDS_Shape& shapeToIntersect) |
| | { |
| | if (shapeToIntersect.IsNull()) { |
| | |
| | Base::Console().warning("DCS::findSectionPlaneInter - %s - cut shape is Null\n", |
| | getNameInDocument()); |
| | return {}; |
| | } |
| | if (ProjectionStrategy.getValue() == 0) { |
| | return singleToolIntersections(shapeToIntersect); |
| | } |
| |
|
| | return alignedToolIntersections(shapeToIntersect); |
| | } |
| |
|
| | |
| | TopoDS_Compound DrawComplexSection::singleToolIntersections(const TopoDS_Shape& cutShape) |
| | { |
| | App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); |
| | if (!isLinearProfile(toolObj)) { |
| | |
| | |
| | } |
| |
|
| | BRep_Builder builder; |
| | TopoDS_Compound result; |
| | builder.MakeCompound(result); |
| |
|
| | if (m_toolFaceShape.IsNull()) { |
| | return result; |
| | } |
| |
|
| | TopExp_Explorer expFaces(cutShape, TopAbs_FACE); |
| | for (; expFaces.More(); expFaces.Next()) { |
| | TopoDS_Face face = TopoDS::Face(expFaces.Current()); |
| | if (!boxesIntersect(face, m_toolFaceShape)) { |
| | continue; |
| | } |
| | std::vector<TopoDS_Face> commonFaces = faceShapeIntersect(face, m_toolFaceShape); |
| | for (auto& cFace : commonFaces) { |
| | builder.Add(result, cFace); |
| | } |
| | } |
| | return result; |
| | } |
| |
|
| | |
| | TopoDS_Compound DrawComplexSection::alignedToolIntersections(const TopoDS_Shape& cutShape) |
| | { |
| | BRep_Builder builder; |
| | TopoDS_Compound result; |
| | builder.MakeCompound(result); |
| |
|
| | App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); |
| | if (!isLinearProfile(toolObj)) { |
| | |
| | |
| | } |
| |
|
| | gp_Pln effectivePlane = getSectionPlane(); |
| | |
| | |
| | BRepBuilderAPI_MakeFace mkFace(effectivePlane, -Precision::Infinite(), Precision::Infinite(), |
| | -Precision::Infinite(), Precision::Infinite()); |
| | TopoDS_Face cuttingFace = mkFace.Face(); |
| |
|
| | TopExp_Explorer expFaces(cutShape, TopAbs_FACE); |
| | for (; expFaces.More(); expFaces.Next()) { |
| | TopoDS_Face face = TopoDS::Face(expFaces.Current()); |
| | if (!boxesIntersect(face, cuttingFace)) { |
| | continue; |
| | } |
| | std::vector<TopoDS_Face> commonFaces = faceShapeIntersect(face, cuttingFace); |
| | for (auto& cFace : commonFaces) { |
| | builder.Add(result, cFace); |
| | } |
| | } |
| | if (debugSection()) { |
| | BRepTools::Write(result, "DCSmakeAlignedPieces_AlignedIntersectionResult.brep"); |
| | } |
| | return result; |
| | } |
| |
|
| | TopoDS_Compound DrawComplexSection::alignSectionFaces(const TopoDS_Shape& faceIntersections) |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | return DrawViewSection::alignSectionFaces(faceIntersections); |
| | } |
| |
|
| | return TopoDS::Compound(mapToPage(faceIntersections)); |
| | } |
| |
|
| | TopoDS_Shape DrawComplexSection::getShapeToIntersect() |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | return DrawViewSection::getShapeToIntersect(); |
| | } |
| | |
| | return m_preparedShape; |
| | } |
| |
|
| | TopoDS_Shape DrawComplexSection::getShapeForDetail() const |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | return DrawViewSection::getShapeForDetail(); |
| | } |
| | |
| | return m_preparedShape; |
| | } |
| |
|
| | TopoDS_Wire DrawComplexSection::makeProfileWire() const |
| | { |
| | App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); |
| | return makeProfileWire(toolObj); |
| | } |
| |
|
| | TopoDS_Wire DrawComplexSection::makeProfileWire(App::DocumentObject* toolObj) |
| | { |
| | if (!isProfileObject(toolObj)) { |
| | return {}; |
| | } |
| |
|
| | TopoDS_Shape toolShape = Part::Feature::getShape(toolObj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); |
| | if (toolShape.IsNull()) { |
| | return {}; |
| | } |
| |
|
| | TopoDS_Wire profileWire; |
| | if (toolShape.ShapeType() == TopAbs_WIRE) { |
| | profileWire = makeNoseToTailWire(toolShape); |
| | } |
| | else { |
| | |
| | TopoDS_Edge edge = TopoDS::Edge(toolShape); |
| | profileWire = BRepBuilderAPI_MakeWire(edge).Wire(); |
| | } |
| | return profileWire; |
| | } |
| |
|
| | gp_Vec DrawComplexSection::makeProfileVector(const TopoDS_Wire& profileWire) |
| | { |
| | auto ends = getWireEnds(profileWire); |
| | auto vec = ends.second - ends.first; |
| | vec.Normalize(); |
| | return Base::convertTo<gp_Vec>(vec); |
| | } |
| |
|
| |
|
| | |
| | BaseGeomPtrVector DrawComplexSection::makeSectionLineGeometry() |
| | { |
| | BaseGeomPtrVector result; |
| | auto* baseDvp = freecad_cast<DrawViewPart*>(BaseView.getValue()); |
| | if (baseDvp) { |
| | TopoDS_Wire lineWire = makeSectionLineWire(); |
| | |
| | TopoDS_Shape projectedWire = |
| | GeometryObject::projectSimpleShape(lineWire, baseDvp->getProjectionCS()); |
| | TopExp_Explorer expEdges(projectedWire, TopAbs_EDGE); |
| | for (; expEdges.More(); expEdges.Next()) { |
| | BaseGeomPtr edge = BaseGeom::baseFactory(TopoDS::Edge(expEdges.Current())); |
| | result.push_back(edge); |
| | } |
| | } |
| | return result; |
| | } |
| |
|
| | |
| | std::pair<Base::Vector3d, Base::Vector3d> DrawComplexSection::sectionLineEnds() |
| | { |
| | std::pair<Base::Vector3d, Base::Vector3d> result; |
| | TopoDS_Wire lineWire = makeSectionLineWire(); |
| | if (lineWire.IsNull()) { |
| | return result; |
| | } |
| |
|
| | auto ends = getWireEnds(lineWire); |
| | Base::Vector3d first = ends.first; |
| | Base::Vector3d last = ends.second; |
| |
|
| | auto* baseDvp = freecad_cast<DrawViewPart*>(BaseView.getValue()); |
| | if (baseDvp) { |
| | first = baseDvp->projectPoint(first); |
| | last = baseDvp->projectPoint(last); |
| | } |
| | result.first = first; |
| | result.second = last; |
| | return result; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | std::pair<Base::Vector3d, Base::Vector3d> DrawComplexSection::sectionLineArrowDirs() |
| | { |
| | App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); |
| | TopoDS_Wire profileWire = makeProfileWire(toolObj); |
| | if (profileWire.IsNull()) { |
| | throw Base::RuntimeError("Complex section profile wire is null"); |
| | } |
| |
|
| | std::vector<std::pair<int, Base::Vector3d>> segmentNormals = |
| | getSegmentViewDirections(profileWire, SectionNormal.getValue()); |
| | if (segmentNormals.empty()) { |
| | throw Base::RuntimeError("A complex section failed to create profile segment view directions"); |
| | } |
| |
|
| | Base::Vector3d firstArrowDir = segmentNormals.front().second * -1; |
| | Base::Vector3d lastArrowDir = segmentNormals.back().second * -1; |
| |
|
| | return { firstArrowDir, lastArrowDir }; |
| | } |
| |
|
| |
|
| | |
| | std::pair<Base::Vector3d, Base::Vector3d> |
| | DrawComplexSection::sectionLineArrowDirsMapped() |
| | { |
| | std::pair<Base::Vector3d, Base::Vector3d> arrowDirsRaw = sectionLineArrowDirs(); |
| | TopoDS_Edge firstMapped = mapEdgeToBase(arrowDirsRaw.first); |
| | TopoDS_Edge lastMapped = mapEdgeToBase(arrowDirsRaw.second); |
| | std::pair<Base::Vector3d, Base::Vector3d> arrowEnds = getSegmentEnds(firstMapped); |
| | Base::Vector3d firstDir = arrowEnds.second - arrowEnds.first; |
| | arrowEnds = getSegmentEnds(lastMapped); |
| | Base::Vector3d lastDir = arrowEnds.second - arrowEnds.first; |
| |
|
| | return { firstDir, lastDir }; |
| | } |
| |
|
| |
|
| | |
| | Base::Vector3d DrawComplexSection::getReferenceAxis() const |
| | { |
| | Base::Vector3d rawDirection = getBaseDVP()->Direction.getValue(); |
| | rawDirection.Normalize(); |
| |
|
| | return DU::closestBasisOriented(rawDirection); |
| | } |
| |
|
| |
|
| | |
| | TopoDS_Wire DrawComplexSection::makeSectionLineWire() |
| | { |
| | TopoDS_Wire lineWire; |
| | App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); |
| | auto* baseDvp = freecad_cast<DrawViewPart*>(BaseView.getValue()); |
| | if (baseDvp) { |
| | TopoDS_Shape toolShape = Part::Feature::getShape(toolObj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); |
| | if (toolShape.IsNull()) { |
| | |
| | return {}; |
| | } |
| | Base::Vector3d centroid = baseDvp->getCurrentCentroid(); |
| | TopoDS_Shape sTrans = |
| | ShapeUtils::ShapeUtils::moveShape(toolShape, centroid * -1.0); |
| | TopoDS_Shape sScaled = ShapeUtils::scaleShape(sTrans, baseDvp->getScale()); |
| | |
| |
|
| | if (sScaled.ShapeType() == TopAbs_WIRE) { |
| | lineWire = makeNoseToTailWire(sScaled); |
| | } |
| | else if (sScaled.ShapeType() == TopAbs_EDGE) { |
| | TopoDS_Edge edge = TopoDS::Edge(sScaled); |
| | lineWire = BRepBuilderAPI_MakeWire(edge).Wire(); |
| | } |
| | else { |
| | |
| | Base::Console().warning("DCS::makeSectionLineGeometry - profile is type: %d\n", |
| | static_cast<int>(sScaled.ShapeType())); |
| | return {}; |
| | } |
| | } |
| | return lineWire; |
| | } |
| |
|
| |
|
| | |
| | |
| | ChangePointVector DrawComplexSection::getChangePointsFromSectionLine() |
| | { |
| | ChangePointVector result; |
| | std::vector<gp_Pnt> allPoints; |
| | auto* baseDvp = freecad_cast<DrawViewPart*>(BaseView.getValue()); |
| | if (baseDvp) { |
| | TopoDS_Wire lineWire = makeSectionLineWire(); |
| | |
| | TopoDS_Shape projectedWire = |
| | GeometryObject::projectSimpleShape(lineWire, baseDvp->getProjectionCS()); |
| | if (projectedWire.IsNull()) { |
| | return result; |
| | } |
| | |
| | TopExp_Explorer expVertex(projectedWire, TopAbs_VERTEX); |
| | gp_Pnt previousPoint(Precision::Infinite(), Precision::Infinite(), Precision::Infinite()); |
| | for (; expVertex.More(); expVertex.Next()) { |
| | TopoDS_Vertex vert = TopoDS::Vertex(expVertex.Current()); |
| | gp_Pnt gPoint = BRep_Tool::Pnt(vert); |
| | if (gPoint.IsEqual(previousPoint, 2 * EWTOLERANCE)) { |
| | continue; |
| | } |
| | allPoints.push_back(gPoint); |
| | previousPoint = gPoint; |
| | } |
| |
|
| | |
| | for (size_t iPoint = 1; iPoint < allPoints.size() - 1; iPoint++) { |
| | gp_Pnt location = allPoints.at(iPoint); |
| | gp_Dir preDir = gp_Dir(allPoints.at(iPoint - 1).XYZ() - allPoints.at(iPoint).XYZ()); |
| | gp_Dir postDir = gp_Dir(allPoints.at(iPoint + 1).XYZ() - allPoints.at(iPoint).XYZ()); |
| | ChangePoint point(location, preDir, postDir); |
| | result.push_back(point); |
| | } |
| |
|
| | |
| | gp_Pnt location0 = allPoints.at(0); |
| | gp_Pnt location1 = allPoints.at(1); |
| | gp_Dir postDir = gp_Dir(location1.XYZ() - location0.XYZ()); |
| | gp_Dir preDir = postDir.Reversed(); |
| | ChangePoint startPoint(location0, preDir, postDir); |
| | result.push_back(startPoint); |
| | location0 = allPoints.at(allPoints.size() - 1); |
| | location1 = allPoints.at(allPoints.size() - 2); |
| | preDir = gp_Dir(location0.XYZ() - location1.XYZ()); |
| | postDir = preDir.Reversed(); |
| | ChangePoint endPoint(location0, preDir, postDir); |
| | result.push_back(endPoint); |
| | } |
| | return result; |
| | } |
| |
|
| | gp_Ax2 DrawComplexSection::getCSFromBase(const std::string& sectionName) const |
| | { |
| | App::DocumentObject* base = BaseView.getValue(); |
| | if (!base |
| | || !base->isDerivedFrom<TechDraw::DrawViewPart>()) { |
| | |
| | return getSectionCS(); |
| | } |
| | return DrawViewSection::getCSFromBase(sectionName); |
| | } |
| |
|
| | |
| | |
| | |
| | bool DrawComplexSection::validateOffsetProfile(const TopoDS_Wire& profile, Base::Vector3d direction, double angleThresholdDeg) const |
| | { |
| | constexpr double HalfCircleDegrees{180.0}; |
| | double angleThresholdRad = angleThresholdDeg * std::numbers::pi / HalfCircleDegrees; |
| | TopExp_Explorer explEdges(profile, TopAbs_EDGE); |
| | for (; explEdges.More(); explEdges.Next()) { |
| | std::pair<Base::Vector3d, Base::Vector3d> segmentEnds = getSegmentEnds(TopoDS::Edge(explEdges.Current())); |
| | Base::Vector3d segmentDir = segmentEnds.second - segmentEnds.first; |
| | double angleRad = segmentDir.GetAngle(direction); |
| | if (angleRad < angleThresholdRad && |
| | angleRad > 0.0) { |
| | |
| | Base::Console().warning("%s profile is slightly skewed. Check SectionNormal low decimal places\n", |
| | getNameInDocument()); |
| | return false; |
| | } |
| | } |
| | return true; |
| | } |
| |
|
| |
|
| | |
| | std::pair<Base::Vector3d, Base::Vector3d> |
| | DrawComplexSection::getSegmentEnds(const TopoDS_Edge& segment) |
| | { |
| | TopoDS_Vertex tvFirst; |
| | TopoDS_Vertex tvLast; |
| | TopExp::Vertices(segment, tvFirst, tvLast); |
| | gp_Pnt gpFirst = BRep_Tool::Pnt(tvFirst); |
| | gp_Pnt gpLast = BRep_Tool::Pnt(tvLast); |
| | std::pair<Base::Vector3d, Base::Vector3d> result; |
| | result.first = Base::convertTo<Base::Vector3d>(gpFirst); |
| | result.second = Base::convertTo<Base::Vector3d>(gpLast); |
| | return result; |
| | } |
| |
|
| | std::pair<Base::Vector3d, Base::Vector3d> |
| | DrawComplexSection::getWireEnds(const TopoDS_Wire& wire) |
| | { |
| | TopoDS_Vertex tvFirst; |
| | TopoDS_Vertex tvLast; |
| | TopExp::Vertices(wire, tvFirst, tvLast); |
| | gp_Pnt gpFirst = BRep_Tool::Pnt(tvFirst); |
| | gp_Pnt gpLast = BRep_Tool::Pnt(tvLast); |
| | std::pair<Base::Vector3d, Base::Vector3d> result; |
| | result.first = Base::convertTo<Base::Vector3d>(gpFirst); |
| | result.second = Base::convertTo<Base::Vector3d>(gpLast); |
| | return result; |
| | } |
| |
|
| |
|
| | |
| | |
| | gp_Pln DrawComplexSection::getSectionPlane() const |
| | { |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | return DrawViewSection::getSectionPlane(); |
| | } |
| |
|
| | |
| | |
| | Base::Vector3d vSectionNormal = SectionNormal.getValue(); |
| | gp_Dir gSectionNormal(vSectionNormal.x, vSectionNormal.y, vSectionNormal.z); |
| | gp_Pnt gOrigin(0.0, 0.0, 0.0); |
| | gp_Ax3 gPlaneCS(gOrigin, gSectionNormal); |
| |
|
| | return {gPlaneCS}; |
| | } |
| |
|
| | bool DrawComplexSection::isBaseValid() const |
| | { |
| | App::DocumentObject* base = BaseView.getValue(); |
| | if (!base) { |
| | |
| | return true; |
| | } |
| | if (!base->isDerivedFrom<TechDraw::DrawViewPart>()) { |
| | |
| | |
| | return false; |
| | } |
| | |
| | return true; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | bool DrawComplexSection::validateProfilePosition(const TopoDS_Wire& profileWire, const gp_Ax2& sectionCS) const |
| | { |
| | auto wireEnds = getWireEnds(profileWire); |
| | auto gpFirst = Base::convertTo<gp_Pnt>(wireEnds.first); |
| | gp_Vec gProfileVector = makeProfileVector(profileWire); |
| |
|
| | |
| | |
| | gp_Vec gSectionVector = getSectionCS().Direction().Reversed(); |
| | gp_Vec gExtrudeVector = gSectionVector.Crossed(gProfileVector); |
| | Base::Vector3d vClosestBasis = DrawUtil::closestBasis(gp_Dir(gExtrudeVector), sectionCS); |
| | auto gClosestBasis = gp_Dir(vClosestBasis.x, vClosestBasis.y, vClosestBasis.z); |
| |
|
| | Bnd_Box shapeBox; |
| | shapeBox.SetGap(0.0); |
| | BRepBndLib::AddOptimal(m_saveShape, shapeBox); |
| | double xMin = 0, xMax = 0, yMin = 0, yMax = 0, zMin = 0, zMax = 0; |
| | shapeBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); |
| | double spanLow = xMin; |
| | double spanHigh = xMax; |
| | double spanCheck = gpFirst.X(); |
| | if (gClosestBasis.IsParallel(sectionCS.YDirection(), Precision::Angular())) { |
| | spanLow = yMin; |
| | spanHigh = yMax; |
| | spanCheck = gpFirst.Y(); |
| | } |
| | else if (gClosestBasis.IsParallel(sectionCS.Direction(), Precision::Angular())) { |
| | spanLow = zMin; |
| | spanHigh = zMax; |
| | spanCheck = gpFirst.Z(); |
| | } |
| |
|
| | if (spanLow > spanCheck || spanHigh < spanCheck) { |
| | |
| | return false; |
| | } |
| | |
| | return true; |
| | } |
| |
|
| | bool DrawComplexSection::showSegment(gp_Dir segmentNormal) const |
| | { |
| | if (ProjectionStrategy.getValue() < 2) { |
| | |
| | return true; |
| | } |
| |
|
| | Base::Vector3d vSectionNormal = SectionNormal.getValue(); |
| | gp_Dir gSectionNormal(vSectionNormal.x, vSectionNormal.y, vSectionNormal.z); |
| | |
| | |
| | return !DU::fpCompare(fabs(gSectionNormal.Dot(segmentNormal)), 0.0); |
| | } |
| |
|
| | bool DrawComplexSection::showSegment(const Base::Vector3d& segmentNormal) const |
| | { |
| | return showSegment(Base::convertTo<gp_Dir>(segmentNormal)); |
| | } |
| |
|
| | |
| | bool DrawComplexSection::canBuild(gp_Ax2 sectionCS, App::DocumentObject* profileObject) |
| | { |
| | if (!isProfileObject(profileObject)) { |
| | return false; |
| | } |
| |
|
| | TopoDS_Shape shape = Part::Feature::getShape(profileObject, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); |
| | if (BRep_Tool::IsClosed(shape)) { |
| | |
| | return true; |
| | } |
| |
|
| | |
| | gp_Vec gProfileVec = makeProfileVector(makeProfileWire(profileObject)); |
| | double dot = fabs(gProfileVec.Dot(sectionCS.Direction())); |
| | return !DU::fpCompare(dot, 1.0, EWTOLERANCE); |
| | } |
| |
|
| | |
| |
|
| | |
| | gp_Dir DrawComplexSection::getFaceNormal(TopoDS_Face& face) |
| | { |
| | BRepAdaptor_Surface adapt(face); |
| | double uParmFirst = adapt.FirstUParameter(); |
| | double uParmLast = adapt.LastUParameter(); |
| | double vParmFirst = adapt.FirstVParameter(); |
| | double vParmLast = adapt.LastVParameter(); |
| | double uMid = (uParmFirst + uParmLast) / 2; |
| | double vMid = (vParmFirst + vParmLast) / 2; |
| |
|
| | constexpr double PropTolerance{0.01}; |
| | BRepLProp_SLProps prop(adapt, uMid, vMid, 1, PropTolerance); |
| | gp_Dir normalDir(0.0, 0.0, 1.0); |
| | if (prop.IsNormalDefined()) { |
| | normalDir = prop.Normal(); |
| | } |
| | return normalDir; |
| | } |
| |
|
| | bool DrawComplexSection::boxesIntersect(TopoDS_Face& face, TopoDS_Shape& shape) |
| | { |
| | constexpr double OverlapTolerance{0.1}; |
| | Bnd_Box box0; |
| | Bnd_Box box1; |
| | BRepBndLib::Add(face, box0); |
| | box0.SetGap(OverlapTolerance); |
| | BRepBndLib::Add(shape, box1); |
| | box1.SetGap(OverlapTolerance); |
| | return !box0.IsOut(box1); |
| | } |
| |
|
| | TopoDS_Shape DrawComplexSection::shapeShapeIntersect(const TopoDS_Shape& shape0, |
| | const TopoDS_Shape& shape1) |
| | { |
| | FCBRepAlgoAPI_Common anOp; |
| | anOp.SetFuzzyValue(EWTOLERANCE); |
| | TopTools_ListOfShape anArg1; |
| | TopTools_ListOfShape anArg2; |
| | anArg1.Append(shape0); |
| | anArg2.Append(shape1); |
| | anOp.SetArguments(anArg1); |
| | anOp.SetTools(anArg2); |
| | anOp.Build(); |
| | TopoDS_Shape result = anOp.Shape(); |
| | if (isTrulyEmpty(result)) { |
| | return {}; |
| | } |
| | return result; |
| | } |
| |
|
| | |
| | std::vector<TopoDS_Face> DrawComplexSection::faceShapeIntersect(const TopoDS_Face& face, |
| | const TopoDS_Shape& shape) |
| | { |
| | TopoDS_Shape intersect = shapeShapeIntersect(face, shape); |
| | if (intersect.IsNull()) { |
| | return {}; |
| | } |
| | std::vector<TopoDS_Face> intersectFaceList; |
| | TopExp_Explorer expFaces(intersect, TopAbs_FACE); |
| | for (int i = 1; expFaces.More(); expFaces.Next(), i++) { |
| | intersectFaceList.push_back(TopoDS::Face(expFaces.Current())); |
| | } |
| | return intersectFaceList; |
| | } |
| |
|
| | |
| | |
| | TopoDS_Wire DrawComplexSection::makeNoseToTailWire(const TopoDS_Shape& inShape) |
| | { |
| | if (inShape.IsNull()) { |
| | return {}; |
| | } |
| |
|
| | std::list<TopoDS_Edge> inList; |
| | TopExp_Explorer expEdges(inShape, TopAbs_EDGE); |
| | for (; expEdges.More(); expEdges.Next()) { |
| | TopoDS_Edge edge = TopoDS::Edge(expEdges.Current()); |
| | inList.push_back(edge); |
| | } |
| |
|
| | std::list<TopoDS_Edge> sortedList; |
| | if (inList.empty() || inList.size() == 1) { |
| | return {}; |
| | } |
| |
|
| | sortedList = DrawUtil::sort_Edges(EWTOLERANCE, inList); |
| | BRepBuilderAPI_MakeWire mkWire; |
| | for (auto& edge : sortedList) { |
| | mkWire.Add(edge); |
| | } |
| | return mkWire.Wire(); |
| | } |
| |
|
| | |
| | bool DrawComplexSection::isProfileObject(App::DocumentObject* obj) |
| | { |
| | |
| | TopoDS_Shape shape = Part::Feature::getShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); |
| | if (shape.IsNull()) { |
| | return false; |
| | } |
| | if (shape.ShapeType() == TopAbs_WIRE || shape.ShapeType() == TopAbs_EDGE) { |
| | return true; |
| | } |
| | |
| | return false; |
| | } |
| |
|
| | bool DrawComplexSection::isMultiSegmentProfile(App::DocumentObject* obj) |
| | { |
| | |
| | TopoDS_Shape shape = Part::Feature::getShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); |
| | if (shape.IsNull()) { |
| | return false; |
| | } |
| | if (shape.ShapeType() == TopAbs_EDGE) { |
| | |
| | return false; |
| | } |
| | if (shape.ShapeType() == TopAbs_WIRE) { |
| | std::vector<TopoDS_Edge> edgesInWire; |
| | TopExp_Explorer expEdges(shape, TopAbs_EDGE); |
| | for (; expEdges.More(); expEdges.Next()) { |
| | TopoDS_Edge edge = TopoDS::Edge(expEdges.Current()); |
| | BRepAdaptor_Curve adapt(edge); |
| | if (adapt.GetType() == GeomAbs_Line) { |
| | edgesInWire.push_back(edge); |
| | } |
| | } |
| | if (edgesInWire.size() > 1) { |
| | return true; |
| | } |
| | } |
| | return false; |
| | } |
| |
|
| | |
| | bool DrawComplexSection::isLinearProfile(App::DocumentObject* obj) |
| | { |
| | TopoDS_Shape shape = Part::Feature::getShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform); |
| | if (shape.IsNull()) { |
| | return false; |
| | } |
| | if (shape.ShapeType() == TopAbs_EDGE) { |
| | |
| | TopoDS_Edge edge = TopoDS::Edge(shape); |
| | BRepAdaptor_Curve adapt(edge); |
| | return (adapt.GetType() == GeomAbs_Line); |
| | } |
| |
|
| | if (shape.ShapeType() == TopAbs_WIRE) { |
| | std::vector<TopoDS_Edge> edgesInWire; |
| | TopExp_Explorer expEdges(shape, TopAbs_EDGE); |
| | for (; expEdges.More(); expEdges.Next()) { |
| | TopoDS_Edge edge = TopoDS::Edge(expEdges.Current()); |
| | BRepAdaptor_Curve adapt(edge); |
| | if (adapt.GetType() != GeomAbs_Line) { |
| | return false; |
| | } |
| | } |
| | |
| | return true; |
| | } |
| |
|
| | |
| | return false; |
| | } |
| |
|
| | |
| | |
| | |
| | bool DrawComplexSection::isTrulyEmpty(const TopoDS_Shape& inShape) |
| | { |
| | bool hasContent = !inShape.IsNull() && TopoDS_Iterator(inShape).More(); |
| | return !hasContent; |
| | } |
| |
|
| |
|
| | TopoDS_Shape DrawComplexSection::removeEmptyShapes(const TopoDS_Shape& roughTool) |
| | { |
| | BRep_Builder builder; |
| | TopoDS_Compound comp; |
| | builder.MakeCompound(comp); |
| | TopExp_Explorer expSolids(roughTool, TopAbs_SOLID); |
| | for (; expSolids.More(); expSolids.Next()) { |
| | TopoDS_Solid solid = TopoDS::Solid(expSolids.Current()); |
| | GProp_GProps gprops; |
| | BRepGProp::VolumeProperties(solid, gprops); |
| | double volume = gprops.Mass(); |
| | if (volume > EWTOLERANCE) { |
| | builder.Add(comp, solid); |
| | } |
| | } |
| | return comp; |
| | } |
| |
|
| | |
| | |
| | bool DrawComplexSection::getReversers(const gp_Vec& gProfileVec, double& horizReverser, double& verticalReverser) |
| | { |
| | bool isProfileVertical = true; |
| | auto sectionCS = getSectionCS(); |
| | auto sectionCSX = sectionCS.XDirection(); |
| | auto sectionCSY = sectionCS.YDirection(); |
| | auto verticalDot = gProfileVec.Dot(sectionCSY); |
| | if (DU::fpCompare(fabs(verticalDot), 0, EWTOLERANCE)) { |
| | |
| | isProfileVertical = false; |
| | } |
| |
|
| | horizReverser = 1.0; |
| | if (gProfileVec.Dot(sectionCSX) < 0.0) { |
| | |
| | horizReverser = -1.0; |
| | } |
| |
|
| | verticalReverser = 1.0; |
| | if (gProfileVec.Dot(sectionCSY) < 0.0) { |
| | |
| | verticalReverser = -1.0; |
| | } |
| |
|
| | return isProfileVertical; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | AlignedSizeResponse DrawComplexSection::getAlignedSize(const TopoDS_Shape& pieceRotated, |
| | int iPiece) const |
| | { |
| | gp_Ax3 stdCS; |
| | BRepBuilderAPI_Copy BuilderPieceCopy(pieceRotated); |
| | TopoDS_Shape copyPieceRotatedShape = BuilderPieceCopy.Shape(); |
| | gp_Trsf xPieceAlign; |
| | xPieceAlign.SetTransformation(stdCS, getProjectionCS()); |
| | BRepBuilderAPI_Transform mkTransAlign(copyPieceRotatedShape, xPieceAlign); |
| | TopoDS_Shape pieceAligned = mkTransAlign.Shape(); |
| | |
| | gp_Trsf xPieceRecenter; |
| | gp_Vec rotatedCentroid = gp_Vec(ShapeUtils::findCentroid(pieceAligned).XYZ()); |
| | xPieceRecenter.SetTranslation(rotatedCentroid * -1.0); |
| | BRepBuilderAPI_Transform mkTransRecenter(pieceAligned, xPieceRecenter, true); |
| | pieceAligned = mkTransRecenter.Shape(); |
| |
|
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSDpieceAligned" << iPiece << ".brep"; |
| | BRepTools::Write(pieceAligned, ss.str().c_str()); |
| | ss.clear(); |
| | ss.str(std::string()); |
| | } |
| | Bnd_Box shapeBox; |
| | shapeBox.SetGap(0.0); |
| | BRepBndLib::AddOptimal(pieceAligned, shapeBox); |
| | double xMin = 0, xMax = 0, yMin = 0, yMax = 0, zMin = 0, zMax = 0; |
| | shapeBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); |
| | double pieceXSize(xMax - xMin); |
| | double pieceYSize(yMax - yMin); |
| | double pieceZSize(zMax - zMin); |
| | Base::Vector3d pieceSize{pieceXSize, pieceYSize, pieceZSize}; |
| | return {pieceAligned, pieceSize, zMax}; |
| | } |
| |
|
| | |
| | |
| | TopoDS_Shape DrawComplexSection::cutAndRotatePiece(const TopoDS_Shape& rawShape, |
| | const TopoDS_Face& segmentFace, |
| | int iPiece, |
| | Base::Vector3d uOrientedSegmentNormal, |
| | double& pieceVertical) |
| | { |
| | auto segmentNormal = Base::convertTo<gp_Vec>(uOrientedSegmentNormal); |
| | auto rotateAxis = Base::convertTo<gp_Vec>(getReferenceAxis()); |
| | gp_Vec extrudeVec = segmentNormal * m_shapeSize; |
| |
|
| | BRepPrimAPI_MakePrism mkPrism(segmentFace, extrudeVec); |
| | TopoDS_Shape segmentTool = mkPrism.Shape(); |
| | TopoDS_Shape intersect = shapeShapeIntersect(segmentTool, rawShape); |
| | if (intersect.IsNull()) { |
| | return {}; |
| | } |
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSAintersect" << iPiece << ".brep"; |
| | BRepTools::Write(intersect, ss.str().c_str()); |
| | ss.clear(); |
| | ss.str(std::string()); |
| | } |
| |
|
| | |
| | |
| | gp_Trsf xPieceCenter; |
| | gp_Vec pieceCentroid = gp_Vec(ShapeUtils::findCentroid(intersect).XYZ()); |
| |
|
| | |
| | |
| | gp_Vec maskedVertical = DU::maskDirection(pieceCentroid, rotateAxis); |
| | maskedVertical = pieceCentroid - maskedVertical; |
| | pieceVertical = maskedVertical.X() + maskedVertical.Y() + maskedVertical.Z(); |
| |
|
| | xPieceCenter.SetTranslation(pieceCentroid * -1.0); |
| | BRepBuilderAPI_Transform mkTransXLate(intersect, xPieceCenter, true); |
| | TopoDS_Shape pieceCentered = mkTransXLate.Shape(); |
| | if (debugSection()) { |
| | stringstream ss; |
| | ss << "DCSBpieceCentered" << iPiece << ".brep"; |
| | BRepTools::Write(pieceCentered, ss.str().c_str()); |
| | ss.clear(); |
| | ss.str(std::string()); |
| | } |
| |
|
| | |
| | |
| | double faceAngle = |
| | gp_Vec(getSectionCS().Direction().Reversed()).AngleWithRef(segmentNormal, rotateAxis); |
| | gp_Ax1 faceAxis(gp_Pnt(0.0, 0.0, 0.0), rotateAxis); |
| | gp_Ax3 stdCS; |
| | gp_Ax3 pieceCS; |
| | pieceCS.Rotate(faceAxis, faceAngle); |
| | gp_Trsf xPieceRotate; |
| | xPieceRotate.SetTransformation(stdCS, pieceCS); |
| | BRepBuilderAPI_Transform mkTransRotate(pieceCentered, xPieceRotate, true); |
| | TopoDS_Shape pieceRotated = mkTransRotate.Shape(); |
| |
|
| | return pieceRotated; |
| | } |
| |
|
| | TopoDS_Shape DrawComplexSection::movePieceToPaperPlane(const TopoDS_Shape& piece, double sizeMax) |
| | { |
| | |
| | |
| | gp_Vec toPaperPlane = gp::OZ().Direction().XYZ() * sizeMax * -1.0; |
| | gp_Trsf xPieceToPlane; |
| | xPieceToPlane.SetTranslation(toPaperPlane); |
| | BRepBuilderAPI_Transform mkTransDisplace(piece, xPieceToPlane, true); |
| | TopoDS_Shape pieceToPlane = mkTransDisplace.Shape(); |
| |
|
| | |
| | return pieceToPlane; |
| | } |
| |
|
| | |
| | TopoDS_Shape DrawComplexSection::distributePiece(const TopoDS_Shape& piece, |
| | double pieceSizeInDirection, |
| | double verticalDisplace, |
| | const gp_Vec& alignmentAxis, |
| | const gp_Vec& gMovementVector, |
| | double cursorPosition) |
| | { |
| | double pieceTotalDistanceToMove = cursorPosition + pieceSizeInDirection / 2; |
| | gp_Vec alignmentVector = alignmentAxis * verticalDisplace * -1; |
| | gp_Vec netDisplacementVector = gMovementVector * pieceTotalDistanceToMove + alignmentVector; |
| |
|
| | gp_Trsf xPieceDistribute; |
| | xPieceDistribute.SetTranslation(netDisplacementVector); |
| | BRepBuilderAPI_Transform mkTransDistribute(piece, xPieceDistribute, true); |
| | auto distributedPiece = mkTransDistribute.Shape(); |
| |
|
| | return distributedPiece; |
| | } |
| |
|
| |
|
| | |
| | std::vector<TopoDS_Edge> DrawComplexSection::getUniqueEdges(const TopoDS_Wire& wireIn) |
| | { |
| | std::vector<TopoDS_Edge> ret; |
| | TopTools_IndexedMapOfShape shapeMap; |
| | TopExp_Explorer Ex(wireIn, TopAbs_EDGE); |
| | while (Ex.More()) { |
| | shapeMap.Add(Ex.Current()); |
| | Ex.Next(); |
| | } |
| |
|
| | for (Standard_Integer k = 1; k <= shapeMap.Extent(); k++) { |
| | const TopoDS_Shape& shape = shapeMap(k); |
| | auto edge = TopoDS::Edge(shape); |
| | ret.push_back(edge); |
| | } |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| | |
| | std::vector<std::pair<int, Base::Vector3d>> |
| | DrawComplexSection::getSegmentViewDirections(const TopoDS_Wire& profileWire, |
| | Base::Vector3d sectionNormal) const |
| | { |
| | auto edgesAll = getUniqueEdges(profileWire); |
| | if (edgesAll.empty()) { |
| | |
| | throw Base::RuntimeError("Complex section: profile wire has no edges."); |
| | } |
| |
|
| | |
| | if (!checkSectionCS()) { |
| | |
| | |
| | Base::Console().warning("Coordinate system for ComplexSection is invalid. Check SectionNormal, Direction or XDirection.\n"); |
| | } |
| |
|
| | auto profileVector = Base::convertTo<Base::Vector3d>(makeProfileVector(profileWire)); |
| | auto parallelDot = profileVector.Dot(sectionNormal); |
| | if (DU::fpCompare(std::fabs(parallelDot), 1, EWTOLERANCE)) { |
| | Base::Console().warning("Section normal is parallel to profile vector. Results may be incorrect.\n"); |
| | } |
| |
|
| |
|
| | TopoDS_Shape profileSolidTool = cuttingToolFromProfile(profileWire, m_shapeSize); |
| | |
| | std::vector<TopoDS_Edge> relocatedEdgesAll = getUniqueEdges(profileWire); |
| |
|
| | if (debugSection()) { |
| | BRepTools::Write(profileSolidTool, "DCStoolFromProfile.brep"); |
| | } |
| |
|
| | std::vector<Base::Vector3d> profileNormals; |
| | std::vector<std::pair<int, Base::Vector3d>> normalKV; |
| | TopExp_Explorer expFaces(profileSolidTool, TopAbs_FACE); |
| | |
| | |
| | |
| | for (int iFace = 0; expFaces.More(); expFaces.Next(), iFace++) { |
| | auto shape = expFaces.Current(); |
| | auto face = TopoDS::Face(shape); |
| | auto normal = Base::convertTo<Base::Vector3d>(getFaceNormal(face)); |
| |
|
| | if (face.Orientation() == TopAbs_FORWARD) { |
| | |
| | |
| | |
| | |
| | |
| | normal *= -1; |
| | } |
| |
|
| | |
| | auto topOrBottomDot = std::fabs(normal.Dot(getReferenceAxis())); |
| | if (DU::fpCompare(topOrBottomDot, 1, EWTOLERANCE)) { |
| | continue; |
| | } |
| | if (!isFacePlanar(face)) { |
| | |
| | |
| | continue; |
| | } |
| | |
| | int iSegment{0}; |
| | for (auto& segment : edgesAll) { |
| | if (faceContainsEndpoints(segment, face)) { |
| | std::pair<int, Base::Vector3d> newEntry; |
| | newEntry.first = iSegment; |
| | newEntry.second = normal; |
| | normalKV.push_back(newEntry); |
| | break; |
| | } |
| | iSegment++; |
| | } |
| | } |
| | std::sort(normalKV.begin(), normalKV.end(), DrawComplexSection::normalLess); |
| |
|
| | return normalKV; |
| | } |
| |
|
| | |
| | bool DrawComplexSection::faceContainsEndpoints(const TopoDS_Edge& edgeToMatch, const TopoDS_Face& faceToSearch) |
| | { |
| | std::pair<Base::Vector3d, Base::Vector3d> edgeEnds = getSegmentEnds(edgeToMatch); |
| | bool matchedFirst = pointOnFace(edgeEnds.first, faceToSearch); |
| | bool matchedLast = pointOnFace(edgeEnds.second, faceToSearch); |
| | return matchedFirst && matchedLast; |
| | } |
| |
|
| |
|
| | |
| | TopoDS_Shape DrawComplexSection::profileToSolid(const TopoDS_Wire& closedProfileWire, |
| | double dMax) const |
| | { |
| | BRepBuilderAPI_MakeFace mkFace(closedProfileWire); |
| | if (!mkFace.IsDone()) { |
| | throw Base::RuntimeError("Complex section could not create face from closed profile"); |
| | } |
| |
|
| | auto extrudeVector = getReferenceAxis() * dMax * 2; |
| |
|
| | BRepPrimAPI_MakePrism mkPrism(mkFace.Face(), Base::convertTo<gp_Vec>(extrudeVector)); |
| | auto profileSolid = mkPrism.Shape(); |
| |
|
| | return profileSolid; |
| | } |
| |
|
| |
|
| | |
| | TopoDS_Edge DrawComplexSection::mapEdgeToBase(const TopoDS_Edge& inEdge) |
| | { |
| | App::DocumentObject* baseObj = BaseView.getValue(); |
| | auto* baseDvp = freecad_cast<DrawViewPart*>(baseObj); |
| |
|
| | BRepBuilderAPI_Copy BuilderEdgeCopy(inEdge); |
| | TopoDS_Edge edgeCopy = TopoDS::Edge(BuilderEdgeCopy.Shape()); |
| |
|
| | gp_Ax3 stdCS; |
| | gp_Trsf xmapEdgeToBase; |
| | xmapEdgeToBase.SetTransformation(stdCS, baseDvp->getProjectionCS()); |
| | BRepBuilderAPI_Transform mkMappedEdge(edgeCopy, xmapEdgeToBase); |
| | TopoDS_Edge mappedEdge = TopoDS::Edge(mkMappedEdge.Shape()); |
| | return mappedEdge; |
| | } |
| |
|
| | TopoDS_Edge DrawComplexSection::mapEdgeToBase(const Base::Vector3d& inVector) |
| | { |
| | if (inVector.Length() == 0) { |
| | throw Base::RuntimeError("Complex section received a request to map a null edge"); |
| | } |
| | gp_Pnt origin(0,0,0); |
| | gp_Pnt endPoint{Base::convertTo<gp_Pnt>(inVector)}; |
| | TopoDS_Edge edgeToMap = BRepBuilderAPI_MakeEdge(origin, endPoint); |
| | return mapEdgeToBase(edgeToMap); |
| | } |
| |
|
| |
|
| | |
| | std::pair< Base::Vector3d, Base::Vector3d> DrawComplexSection::sketchNormalAndX(App::DocumentObject* sketchObj) |
| | { |
| | auto sketch = dynamic_cast<Part::Feature*>(sketchObj); |
| | if (!sketch || |
| | !sketchObj->isDerivedFrom(Base::Type::fromName("Sketcher::SketchObject"))) { |
| | |
| | return { Base::Vector3d(0,0,0), Base::Vector3d(0,0,0) }; |
| | } |
| |
|
| | auto plm = sketch->Placement.getValue(); |
| | Base::Rotation rot = plm.getRotation(); |
| |
|
| | Base::Vector3d stdZ {0.0, 0.0, 1.0}; |
| | Base::Vector3d sketchNormal; |
| | rot.multVec(stdZ, sketchNormal); |
| |
|
| | Base::Vector3d stdX {1.0, 0.0, 0.0}; |
| | Base::Vector3d sketchX; |
| | rot.multVec(stdX, sketchX); |
| |
|
| | return {sketchNormal, sketchX}; |
| | } |
| |
|
| | |
| | bool DrawComplexSection::validateSketchNormal(App::DocumentObject* sketchObject) const |
| | { |
| | if (!sketchObject || |
| | !sketchObject->isDerivedFrom(Base::Type::fromName("Sketcher::SketchObject"))) { |
| | return false; |
| | } |
| |
|
| | std::pair<Base::Vector3d, Base::Vector3d> normalX = sketchNormalAndX(sketchObject); |
| | auto* baseDvp = freecad_cast<DrawViewPart*>(BaseView.getValue()); |
| | double dot = std::fabs((normalX.first).Dot(baseDvp->Direction.getValue())); |
| | return DU::fpCompare(dot, 1, EWTOLERANCE); |
| | } |
| |
|
| |
|
| | |
| | int DrawComplexSection::getSegmentIndex(const TopoDS_Face& face, |
| | const std::vector<TopoDS_Edge>& edgesAll) |
| | { |
| | int iSegment{0}; |
| | for (auto& segment : edgesAll) { |
| | if (faceContainsEndpoints(segment, face)) { |
| | return iSegment; |
| | } |
| | iSegment++; |
| | } |
| | return -1; |
| | } |
| |
|
| |
|
| | |
| | std::pair<int, Base::Vector3d> |
| | DrawComplexSection::findNormalForFace(const TopoDS_Face& face, |
| | const std::vector<std::pair<int, Base::Vector3d>>& normalKV, |
| | const std::vector<TopoDS_Edge>& segmentEdges) |
| | { |
| | size_t index = getSegmentIndex(face, segmentEdges); |
| | if (index < 0 || |
| | index >= segmentEdges.size()) { |
| | throw Base::RuntimeError("DCS::findNormalForFace - did not find normal for face!"); |
| | } |
| | for (auto& keyValue : normalKV) { |
| | if (static_cast<size_t>(keyValue.first) == index) { |
| | return keyValue; |
| | } |
| | } |
| | throw Base::RuntimeError("DCS::findNormalForFace - no keyValue pair for segment!"); |
| | } |
| |
|
| |
|
| | bool DrawComplexSection::pointOnFace(Base::Vector3d point, const TopoDS_Face& face) |
| | { |
| | TopoDS_Vertex vert = BRepBuilderAPI_MakeVertex(Base::convertTo<gp_Pnt>(point)); |
| | double dist = DU::simpleMinDist(vert, face); |
| | return (dist < EWTOLERANCE); |
| | } |
| |
|
| |
|
| | |
| | TopoDS_Shape DrawComplexSection::makeCuttingToolFromClosedProfile(const TopoDS_Wire& profileWire, double dMax) |
| | { |
| | TopoDS_Face toolFace; |
| | try { |
| | BRepBuilderAPI_MakeFace mkFace(profileWire); |
| | toolFace = mkFace.Face(); |
| | if (toolFace.IsNull()) { |
| | return {}; |
| | } |
| | } |
| | catch (...) { |
| | Base::Console().error("%s could not make tool from closed profile\n", Label.getValue()); |
| | return {}; |
| | } |
| | gp_Dir gpNormal = getFaceNormal(toolFace); |
| | auto extrudeDir = 2 * dMax * gpNormal; |
| | TopoDS_Shape prism = BRepPrimAPI_MakePrism(toolFace, extrudeDir).Shape(); |
| | prism = ShapeUtils::moveShape(prism, Base::convertTo<Base::Vector3d>(gpNormal) * -dMax); |
| | return prism; |
| | } |
| |
|
| | bool DrawComplexSection::validateProfileAlignment(const TopoDS_Wire& profileWire) const |
| | { |
| | |
| | if (ProjectionStrategy.getValue() == 0) { |
| | |
| | |
| | |
| | |
| | constexpr double AngleThresholdDeg{5.0}; |
| | if (!validateOffsetProfile(profileWire, SectionNormal.getValue(), AngleThresholdDeg)) { |
| | Base::Console().warning("%s: profile and section normal are misaligned\n", Label.getValue()); |
| | } |
| | } |
| |
|
| | |
| | |
| | if (CuttingToolWireObject.getValue()->isDerivedFrom(Base::Type::fromName("Sketcher::SketchObject"))) { |
| | if (!validateSketchNormal(CuttingToolWireObject.getValue())) { |
| | Base::Console().error("%s: cutting object not aligned with section normal\n", Label.getValue()); |
| | return false; |
| | } |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| |
|
| | |
| | TopoDS_Shape DrawComplexSection::cuttingToolFromProfile(const TopoDS_Wire& inProfileWire, |
| | double dMax) const |
| | { |
| | TopoDS_Wire profileWireClosed = closeProfileForCut(inProfileWire, dMax); |
| |
|
| | if (debugSection()) { |
| | BRepTools::Write(profileWireClosed, "DCSprofileWireClosed.brep"); |
| | } |
| |
|
| | TopoDS_Shape solid = profileToSolid(profileWireClosed, dMax); |
| | solid = ShapeUtils::moveShape(solid, getReferenceAxis() * -dMax); |
| | return solid; |
| | } |
| |
|
| | TopoDS_Wire DrawComplexSection::closeProfileForCut(const TopoDS_Wire& profileWire, |
| | double dMax) const |
| | { |
| | |
| | |
| |
|
| | auto* baseDvp = freecad_cast<DrawViewPart*>(BaseView.getValue()); |
| | TopoDS_Shape flatShape = GeometryObject::simpleProjection(profileWire, baseDvp->getProjectionCS()); |
| | TopoDS_Wire flatWire = makeNoseToTailWire(flatShape); |
| | if (debugSection()) { |
| | BRepTools::Write(flatWire, "DCSflatCloseWire.brep"); |
| | } |
| |
|
| | std::pair<Base::Vector3d, Base::Vector3d> pvEnds = getWireEnds(flatWire); |
| | Base::Vector3d firstPWPoint = pvEnds.first; |
| | Base::Vector3d lastPWPoint = pvEnds.second; |
| |
|
| | Base::Vector3d midPWPoint = (firstPWPoint + lastPWPoint) / 2; |
| | Base::Vector3d SNPoint = SectionNormal.getValue() * dMax; |
| | Base::Vector3d awayDirection = SNPoint - midPWPoint; |
| | awayDirection.Normalize(); |
| |
|
| | std::vector<TopoDS_Edge> profileEdges = DU::shapeToVector(flatWire); |
| | TopoDS_Edge firstEdge = profileEdges.front(); |
| | std::pair<Base::Vector3d, Base::Vector3d> edgeEnds = getSegmentEnds(firstEdge); |
| | Base::Vector3d firstExtendDir = edgeEnds.first - edgeEnds.second; |
| | firstExtendDir.Normalize(); |
| | Base::Vector3d firstExtendStartPoint = edgeEnds.second; |
| | double firstInternalDistance = (midPWPoint - firstExtendStartPoint).Length(); |
| | Base::Vector3d firstExtendEndPoint = firstExtendStartPoint + firstExtendDir * (dMax - firstInternalDistance); |
| | TopoDS_Edge firstReplacementEdge = BRepBuilderAPI_MakeEdge(Base::convertTo<gp_Pnt>(firstExtendStartPoint), |
| | Base::convertTo<gp_Pnt>(firstExtendEndPoint)); |
| |
|
| | TopoDS_Edge lastEdge = profileEdges.back(); |
| | edgeEnds = getSegmentEnds(lastEdge); |
| | Base::Vector3d lastExtendDir = edgeEnds.second - edgeEnds.first; |
| | lastExtendDir.Normalize(); |
| | Base::Vector3d lastExtendStartPoint = edgeEnds.first; |
| | double lastInternalDistance = (midPWPoint - lastExtendStartPoint).Length(); |
| | Base::Vector3d lastExtendEndPoint = lastExtendStartPoint + lastExtendDir * (dMax - lastInternalDistance); |
| | TopoDS_Edge lastReplacementEdge = BRepBuilderAPI_MakeEdge(Base::convertTo<gp_Pnt>(lastExtendStartPoint), |
| | Base::convertTo<gp_Pnt>(lastExtendEndPoint)); |
| |
|
| | Base::Vector3d pointOnArc = midPWPoint + awayDirection * dMax; |
| |
|
| | Handle(Geom_TrimmedCurve) circleArc; |
| | try { |
| | GC_MakeArcOfCircle mkArc(Base::convertTo<gp_Pnt>(lastExtendEndPoint), |
| | Base::convertTo<gp_Pnt>(pointOnArc), |
| | Base::convertTo<gp_Pnt>(firstExtendEndPoint)); |
| | circleArc = mkArc.Value(); |
| | if (!mkArc.IsDone()) { |
| | throw Base::RuntimeError("Complex section failed to create arc"); |
| | } |
| | } |
| | catch (...) { |
| | throw Base::RuntimeError("Complex section failed to create circular arc to close profile"); |
| | } |
| |
|
| | TopoDS_Edge circleEdge = BRepBuilderAPI_MakeEdge(circleArc); |
| |
|
| | |
| | std::vector<TopoDS_Edge> oldProfileEdges = DU::shapeToVector(flatWire); |
| | std::vector<TopoDS_Edge> newProfileEdges; |
| | newProfileEdges.emplace_back(firstReplacementEdge); |
| | if (oldProfileEdges.size() > 2) { |
| | newProfileEdges.insert(newProfileEdges.end(), oldProfileEdges.begin()+1, oldProfileEdges.end()-1); |
| | } |
| | newProfileEdges.emplace_back(lastReplacementEdge); |
| | newProfileEdges.emplace_back(circleEdge); |
| | BRepBuilderAPI_MakeWire mkWire; |
| | for (auto& edge : newProfileEdges) { |
| | mkWire.Add(edge); |
| | } |
| |
|
| | return mkWire.Wire(); |
| | } |
| |
|
| |
|
| | bool DrawComplexSection::isFacePlanar(const TopoDS_Face& face) |
| | { |
| | BRepAdaptor_Surface adaptSurface(face); |
| | const GeomAdaptor_Surface& surf = adaptSurface.Surface(); |
| | Handle(Geom_Surface) hsurf = surf.Surface(); |
| | return GeomLib_IsPlanarSurface(hsurf).IsPlanar(); |
| | } |
| |
|
| |
|
| | |
| | bool DrawComplexSection::normalLess(const std::pair<int, Base::Vector3d>& n1, const std::pair<int, Base::Vector3d>& n2) |
| | { |
| | return n1.first < n2.first; |
| | } |
| |
|
| |
|
| | |
| |
|
| | namespace App |
| | { |
| | |
| | PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawComplexSectionPython, TechDraw::DrawComplexSection) |
| | template<> const char* TechDraw::DrawComplexSectionPython::getViewProviderName() const |
| | { |
| | return "TechDrawGui::ViewProviderDrawingView"; |
| | } |
| | |
| |
|
| | |
| | template class TechDrawExport FeaturePythonT<TechDraw::DrawComplexSection>; |
| | } |
| |
|