diff --git a/src/Gui/MDIView.cpp b/src/Gui/MDIView.cpp
index b803b3287f..27f312237f 100644
--- a/src/Gui/MDIView.cpp
+++ b/src/Gui/MDIView.cpp
@@ -147,11 +147,13 @@ void MDIView::onRelabel(Gui::Document *pDoc)
// Either with dirty flag ...
QRegularExpression rx(QLatin1String("(\\s\\:\\s\\d+\\[\\*\\])$"));
QRegularExpressionMatch match;
- int pos = cap.lastIndexOf(rx, -1, &match);
+ //int pos =
+ cap.lastIndexOf(rx, -1, &match);
if (!match.hasMatch()) {
// ... or not
rx.setPattern(QLatin1String("(\\s\\:\\s\\d+)$"));
- pos = cap.lastIndexOf(rx, -1, &match);
+ //pos =
+ cap.lastIndexOf(rx, -1, &match);
}
if (match.hasMatch()) {
cap = QString::fromUtf8(pDoc->getDocument()->Label.getValue());
diff --git a/src/Mod/TechDraw/App/AppTechDraw.cpp b/src/Mod/TechDraw/App/AppTechDraw.cpp
index 6e216ef70a..ada92519c6 100644
--- a/src/Mod/TechDraw/App/AppTechDraw.cpp
+++ b/src/Mod/TechDraw/App/AppTechDraw.cpp
@@ -24,8 +24,9 @@
#include
#include
-#include "Cosmetic.h"
#include "CosmeticExtension.h"
+#include "Cosmetic.h"
+#include "DrawComplexSection.h"
#include "DrawGeomHatch.h"
#include "DrawHatch.h"
#include "DrawLeaderLine.h"
@@ -37,7 +38,6 @@
#include "DrawSVGTemplate.h"
#include "DrawTile.h"
#include "DrawTileWeld.h"
-#include "DrawView.h"
#include "DrawViewAnnotation.h"
#include "DrawViewArch.h"
#include "DrawViewBalloon.h"
@@ -47,8 +47,8 @@
#include "DrawViewDimension.h"
#include "DrawViewDimExtent.h"
#include "DrawViewDraft.h"
+#include "DrawView.h"
#include "DrawViewImage.h"
-#include "DrawViewMulti.h"
#include "DrawViewPart.h"
#include "DrawViewSection.h"
#include "DrawViewSpreadsheet.h"
@@ -62,6 +62,7 @@
#include "PropertyGeomFormatList.h"
+
namespace TechDraw {
extern PyObject* initModule();
}
@@ -90,7 +91,7 @@ PyMOD_INIT_FUNC(TechDraw)
TechDraw::DrawViewSpreadsheet ::init();
TechDraw::DrawViewSection ::init();
- TechDraw::DrawViewMulti ::init();
+ TechDraw::DrawComplexSection ::init();
TechDraw::DrawViewDimension ::init();
TechDraw::DrawViewDimExtent ::init();
TechDraw::LandmarkDimension ::init();
@@ -134,7 +135,8 @@ PyMOD_INIT_FUNC(TechDraw)
TechDraw::DrawPagePython ::init();
TechDraw::DrawViewPython ::init();
TechDraw::DrawViewPartPython ::init();
- TechDraw::DrawViewMultiPython ::init();
+ TechDraw::DrawViewSectionPython::init();
+ TechDraw::DrawComplexSectionPython ::init();
TechDraw::DrawTemplatePython ::init();
TechDraw::DrawViewSymbolPython::init();
TechDraw::DrawLeaderLinePython::init();
diff --git a/src/Mod/TechDraw/App/CMakeLists.txt b/src/Mod/TechDraw/App/CMakeLists.txt
index adc4cd507a..8b54b0e165 100644
--- a/src/Mod/TechDraw/App/CMakeLists.txt
+++ b/src/Mod/TechDraw/App/CMakeLists.txt
@@ -75,6 +75,8 @@ generate_from_xml(CosmeticExtensionPy)
SET(Draw_SRCS
DrawPage.cpp
DrawPage.h
+ DrawComplexSection.cpp
+ DrawComplexSection.h
DrawView.cpp
DrawView.h
DrawViewPart.cpp
diff --git a/src/Mod/TechDraw/App/DrawComplexSection.cpp b/src/Mod/TechDraw/App/DrawComplexSection.cpp
new file mode 100644
index 0000000000..a2559dd2b1
--- /dev/null
+++ b/src/Mod/TechDraw/App/DrawComplexSection.cpp
@@ -0,0 +1,1205 @@
+/***************************************************************************
+ * Copyright (c) 2022 WandererFan *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+//DrawComplexSection processing overview
+//for Strategy = Single, DCS is much the same as DVS
+//for Strategy = PieceWise, there are many differences
+
+//execute
+// sectionExec(getShapeToCut()*)
+
+//sectionExec
+// makeSectionCut(baseShape)
+
+//makeSectionCut (separate thread)
+// note that the section cut is not required for Piecewise strategy,
+// but it is useful for debugging
+// m_cuttingTool = makeCuttingTool* (DVSTool.brep)
+// m_cutPieces = (baseShape - m_cuttingTool) (DVSCutPieces.brep)
+
+//onSectionCutFinished
+// m_preparedShape = prepareShape(m_cutPieces)* - centered, scaled, rotated
+// geometryObject = DVP::buildGeometryObject(m_preparedShape) (HLR)
+
+//postHlrTasks
+// faceIntersections = findSectionPlaneIntersections
+// m_sectionTopoDSFaces = alignSectionFaces(faceIntersections)
+// m_tdSectionFaces = makeTDSectionFaces(m_sectionTopoDSFaces)
+
+//* for Piecewise, we use a different ShapeToCut, as the standard one will
+// cause many coincident face problems later
+//* the cutting tool is built up from the profile, instead of the simple plane in DVS
+//* for Piecewise, preparing the shape is much different than Single or DVS
+// - most of the work is done in makeAlignedPieces
+// - for each segment of the profile, make a cutting tool, then get the boolean
+// intersection of the tool and the shape to cut
+// - align and distribute the intersections along an "effective" section plane
+// which is a flattened version of the profile
+
+#include "PreCompiled.h"
+
+#ifndef _PreComp_
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#define _USE_MATH_DEFINES // for C++
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "DrawUtil.h"
+#include "GeometryObject.h"
+#include "DrawComplexSection.h"
+
+using namespace TechDraw;
+using namespace std;
+using DU = DrawUtil;
+
+//class to store geometry of points where the section line changes direction
+ChangePoint::ChangePoint(QPointF location, QPointF preDirection, QPointF postDirection)
+{
+ m_location = location;
+ m_preDirection = preDirection;
+ m_postDirection = postDirection;
+}
+
+ChangePoint::ChangePoint(gp_Pnt location, gp_Dir preDirection, gp_Dir postDirection)
+{
+ m_location.setX(location.X());
+ m_location.setY(location.Y());
+ m_preDirection.setX(preDirection.X());
+ m_preDirection.setY(preDirection.Y());
+ m_postDirection.setX(postDirection.X());
+ m_postDirection.setY(postDirection.Y());
+}
+
+void ChangePoint::scale(double scaleFactor)
+{
+ m_location = m_location * scaleFactor;
+ m_preDirection = m_preDirection * scaleFactor;
+ m_postDirection = m_postDirection * scaleFactor;
+}
+
+//===========================================================================
+// DrawComplexSection
+//===========================================================================
+
+PROPERTY_SOURCE(TechDraw::DrawComplexSection, TechDraw::DrawViewSection)
+
+const char* DrawComplexSection::ProjectionStrategyEnums[] = { "Single",
+ "Piecewise",
+ "NoParallel",
+ nullptr };
+
+DrawComplexSection::DrawComplexSection() :
+ m_toolFaceShape(TopoDS_Shape()),
+ m_profileWire(TopoDS_Wire())
+{
+ 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::getShapeToCut()
+{
+// Base::Console().Message("DCS::getShapeToCut()\n");
+ App::DocumentObject* base = BaseView.getValue();
+ TopoDS_Shape shapeToCut;
+ if (base && base == this) {
+ shapeToCut = getSourceShape();
+ if (FuseBeforeCut.getValue()) {
+ shapeToCut = getSourceShapeFused();
+ }
+ return shapeToCut;
+ }
+ if (!base ||
+ !base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { //is second clause necessary?
+ //Complex section is based on 3d objects, need to get our own shapes since we can't ask a dvp
+ shapeToCut = getSourceShape();
+ if (FuseBeforeCut.getValue()) {
+ shapeToCut = getSourceShapeFused();
+ }
+ return shapeToCut;
+ }
+ //complex section is based on a DVP, so get the shape the normal way
+ return DrawViewSection::getShapeToCut();
+}
+
+void DrawComplexSection::makeSectionCut(TopoDS_Shape &baseShape)
+{
+// Base::Console().Message("DCS::makeSectionCut() - %s\n", getNameInDocument(), baseShape.IsNull());
+ if (ProjectionStrategy.getValue() == 0) {
+ //Single. Use regular section behaviour
+ DrawViewSection::makeSectionCut(baseShape);
+ return;
+ }
+ //Piecewise strategy
+ if (debugSection()) {
+ //only useful for debugging with Piecewise strategy
+ DrawViewSection::makeSectionCut(baseShape);
+ }
+ return;
+}
+
+TopoDS_Shape DrawComplexSection::makeCuttingTool(double dMax)
+{
+// Base::Console().Message("DCS::makeCuttingTool()\n");
+ App::DocumentObject* toolObj = CuttingToolWireObject.getValue();
+ if (!isProfileObject(toolObj)) {
+ return TopoDS_Shape();
+ }
+ TopoDS_Wire profileWire = makeProfileWire(toolObj);
+ m_profileWire = profileWire;
+ BRepBuilderAPI_Copy BuilderCopy(profileWire);
+ m_profileWire = TopoDS::Wire(BuilderCopy.Shape());
+ if (debugSection()){
+ //the nose to tail version of the profile
+ BRepTools::Write(m_profileWire, "DCSProfileWire.brep"); //debug
+ }
+
+ gp_Ax2 sectionCS = getSectionCS();
+ gp_Dir gClosestBasis; //direction perpendicular to profile & section normal
+ bool isPositionOK = validateProfilePosition(profileWire,
+ sectionCS,
+ gClosestBasis);
+ if (!isPositionOK) {
+ //profile is not in a good position. Result might not be right.
+ Base::Console().Warning("DCS::makeCuttingTool - %s - profile is outside shape box\n",
+ getNameInDocument());
+ }
+
+ //move the profile wire to one side of the shape
+ gp_Trsf mov;
+ mov.SetTranslation(gp_Vec(gClosestBasis) * (-dMax));
+ TopLoc_Location loc(mov);
+ profileWire.Move(loc);
+
+ gp_Vec extrudeDir(0.0, 0.0, 1.0); //arbitrary default
+ if (BRep_Tool::IsClosed(profileWire)) {
+ // Wire is closed, so make a face from it and extrude "vertically"
+ BRepBuilderAPI_MakeFace mkFace(profileWire);
+ TopoDS_Face toolFace = mkFace.Face();
+ if(toolFace.IsNull()) {
+ return TopoDS_Shape();
+ }
+ gp_Dir gpNormal = getFaceNormal(toolFace);
+ extrudeDir = 2.0 * dMax * gpNormal;
+ return BRepPrimAPI_MakePrism(toolFace, extrudeDir).Shape();
+ }
+
+ //if the wire is open we need to make a "face" from the wire by extruding it
+ //in the direction of gClosestBasis , then extrude the face in the reverse of the section normal
+ m_toolFaceShape = extrudeWireToFace(profileWire, gClosestBasis, 2.0 * dMax);
+ if (debugSection()){
+ BRepTools::Write(m_toolFaceShape, "DCSToolFaceShape.brep"); //debug
+ }
+ extrudeDir = dMax * sectionCS.Direction();
+ return BRepPrimAPI_MakePrism(m_toolFaceShape, extrudeDir).Shape();
+}
+
+TopoDS_Shape DrawComplexSection::getShapeToPrepare() const
+{
+ if (ProjectionStrategy.getValue() == 0) {
+ //Single. Use regular section behaviour
+ return DrawViewSection::getShapeToPrepare();
+ }
+ //Piecewise strategy
+ return m_saveShape; //the original input shape
+}
+
+//get the shape ready for projection and cut surface finding
+TopoDS_Shape DrawComplexSection::prepareShape(const TopoDS_Shape &cutShape,
+ double shapeSize)
+{
+// Base::Console().Message("DCS::prepareShape() - strat: %d\n", ProjectionStrategy.getValue());
+ if (ProjectionStrategy.getValue() == 0) {
+ //Single. Use regular section behaviour
+ return DrawViewSection::prepareShape(cutShape, shapeSize);
+ }
+
+ //"Piecewise" projection (Aligned Section)
+ TopoDS_Shape alignedResult = makeAlignedPieces(cutShape,
+ m_toolFaceShape,
+ shapeSize);
+
+ if (alignedResult.IsNull()) {
+ return TopoDS_Shape();
+ }
+
+ if (debugSection()){
+ BRepTools::Write(alignedResult, "DCSPrepareAlignedResult.brep"); //debug
+ }
+
+ TopoDS_Shape scaledShape = scaleShape(alignedResult, getScale());
+ if (debugSection()){
+ BRepTools::Write(scaledShape, "DCSPrepareScaled.brep"); //debug
+ }
+
+ return scaledShape;
+}
+
+//for Piecewise strategy, cut the rawShape by each segment of the tool
+//TODO: this process should replace the "makeSectionCut" from DVS
+TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape,
+ const TopoDS_Shape &toolFaceShape,
+ double extrudeDistance)
+{
+// Base::Console().Message("DCS::makeAlignedPieces()\n");
+ std::vector pieces;
+ std::vector pieceXSize; //size in sectionCS.XDirection (width)
+ std::vector pieceYSize; //size in sectionCS.Direction (depth)
+ std::vector pieceZSize; //size in sectionCS.YDirection (height)
+ std::vector pieceDirections;
+ gp_Ax3 alignedCS(gp_Pnt(0.0, 0.0, 0.0),
+ getSectionCS().YDirection(), //up and down
+ getSectionCS().XDirection()); //left to right
+ gp_Ax3 stdCS; //OXYZ
+ gp_Vec gProjectionUnit = gp_Vec(getSectionCS().Direction());
+
+ //get a vector that describes the profile's orientation in paper space.
+ gp_Vec gProfileVec = projectProfileWire(m_profileWire, gp_Ax3(getSectionCS()));
+ if (fabs(gProfileVec.Dot(getProjectionCS().Direction()) == 1.0)) {
+ Base::Console().Error("DCS::makeAlignedPieces - %s - profile is parallel to SectionNormal\n", getNameInDocument());
+ throw Base::RuntimeError("Profile orientation error");
+ }
+
+ //convert the profileVector with OXYZ.
+ gp_Trsf xProfileOXYZ;
+ gp_Ax3 OXYZ;
+ xProfileOXYZ.SetTransformation(OXYZ, gp_Ax3(getSectionCS()));
+ gp_Vec profileVecOXYZ = gProfileVec.Transformed(xProfileOXYZ);
+
+ bool isVertical = true;
+ if (fabs(profileVecOXYZ.Dot(gp::OY().Direction().XYZ())) != 1.0) {
+ //profile is not parallel with stdY (paper space Up).
+ //this test is not good enough for "vertical-ish" diagonal profiles
+ isVertical = false;
+ }
+
+ double leftToRight = 1.0; //profile vector points to right, so we move to right
+ if (profileVecOXYZ.Dot(gp_Vec(gp::OX().Direction().XYZ())) < 0.0) {
+ //profileVec does not point towards stdX (right in paper space)
+ leftToRight = -1.0;
+ }
+ double topToBottom = 1.0; //profile vector points to top, so we move to top
+ if (profileVecOXYZ.Dot(gp_Vec(gp::OY().Direction().XYZ())) < 0.0) {
+ //profileVec does not point towards stdY (up in paper space)
+ topToBottom = -1.0;
+ }
+
+ gp_Vec rotateAxis = getSectionCS().Direction().Crossed(gProfileVec);
+
+
+ //make a tool for each segment of the toolFaceShape and intersect it with the
+ //raw shape
+ TopExp_Explorer expFaces(toolFaceShape, TopAbs_FACE);
+ for (int iPiece = 0; expFaces.More(); expFaces.Next(), iPiece++) {
+ TopoDS_Face face = TopoDS::Face(expFaces.Current());
+ gp_Vec segmentNormal = gp_Vec(getFaceNormal(face));
+ if (!showSegment(gp_Dir(segmentNormal))) {
+ //skip this segment of the profile
+ continue;
+ }
+ //we only want to reverse the segment normal if it is not perpendicular to section normal
+ if (segmentNormal.Dot(-gProjectionUnit) != 0.0 &&
+ segmentNormal.Angle(gProjectionUnit) <= M_PI_2) {
+ segmentNormal.Reverse();
+ }
+
+ gp_Vec extrudeDir = segmentNormal * extrudeDistance;
+ BRepPrimAPI_MakePrism mkPrism(face, extrudeDir);
+ TopoDS_Shape segmentTool = mkPrism.Shape();
+ TopoDS_Shape intersect = shapeShapeIntersect(segmentTool, rawShape);
+ if (debugSection()) {
+ std::stringstream ss;
+ ss << "DCS1SegmentTool" << iPiece << ".brep";
+ BRepTools::Write(segmentTool, ss.str().c_str()); //debug
+ ss.str(std::string());
+ ss.clear();
+ ss << "DCS1IntersectPiece" << iPiece << ".brep";
+ BRepTools::Write(intersect, ss.str().c_str()); //debug
+ }
+
+ //experiment
+ gp_Pnt pieceCentroid = findCentroid(intersect);
+ double faceAngle = gp_Vec(getSectionCS().Direction().Reversed()).AngleWithRef(segmentNormal, rotateAxis);
+
+ //move intersection shape to the origin
+ gp_Trsf xPieceCenter;
+ xPieceCenter.SetTranslation(gp_Vec(findCentroid(intersect).XYZ()) * -1.0);
+ BRepBuilderAPI_Transform mkTransXLate(intersect, xPieceCenter, true);
+ TopoDS_Shape pieceCentered = mkTransXLate.Shape();
+ if (debugSection()) {
+ std::stringstream ss;
+ ss << "DCS2Centered" << iPiece << ".brep";
+ BRepTools::Write(pieceCentered, ss.str().c_str()); //debug
+ }
+
+ //rotate the intersection so interesting face is aligned with paper plane
+ pieceCentroid = findCentroid(pieceCentered);
+ gp_Ax1 faceAxis(gp_Pnt(0.0, 0.0, 0.0), rotateAxis);
+ gp_Ax3 pieceCS; //XYZ tipped so face is aligned with sectionCS
+ pieceCS.Rotate(faceAxis, faceAngle);
+ gp_Trsf xPieceRotate;
+ xPieceRotate.SetTransformation(stdCS, pieceCS);
+ BRepBuilderAPI_Transform mkTransRotate(pieceCentered, xPieceRotate, true);
+ TopoDS_Shape pieceRotated = mkTransRotate.Shape();
+ if (debugSection()) {
+ std::stringstream ss;
+ ss << "DCS3Rotated" << iPiece << ".brep";
+ BRepTools::Write(pieceRotated, ss.str().c_str()); //debug
+ }
+
+ //align a copy of the piece with OXYZ so we can use bounding box to get
+ //width, depth, height of the piece. We copy the piece so the tranformation
+ //does not affect the original.
+ BRepBuilderAPI_Copy BuilderPieceCopy(pieceRotated);
+ TopoDS_Shape copyPieceRotatedShape = BuilderPieceCopy.Shape();
+ gp_Trsf xPieceAlign;
+ xPieceAlign.SetTransformation(stdCS, alignedCS);
+ BRepBuilderAPI_Transform mkTransAlign(copyPieceRotatedShape, xPieceAlign);
+ TopoDS_Shape pieceAligned = mkTransAlign.Shape();
+ if (debugSection()) {
+ std::stringstream ss;
+ ss << "DCS4Aligned" << iPiece << ".brep";
+ BRepTools::Write(pieceAligned, ss.str().c_str()); //debug
+ }
+ 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 pieceWidth(xMax - xMin);
+ double pieceDepth(yMax - yMin);
+ double pieceHeight(zMax - zMin);
+ pieceXSize.push_back(pieceWidth);
+ pieceYSize.push_back(pieceDepth);
+ pieceZSize.push_back(pieceHeight);
+
+ //now we need to move the piece so that the interesting face is coincident
+ //with the paper plane
+ //TODO: need direction vectors here???
+ gp_Vec depthVector(gp::OY().Direction().XYZ() * pieceDepth / 2.0); //move "back"
+ //only aligned to paper plane
+ gp_Vec netDisplacement = -1.0 * gp_Vec(findCentroid(pieceAligned).XYZ()) +
+ depthVector;
+ //if we are going to space along X, we need to bring the pieces into alignment
+ //with the XY plane.
+ gp_Vec xyDisplacement = isVertical ? gp_Vec(0.0, 0.0, 0.0) : gp_Vec(gp::OZ().Direction());
+ xyDisplacement = xyDisplacement * topToBottom * pieceHeight / 2.0;
+ netDisplacement = netDisplacement + xyDisplacement;
+
+ gp_Trsf xPieceDisplace;
+ xPieceDisplace.SetTranslation(netDisplacement);
+ BRepBuilderAPI_Transform mkTransDisplace(pieceAligned, xPieceDisplace, true);
+ TopoDS_Shape pieceDisplaced = mkTransDisplace.Shape();
+ if (debugSection()) {
+ std::stringstream ss;
+ ss << "DCS5CorrectInStdY" << iPiece << ".brep";
+ BRepTools::Write(pieceDisplaced, ss.str().c_str()); //debug
+ }
+ //piece is now centered on X, aligned with XZ plane (which will be the effective
+ //cutting plane)
+ pieces.push_back(pieceDisplaced);
+ }
+
+ if (pieces.empty()) {
+ return TopoDS_Compound();
+ }
+
+ int pieceCount = pieces.size();
+ if (pieceCount < 2) {
+ //no need to space out the pieces
+ return TopoDS::Compound(pieces.front());
+ }
+
+ //space the pieces "horizontally" (stdX) or "vertically" (stdZ)
+ double movementReverser = isVertical ? topToBottom : leftToRight;
+ //TODO: non-cardinal profiles!
+ gp_Vec movementAxis = isVertical ? gp_Vec(gp::OZ().Direction()) : gp_Vec(gp::OX().Direction());
+ gp_Vec gMovementVector = movementAxis * movementReverser;
+
+ int stopAt = pieces.size();
+ double distanceToMove = 0.0;
+ for (int iPiece = 0; iPiece < stopAt; iPiece++) {
+ double pieceSize = pieceXSize.at(iPiece);
+ if (isVertical) {
+ pieceSize = pieceZSize.at(iPiece);
+ }
+ double myDistanceToMove = distanceToMove + pieceSize / 2.0;
+ gp_Trsf xPieceDistribute;
+ xPieceDistribute.SetTranslation(gMovementVector * myDistanceToMove);
+ BRepBuilderAPI_Transform mkTransDistribute(pieces.at(iPiece), xPieceDistribute, true);
+ pieces.at(iPiece) = mkTransDistribute.Shape();
+ distanceToMove += pieceSize;
+ if (debugSection()) {
+ std::stringstream ss;
+ ss << "DCSfinDistributedPiece" << iPiece << ".brep";
+ BRepTools::Write(pieces.at(iPiece), ss.str().c_str()); //debug
+ }
+ }
+
+ //make a compound of the aligned pieces
+ BRep_Builder builder;
+ TopoDS_Compound comp;
+ builder.MakeCompound(comp);
+ for (auto& piece : pieces) {
+ builder.Add(comp, piece);
+ }
+
+ //center the compound along SectionCS XDirection
+ Base::Vector3d centerVector = DU::toVector3d(gMovementVector) * distanceToMove / -2.0;
+ TopoDS_Shape centeredCompound = moveShape(comp,
+ centerVector);
+ if (debugSection()) {
+ BRepTools::Write(centeredCompound, "DCSfinCenteredCompound.brep"); //debug
+ }
+ //realign with SectionCS
+ gp_Trsf xPieceAlign;
+ xPieceAlign.SetTransformation(alignedCS, stdCS);
+ BRepBuilderAPI_Transform mkTransAlign(centeredCompound, xPieceAlign);
+ TopoDS_Shape alignedCompound = mkTransAlign.Shape();
+ if (debugSection()) {
+ BRepTools::Write(alignedCompound, "DCSfinAlignedCompound.brep"); //debug
+ }
+
+ return alignedCompound;
+}
+
+//! tries to find the intersection faces of the cut shape and the cutting tool.
+//! Piecewise complex sections need to intersect the final cut shape (which in this
+//! case is a compound of individual cuts) with the "effective" (flattened) section plane.
+//! Profiles containing curves need special handling.
+TopoDS_Compound DrawComplexSection::findSectionPlaneIntersections(const TopoDS_Shape& cutShape)
+{
+// Base::Console().Message("DCS::findSectionPlaneIntersections() - %s\n", getNameInDocument());
+ if(cutShape.IsNull()){
+ // this shouldn't happen
+ Base::Console().Warning("DCS::findSectionPlaneInter - %s - cut shape is Null\n", getNameInDocument());
+ return TopoDS_Compound();
+ }
+ if (ProjectionStrategy.getValue() == 0) { //Single
+ return singleToolIntersections(cutShape);
+ }
+
+ return piecewiseToolIntersections(cutShape);
+}
+
+//Intersect cutShape with each segment of the cutting tool
+TopoDS_Compound DrawComplexSection::singleToolIntersections(const TopoDS_Shape& cutShape)
+{
+// Base::Console().Message("DCS::singleToolInterSections()\n");
+ App::DocumentObject* toolObj = CuttingToolWireObject.getValue();
+ if (!isLinearProfile(toolObj)) {
+ //TODO: special handling here
+// Base::Console().Message("DCS::singleToolIntersection - profile has curves\n");
+ }
+
+ BRep_Builder builder;
+ TopoDS_Compound result;
+ builder.MakeCompound(result);
+
+ if (debugSection()) {
+ BRepTools::Write(cutShape, "DCSSingleCutShape.brep"); //debug
+ BRepTools::Write(m_toolFaceShape, "DCSSingleCuttingToolFace.brep"); //debug
+ }
+
+ 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 commonFaces = faceShapeIntersect(face, m_toolFaceShape);
+ for (auto& cFace : commonFaces) {
+ builder.Add(result, cFace);
+ }
+ }
+ return result;
+}
+
+//Intersect cutShape with the effective (flattened) cutting plane to generate cut surface faces
+TopoDS_Compound DrawComplexSection::piecewiseToolIntersections(const TopoDS_Shape& cutShape)
+{
+// Base::Console().Message("DCS::piecewiseToolIntersections()\n");
+ BRep_Builder builder;
+ TopoDS_Compound result;
+ builder.MakeCompound(result);
+
+ App::DocumentObject* toolObj = CuttingToolWireObject.getValue();
+ if (!isLinearProfile(toolObj)) {
+ //TODO: special handling here
+// Base::Console().Message("DCS::pieceWiseToolIntersection - profile has curves\n");
+ }
+
+ gp_Pln effectivePlane = getSectionPlane();
+ //piecewise result can be much wider than the shape itself, so we use an
+ //infinite face.
+ BRepBuilderAPI_MakeFace mkFace(effectivePlane, -Precision::Infinite(), Precision::Infinite(),
+ -Precision::Infinite(), Precision::Infinite());
+ TopoDS_Face cuttingFace = mkFace.Face();
+ if (debugSection()) {
+ BRepTools::Write(cuttingFace, "DCSPiecewiseCuttingFace.brep"); //debug
+ }
+ 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 commonFaces = faceShapeIntersect(face, cuttingFace);
+ for (auto& cFace : commonFaces) {
+ builder.Add(result, cFace);
+ }
+ }
+ if (debugSection()){
+ BRepTools::Write(cuttingFace, "DCSPiecewiseCuttingFace.brep"); //debug
+ BRepTools::Write(cutShape, "DCSPiecewiseCutShape.brep"); //debug
+ BRepTools::Write(result, "DCSPiecewiseIntersections.brep"); //debug
+ }
+ return result;
+}
+
+TopoDS_Compound DrawComplexSection::alignSectionFaces(TopoDS_Shape faceIntersections)
+{
+// Base::Console().Message("DCS::alignSectionFaces()\n");
+ if (ProjectionStrategy.getValue() == 0) {
+ //Single. Use regular section behaviour
+ return DrawViewSection::alignSectionFaces(faceIntersections);
+ }
+
+ return TopoDS::Compound(mapToPage(faceIntersections));
+}
+
+TopoDS_Shape DrawComplexSection::getShapeToIntersect()
+{
+ if (ProjectionStrategy.getValue() == 0) { //Single
+ return DrawViewSection::getShapeToIntersect();
+ }
+ //Piecewise
+ return m_preparedShape;
+}
+
+TopoDS_Wire DrawComplexSection::makeProfileWire(App::DocumentObject* toolObj)
+{
+ TopoDS_Shape toolShape = Part::Feature::getShape(toolObj);
+ if (toolShape.IsNull()) {
+ return TopoDS_Wire();
+ }
+
+ TopoDS_Wire profileWire;
+ if (toolShape.ShapeType() == TopAbs_WIRE) {
+ profileWire = makeNoseToTailWire(TopoDS::Wire(toolShape));
+ } else { //we have already checked that the shape is a wire or an edge in isProfileObject
+ TopoDS_Edge edge = TopoDS::Edge(toolShape);
+ profileWire = BRepBuilderAPI_MakeWire(edge).Wire();
+ }
+ return profileWire;
+}
+
+//methods related to section line
+
+//project the profile onto the paper and convert to the working CS
+gp_Dir DrawComplexSection::projectProfileWire(TopoDS_Wire profileWire, gp_Ax3 paperCS)
+{
+// Base::Console().Message("DCS::projectProfileWire()\n");
+ gp_Pln plane(paperCS);
+ TopoDS_Face paper = BRepBuilderAPI_MakeFace(plane);
+ BRepAlgo_NormalProjection projector(paper);
+ projector.Add(profileWire);
+ projector.Build();
+ TopoDS_Shape projectedShape = projector.Projection();
+ if (debugSection()){
+ BRepTools::Write(projectedShape, "DCSprojectedProfileShape.brep"); //debug
+ }
+ TopoDS_Edge projectedSegment;
+ //we only need 1 projected edge to determine direction
+ TopExp_Explorer expEdges(projectedShape, TopAbs_EDGE);
+ for ( ; expEdges.More(); expEdges.Next()) {
+ projectedSegment = TopoDS::Edge(expEdges.Current());
+ break;
+ }
+ if (debugSection()){
+ BRepTools::Write(projectedSegment, "DCSprojectedSegment.brep"); //debug
+ }
+ if (projectedSegment.IsNull()) {
+ Base::Console().Warning("DCS::projectProfileWire - projection of profile failed\n");
+ return gp_Dir(1.0, 0.0, 0.0);
+ }
+ gp_Pnt gpProfileFirst = BRep_Tool::Pnt(TopExp::FirstVertex(projectedSegment));
+ gp_Pnt gpProfileLast = BRep_Tool::Pnt(TopExp::LastVertex(projectedSegment));
+ gp_Vec gProfileVec(gpProfileFirst, gpProfileLast);
+ gProfileVec.Normalize();
+ return gp_Dir(gProfileVec);
+}
+
+//make drawable td geometry for section line
+BaseGeomPtrVector DrawComplexSection::makeSectionLineGeometry()
+{
+// Base::Console().Message("DCS::makeSectionLineGeometry()\n");
+ BaseGeomPtrVector result;
+ DrawViewPart* baseDvp = dynamic_cast(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;
+}
+
+//get the end points of the section wire
+std::pair DrawComplexSection::sectionLineEnds()
+{
+// Base::Console().Message("DCS::sectionLineEnds()\n");
+ std::pair result;
+ TopoDS_Wire lineWire = makeSectionLineWire();
+ if (lineWire.IsNull()) {
+ return result;
+ }
+
+ TopoDS_Vertex vFirst, vLast;
+ TopExp::Vertices (lineWire, vFirst, vLast);
+ gp_Pnt gpFirst = BRep_Tool::Pnt(vFirst);
+ gp_Pnt gpLast = BRep_Tool::Pnt(vLast);
+ Base::Vector3d first = Base::Vector3d(gpFirst.X(), gpFirst.Y(), gpFirst.Z());
+ Base::Vector3d last = Base::Vector3d(gpLast.X(), gpLast.Y(), gpLast.Z());
+
+ DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue());
+ if (baseDvp) {
+ first = baseDvp->projectPoint(first);
+ last = baseDvp->projectPoint(last);
+ }
+ result.first = first;
+ result.second = last;
+ return result;
+}
+
+//get directions of the section line arrows
+std::pair DrawComplexSection::sectionArrowDirs()
+{
+// Base::Console().Message("DCS::sectionArrowDirs()\n");
+ std::pair result;
+ App::DocumentObject* toolObj = CuttingToolWireObject.getValue();
+ TopoDS_Wire profileWire = makeProfileWire(toolObj);
+ if (profileWire.IsNull()) {
+ return result;
+ }
+
+ TopoDS_Vertex tvFirst, tvLast;
+ TopExp::Vertices (profileWire, tvFirst, tvLast);
+ gp_Pnt gpFirst = BRep_Tool::Pnt(tvFirst);
+ gp_Pnt gpLast = BRep_Tool::Pnt(tvLast);
+ gp_Vec gProfileVector = gp_Vec(gpLast.XYZ()) - gp_Vec(gpFirst.XYZ());
+
+ gp_Vec gSectionVector = getSectionCS().Direction().Reversed();
+ gp_Vec gExtrudeVector = gSectionVector.Crossed(gProfileVector);
+ Base::Vector3d vClosestBasis = DrawUtil::closestBasis(gp_Dir(gExtrudeVector), getSectionCS());
+ gp_Dir gExtrudeDir = gp_Dir(vClosestBasis.x, vClosestBasis.y, vClosestBasis.z);
+
+ TopoDS_Shape toolFaceShape = extrudeWireToFace(profileWire, gExtrudeDir, 100.0);
+ if (toolFaceShape.IsNull()) {
+ return result;
+ }
+
+ if (debugSection()){
+ BRepTools::Write(toolFaceShape, "DCSEndsToolFace.brep"); //debug
+ }
+
+ std::vector faces;
+ TopExp_Explorer expl(toolFaceShape, TopAbs_FACE);
+ for ( ; expl.More(); expl.Next()) {
+ faces.push_back(TopoDS::Face(expl.Current()));
+ }
+
+ gp_Vec gDir0 = gp_Vec(getFaceNormal(faces.front()));
+ gp_Vec gDir1 = gp_Vec(getFaceNormal(faces.back()));
+
+ //TODO: similar code elsewhere. Opportunity for reuse.
+ Base::Vector3d vDir0(gDir0.X(), gDir0.Y(), gDir0.Z());
+ Base::Vector3d vDir1(gDir1.X(), gDir1.Y(), gDir1.Z());
+ vDir0.Normalize();
+ vDir1.Normalize();
+ DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue());
+ if (baseDvp) {
+ vDir0 = baseDvp->projectPoint(vDir0);
+ vDir1 = baseDvp->projectPoint(vDir1);
+ }
+
+ result.first = vDir0;
+ result.second = vDir1;
+ return result;
+}
+
+//make a wire suitable for projection on a base view
+TopoDS_Wire DrawComplexSection::makeSectionLineWire()
+{
+ TopoDS_Wire lineWire;
+ App::DocumentObject* toolObj = CuttingToolWireObject.getValue();
+ DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue());
+ if (baseDvp) {
+ Base::Vector3d centroid = baseDvp->getCurrentCentroid();
+ TopoDS_Shape sTrans = TechDraw::moveShape(Part::Feature::getShape(toolObj),
+ centroid * -1.0);
+ TopoDS_Shape sScaled = TechDraw::scaleShape(sTrans,
+ baseDvp->getScale());
+ //we don't mirror the scaled shape here as it will be mirrored by the projection
+ if (debugSection()){
+ BRepTools::Write(sScaled, "DCSSectionLineWire.brep"); //debug
+ }
+ if (sScaled.ShapeType() == TopAbs_WIRE) {
+ lineWire = makeNoseToTailWire(TopoDS::Wire(sScaled));
+ } else if (sScaled.ShapeType() == TopAbs_EDGE) {
+ TopoDS_Edge edge = TopoDS::Edge(sScaled);
+ lineWire = BRepBuilderAPI_MakeWire(edge).Wire();
+ } else {
+ //probably can't happen as cut profile has been checked before this
+ Base::Console().Message("DCS::makeSectionLineGeometry - profile is type: %d\n", sScaled.ShapeType());
+ return TopoDS_Wire();
+ }
+ }
+ return lineWire;
+}
+
+//find the points where the section line changes direction, and the direction
+//of the profile before and after the point
+ChangePointVector DrawComplexSection::getChangePointsFromSectionLine()
+{
+// Base::Console().Message("DCS::getChangePointsFromSectionLine()\n");
+ ChangePointVector result;
+ std::vector allPoints;
+ DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue());
+ if (baseDvp) {
+ TopoDS_Wire lineWire = makeSectionLineWire();
+ TopoDS_Shape projectedWire = GeometryObject::projectSimpleShape(lineWire,
+ baseDvp->getProjectionCS());
+ if (projectedWire.IsNull()) {
+ return result;
+ }
+ //get UNIQUE points along the projected profile
+ 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.0 * EWTOLERANCE)) {
+ continue;
+ }
+ allPoints.push_back(gPoint);
+ previousPoint = gPoint;
+ }
+
+ //make the intermediate marks
+ 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);
+ }
+
+ //make start and end marks
+ 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
+{
+// Base::Console().Message("DCS::getCSFromBase()\n");
+ App::DocumentObject* base = BaseView.getValue();
+ if (!base ||
+ !base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { //is second clause necessary?
+ return getSectionCS();
+ }
+ return DrawViewSection::getCSFromBase(sectionName);
+}
+
+//get the "effective" (flattened) section plane for Piecewise and
+//the regular sectionPlane for Single.
+gp_Pln DrawComplexSection::getSectionPlane() const
+{
+ if (ProjectionStrategy.getValue() == 0) {
+ //Single. Use regular section behaviour
+ return DrawViewSection::getSectionPlane();
+ }
+
+ //"Piecewise" projection (Aligned Section)
+ //this is the same as DVS::getSectionPlane except that the plane origin is not the SectionOrigin
+ 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 gp_Pln(gPlaneCS);
+}
+
+bool DrawComplexSection::isBaseValid() const
+{
+ App::DocumentObject* base = BaseView.getValue();
+ if (!base) {
+ //complex section is not based on an existing DVP
+ return true;
+ }
+ if (!base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) {
+ //this is probably an error somewhere. the valid options are base = a DVP,
+ //or no base
+ return false;
+ }
+ //have a base and it is a DVP
+ return true;
+}
+
+//if the profile is not nicely positioned within the vertical span of the shape, we might not overlap
+//the shape after extrusion. As long as the profile is within the extent of the shape in the
+//extrude direction we should be ok. the extrude direction has to be perpendicular to the profile and SectionNormal
+bool DrawComplexSection::validateProfilePosition(TopoDS_Wire profileWire,
+ gp_Ax2 sectionCS,
+ gp_Dir& gClosestBasis) const
+{
+// Base::Console().Message("DCS::validateProfilePosition()\n");
+ TopoDS_Vertex tvFirst, tvLast;
+ TopExp::Vertices (profileWire, tvFirst, tvLast);
+ gp_Pnt gpFirst = BRep_Tool::Pnt(tvFirst); //a position point for the wire
+ gp_Pnt gpLast = BRep_Tool::Pnt(tvLast);
+ gp_Vec gProfileVector = gp_Vec(gpLast.XYZ()) - gp_Vec(gpFirst.XYZ());
+
+ //since bounding boxes are aligned with the cardinal directions, we need to find
+ //the appropriate direction to use when validating the profile position
+ gp_Vec gSectionVector = getSectionCS().Direction().Reversed();
+ gp_Vec gExtrudeVector = gSectionVector.Crossed(gProfileVector);
+ Base::Vector3d vClosestBasis = DrawUtil::closestBasis(gp_Dir(gExtrudeVector), sectionCS);
+ 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) {
+ //profile is not within span of shape
+ return false;
+ }
+ //profile is within span of shape
+ return true;
+}
+
+bool DrawComplexSection::showSegment(gp_Dir segmentNormal) const
+{
+ if (ProjectionStrategy.getValue() < 2) {
+ //Single or Piecewise are always true
+ return true;
+ }
+
+ Base::Vector3d vSectionNormal = SectionNormal.getValue();
+ gp_Dir gSectionNormal(vSectionNormal.x, vSectionNormal.y, vSectionNormal.z);
+ if (DU::fpCompare(fabs(gSectionNormal.Dot(segmentNormal)), 0.0)) {
+ //segment normal is perpendicular to section normal, so segment is parallel to section normal,
+ //and for ProjectionStrategy "NoParallel", we don't display these segments.
+ return false;
+ }
+ return true;
+}
+
+// general purpose geometry methods
+
+//make a "face" (not necessarily a TopoDS_Face since the extrusion of a wire is a shell)
+//from a single open wire by displacing the wire extruding it
+TopoDS_Shape DrawComplexSection::extrudeWireToFace(TopoDS_Wire& wire,
+ gp_Dir extrudeDir,
+ double extrudeDist)
+{
+ gp_Trsf mov;
+ mov.SetTranslation(gp_Vec(extrudeDir) * (-extrudeDist));
+ TopLoc_Location loc(mov);
+ wire.Move(loc);
+
+ BRepPrimAPI_MakePrism mkPrism(wire, gp_Vec(extrudeDir) * 2.0 * extrudeDist);
+
+ return mkPrism.Shape();
+}
+
+//returns the normal of the face to be extruded into a cutting tool
+//the face is expected to be planar
+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.0;
+ double vMid = (vParmFirst + vParmLast) / 2.0;
+
+ BRepLProp_SLProps prop(adapt, uMid, vMid, 1, 0.01);
+ 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)
+{
+ Bnd_Box box0, box1;
+ BRepBndLib::Add(face, box0);
+ box0.SetGap(0.1); //generous
+ BRepBndLib::Add(shape, box1);
+ box1.SetGap(0.1);
+ if (box0.IsOut(box1)) {
+ return false; //boxes don't intersect
+ }
+ return true;
+}
+
+TopoDS_Shape DrawComplexSection::shapeShapeIntersect(const TopoDS_Shape &shape0, const TopoDS_Shape &shape1)
+{
+ BRepAlgoAPI_Common anOp;
+ anOp.SetFuzzyValue (EWTOLERANCE);
+ TopTools_ListOfShape anArg1, anArg2;
+ anArg1.Append (shape0);
+ anArg2.Append (shape1);
+ anOp.SetArguments (anArg1);
+ anOp.SetTools (anArg2);
+ anOp.Build();
+ return anOp.Shape(); //always a compound
+}
+
+//find all the intersecting regions of face and shape
+std::vector DrawComplexSection::faceShapeIntersect(const TopoDS_Face &face, const TopoDS_Shape &shape)
+{
+// Base::Console().Message("DCS::faceShapeIntersect()\n");
+ TopoDS_Shape intersect = shapeShapeIntersect(face, shape);
+ if (intersect.IsNull()) {
+ return std::vector();
+ }
+ std::vector 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;
+}
+
+//ensure that the edges in the output wire are in nose to tail order
+TopoDS_Wire DrawComplexSection::makeNoseToTailWire(TopoDS_Wire inWire)
+{
+ if (inWire.IsNull()) {
+ return inWire;
+ }
+
+ std::list inList;
+ TopExp_Explorer expEdges(inWire, TopAbs_EDGE);
+ for ( ; expEdges.More(); expEdges.Next()) {
+ TopoDS_Edge edge = TopoDS::Edge(expEdges.Current());
+ inList.push_back(edge);
+ }
+
+ std::list sortedList;
+ if (inList.empty() ||
+ inList.size() == 1) {
+ return inWire;
+ } else {
+ sortedList = DrawUtil::sort_Edges(EWTOLERANCE, inList);
+ }
+
+ BRepBuilderAPI_MakeWire mkWire;
+ for (auto& edge : sortedList) {
+ mkWire.Add(edge);
+ }
+ return mkWire.Wire();
+}
+
+//static
+bool DrawComplexSection::isProfileObject(App::DocumentObject* obj)
+{
+ //if the object's shape is a wire or an edge, then it can be a profile object
+ TopoDS_Shape shape = Part::Feature::getShape(obj);
+ if (shape.IsNull()) {
+ return false;
+ }
+ if (shape.ShapeType() == TopAbs_WIRE ||
+ shape.ShapeType() == TopAbs_EDGE) {
+ return true;
+ }
+ //don't know what this is, but it isn't suitable as a profile
+ return false;
+}
+
+bool DrawComplexSection::isMultiSegmentProfile(App::DocumentObject* obj)
+{
+ //if the object's shape is a wire or an edge, then it can be a profile object
+ TopoDS_Shape shape = Part::Feature::getShape(obj);
+ if (shape.IsNull()) {
+ return false;
+ }
+ if (shape.ShapeType() == TopAbs_EDGE) {
+ //only have 1 edge, can't be multisegment;
+ return false;
+ }
+ if (shape.ShapeType() == TopAbs_WIRE) {
+ std::vector 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;
+}
+
+//check if the profile has curves in it
+bool DrawComplexSection::isLinearProfile(App::DocumentObject* obj)
+{
+ TopoDS_Shape shape = Part::Feature::getShape(obj);
+ if (shape.IsNull()) {
+ return false;
+ }
+ if (shape.ShapeType() == TopAbs_EDGE) {
+ //only have 1 edge
+ TopoDS_Edge edge = TopoDS::Edge(shape);
+ BRepAdaptor_Curve adapt(edge);
+ if (adapt.GetType() == GeomAbs_Line ) {
+ return true;
+ }
+ return false;
+ }
+
+ if (shape.ShapeType() == TopAbs_WIRE) {
+ std::vector 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;
+ }
+ }
+ //all the edges in the wire are lines
+ return true;
+ }
+
+ //this shouldn't happen
+ return false;
+}
+
+// Python Drawing feature ---------------------------------------------------------
+
+namespace App {
+/// @cond DOXERR
+PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawComplexSectionPython, TechDraw::DrawComplexSection)
+template<> const char* TechDraw::DrawComplexSectionPython::getViewProviderName() const {
+ return "TechDrawGui::ViewProviderDrawingView";
+}
+/// @endcond
+
+// explicit template instantiation
+template class TechDrawExport FeaturePythonT;
+}
diff --git a/src/Mod/TechDraw/App/DrawComplexSection.h b/src/Mod/TechDraw/App/DrawComplexSection.h
new file mode 100644
index 0000000000..5d973954d6
--- /dev/null
+++ b/src/Mod/TechDraw/App/DrawComplexSection.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright (c) 2022 WandererFan *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+
+#ifndef DrawComplexSection_h_
+#define DrawComplexSection_h_
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "DrawViewSection.h"
+
+namespace TechDraw
+{
+
+//changes in direction of complex section line
+class ChangePoint
+{
+public:
+ ChangePoint(QPointF location, QPointF preDirection, QPointF postDirection);
+ ChangePoint(gp_Pnt location, gp_Dir preDirection, gp_Dir postDirection);
+ ~ChangePoint() = default;
+
+ QPointF getLocation() const { return m_location; }
+ QPointF getPreDirection() const { return m_preDirection; }
+ QPointF getPostDirection() const { return m_postDirection; }
+ void scale(double scaleFactor);
+
+private:
+ QPointF m_location;
+ QPointF m_preDirection;
+ QPointF m_postDirection;
+};
+
+using ChangePointVector = std::vector;
+
+class TechDrawExport DrawComplexSection : public DrawViewSection
+{
+ PROPERTY_HEADER_WITH_OVERRIDE(Part::DrawComplexSection);
+
+public:
+ DrawComplexSection();
+ ~DrawComplexSection() = default;
+
+ App::PropertyLink CuttingToolWireObject;
+ App::PropertyEnumeration ProjectionStrategy; //Single or Piecewise
+
+ TopoDS_Shape getShapeToCut() override;
+ TopoDS_Shape makeCuttingTool(double dMax) override;
+ gp_Ax2 getCSFromBase(const std::string sectionName) const override;
+ bool isBaseValid() const override;
+ TopoDS_Compound findSectionPlaneIntersections(const TopoDS_Shape& cutShape) override;
+ TopoDS_Shape prepareShape(const TopoDS_Shape& cutShape,
+ double shapeSize) override;
+ TopoDS_Shape getShapeToPrepare() const override;
+ void makeSectionCut(TopoDS_Shape &baseShape) override;
+ TopoDS_Shape getShapeToIntersect() override;
+ gp_Pln getSectionPlane() const override;
+ TopoDS_Compound alignSectionFaces(TopoDS_Shape faceIntersections) override;
+ std::pair
+ sectionLineEnds() override;
+
+ bool boxesIntersect(TopoDS_Face& face, TopoDS_Shape &shape);
+ TopoDS_Shape shapeShapeIntersect(const TopoDS_Shape& shape0, const TopoDS_Shape& shape1);
+ std::vector faceShapeIntersect(const TopoDS_Face& face, const TopoDS_Shape& shape);
+ TopoDS_Shape extrudeWireToFace(TopoDS_Wire& wire, gp_Dir extrudeDir, double extrudeDist);
+ TopoDS_Shape makeAlignedPieces(const TopoDS_Shape& rawShape,
+ const TopoDS_Shape& toolFaceShape,
+ double extrudeDistance);
+ TopoDS_Shape distributeAlignedPieces(std::vector pieces);
+ TopoDS_Compound singleToolIntersections(const TopoDS_Shape& cutShape);
+ TopoDS_Compound piecewiseToolIntersections(const TopoDS_Shape& cutShape);
+
+ BaseGeomPtrVector makeSectionLineGeometry();
+ std::pair
+ sectionArrowDirs();
+ TopoDS_Wire makeSectionLineWire();
+
+ TopoDS_Wire makeProfileWire(App::DocumentObject* toolObj);
+ TopoDS_Wire makeNoseToTailWire(TopoDS_Wire inWire);
+ gp_Dir projectProfileWire(TopoDS_Wire profileWire, gp_Ax3 paperCS);
+ ChangePointVector getChangePointsFromSectionLine();
+
+ bool validateProfilePosition(TopoDS_Wire profileWire,
+ gp_Ax2 sectionCS,
+ gp_Dir& gClosestBasis) const;
+ bool showSegment(gp_Dir segmentNormal) const;
+
+ static bool isProfileObject(App::DocumentObject* obj);
+ static bool isMultiSegmentProfile(App::DocumentObject* obj);
+ static bool isLinearProfile(App::DocumentObject* obj);
+
+private:
+ gp_Dir getFaceNormal(TopoDS_Face& face);
+
+ TopoDS_Shape m_toolFaceShape;
+ TopoDS_Wire m_profileWire;
+
+ static const char* ProjectionStrategyEnums[];
+};
+
+using DrawComplexSectionPython = App::FeaturePythonT;
+
+} //namespace TechDraw
+
+#endif
+
diff --git a/src/Mod/TechDraw/App/DrawProjectSplit.cpp b/src/Mod/TechDraw/App/DrawProjectSplit.cpp
index 90212cd882..72aaa7e602 100644
--- a/src/Mod/TechDraw/App/DrawProjectSplit.cpp
+++ b/src/Mod/TechDraw/App/DrawProjectSplit.cpp
@@ -512,6 +512,12 @@ std::vector DrawProjectSplit::scrubEdges(const std::vector DrawProjectSplit::scrubEdges(std::vector& origEdges,
std::vector &closedEdges)
{
+ if (origEdges.empty()) {
+ //how did this happen? if Scale is zero, all the edges will be zero length,
+ //but Scale property has constraint, so this shouldn't happen!
+// Base::Console().Message("DPS::scrubEdges(2) - origEdges is empty\n"); //debug
+ return std::vector();
+ }
//HLR usually delivers overlapping edges. We need to refine edge overlaps
//into non-overlapping pieces
std::vector noOverlaps;
diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp
index ee2a925442..b826bead0a 100644
--- a/src/Mod/TechDraw/App/DrawUtil.cpp
+++ b/src/Mod/TechDraw/App/DrawUtil.cpp
@@ -23,36 +23,38 @@
#include "PreCompiled.h"
#ifndef _PreComp_
-# include
-# include
-# include
-# include
+#include
+#include
+#include
+#include
-# include
+#include
-# include
-# include
-# include
+#include
+#include
+#include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#endif
#include
@@ -511,6 +513,11 @@ Base::Vector3d DrawUtil::vecRotate(Base::Vector3d vec,
return Base::Vector3d(xForm * (vec));
}
+gp_Vec DrawUtil::closestBasis(gp_Vec inVec)
+{
+ return gp_Vec(togp_Dir(closestBasis(toVector3d(inVec))));
+}
+
Base::Vector3d DrawUtil::closestBasis(Base::Vector3d v)
{
Base::Vector3d result(0.0, -1, 0);
@@ -522,11 +529,16 @@ Base::Vector3d DrawUtil::closestBasis(Base::Vector3d v)
Base::Vector3d stdZr(0.0, 0.0, -1.0);
//first check if already a basis
- if (checkParallel(v, stdZ) ||
- checkParallel(v, stdY) ||
- checkParallel(v, stdX)) {
+ if (v.Dot(stdX) == 1.0 ||
+ v.Dot(stdY) == 1.0 ||
+ v.Dot(stdZ) == 1.0) {
return v;
}
+ if (v.Dot(stdX) == -1.0 ||
+ v.Dot(stdY) == -1.0 ||
+ v.Dot(stdZ) == -1.0) {
+ return -v;
+ }
//not a basis. find smallest angle with a basis.
double angleX, angleY, angleZ, angleXr, angleYr, angleZr, angleMin;
@@ -537,23 +549,139 @@ Base::Vector3d DrawUtil::closestBasis(Base::Vector3d v)
angleYr = stdYr.GetAngle(v);
angleZr = stdZr.GetAngle(v);
- angleMin = angleX;
- if (angleY < angleMin) {
- return stdY;
+ angleMin = std::min({angleX, angleY, angleZ, angleXr, angleYr, angleZr});
+ if (angleX == angleMin) {
+ return Base::Vector3d(1.0, 0.0, 0.0);
}
- if (angleZ < angleMin) {
- return stdZ;
+
+ if (angleY == angleMin) {
+ return Base::Vector3d(0.0, 1.0, 0.0);
}
- if (angleXr < angleMin) {
- return stdXr;
+
+ if (angleZ == angleMin) {
+ return Base::Vector3d(0.0, 0.0, 1.0);
}
- if (angleYr < angleMin) {
- return stdYr;
+
+ if (angleXr == angleMin) {
+ return Base::Vector3d(1.0, 0.0, 0.0);
}
- if (angleZr < angleMin) {
- return stdZr;
+
+ if (angleYr == angleMin) {
+ return Base::Vector3d(0.0, 1.0, 0.0);
}
- return stdX;
+
+ if (angleZr == angleMin) {
+ return Base::Vector3d(0.0, 0.0, 1.0);
+ }
+
+ //should not get to here
+ return Base::Vector3d(1.0, 0.0, 0.0);
+
+}
+
+Base::Vector3d DrawUtil::closestBasis(Base::Vector3d vDir, gp_Ax2 coordSys)
+{
+ gp_Dir gDir(vDir.x, vDir.y, vDir.z);
+ return closestBasis(gDir, coordSys);
+}
+
+Base::Vector3d DrawUtil::closestBasis(gp_Dir gDir, gp_Ax2 coordSys)
+{
+ gp_Dir xCS = coordSys.XDirection(); //these are unit vectors?
+ gp_Dir yCS = coordSys.YDirection();
+ gp_Dir zCS = coordSys.Direction();
+
+ //first check if already a basis
+ if (gDir.Dot(xCS) == 1.0 ||
+ gDir.Dot(yCS) == 1.0 ||
+ gDir.Dot(zCS) == 1.0 ) {
+ //gDir is parallel with a basis
+ return Base::Vector3d( gDir.X(), gDir.Y(), gDir.Z()) ;
+ }
+
+ if (gDir.Dot(xCS.Reversed()) == 1.0 ||
+ gDir.Dot(yCS.Reversed()) == 1.0 ||
+ gDir.Dot(zCS.Reversed()) == 1.0 ) {
+ //gDir is anti-parallel with a basis
+ return Base::Vector3d( -gDir.X(), -gDir.Y(), -gDir.Z());
+ }
+
+ //not a basis. find smallest angle with a basis.
+ double angleX, angleY, angleZ, angleXr, angleYr, angleZr, angleMin;
+ angleX = gDir.Angle(xCS);
+ angleY = gDir.Angle(yCS);
+ angleZ = gDir.Angle(zCS);
+ angleXr = gDir.Angle(xCS.Reversed());
+ angleYr = gDir.Angle(yCS.Reversed());
+ angleZr = gDir.Angle(zCS.Reversed());
+
+ angleMin = std::min({angleX, angleY, angleZ, angleXr, angleYr, angleZr});
+ if (angleX == angleMin) {
+ return Base::Vector3d(xCS.X(), xCS.Y(), xCS.Z());
+ }
+
+ if (angleY == angleMin) {
+ return Base::Vector3d(yCS.X(), yCS.Y(), yCS.Z());
+ }
+
+ if (angleZ == angleMin) {
+ return Base::Vector3d(zCS.X(), zCS.Y(), zCS.Z()) ;
+ }
+
+ if (angleXr == angleMin) {
+ return Base::Vector3d(-xCS.X(), -xCS.Y(), -xCS.Z());
+ }
+
+ if (angleYr == angleMin) {
+ return Base::Vector3d(-yCS.X(), -yCS.Y(), -yCS.Z());
+ }
+
+ if (angleZr == angleMin) {
+ return Base::Vector3d(-zCS.X(), -zCS.Y(), -zCS.Z());
+ }
+
+ //should not get to here
+ return Base::Vector3d(xCS.X(), xCS.Y(), xCS.Z());
+}
+
+double DrawUtil::getWidthInDirection(gp_Dir direction, TopoDS_Shape& shape)
+{
+ Base::Vector3d stdX(1.0, 0.0, 0.0);
+ Base::Vector3d stdY(0.0, 1.0, 0.0);
+ Base::Vector3d stdZ(0.0, 0.0, 1.0);
+ Base::Vector3d stdXr(-1.0, 0.0, 0.0);
+ Base::Vector3d stdYr(0.0, -1.0, 0.0);
+ Base::Vector3d stdZr(0.0, 0.0, -1.0);
+ Base::Vector3d vClosestBasis = closestBasis(toVector3d(direction));
+
+ Bnd_Box shapeBox;
+ shapeBox.SetGap(0.0);
+ BRepBndLib::AddOptimal(shape, shapeBox);
+ double xMin = 0, xMax = 0, yMin = 0, yMax = 0, zMin = 0, zMax = 0;
+ if (shapeBox.IsVoid()) {
+ //this really shouldn't happen here as null shapes should have been caught
+ //long before this
+ Base::Console().Error("DU::getWidthInDirection - shapeBox is void\n");
+ return 0.0;
+ }
+ shapeBox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
+
+ if (vClosestBasis.IsEqual(stdX, EWTOLERANCE) ||
+ vClosestBasis.IsEqual(stdXr, EWTOLERANCE) ) {
+ return xMax - xMin;
+ }
+
+ if (vClosestBasis.IsEqual(stdY, EWTOLERANCE) ||
+ vClosestBasis.IsEqual(stdYr, EWTOLERANCE) ) {
+ return yMax - yMin;
+ }
+
+ if (vClosestBasis.IsEqual(stdZ, EWTOLERANCE) ||
+ vClosestBasis.IsEqual(stdZr, EWTOLERANCE) ) {
+ return zMax - zMin;
+ }
+
+ return 0.0;
}
//based on Function provided by Joe Dowsett, 2014
@@ -873,6 +1001,102 @@ void DrawUtil::encodeXmlSpecialChars(std::string& inoutText)
inoutText.swap(buffer);
}
+//Sort edges into nose to tail order. From Part/App/AppPartPy.cpp. gives back a sequence
+//of nose to tail edges and a shrunken input sequence of edges (the unconnected left overs)
+//struct EdgePoints {
+// gp_Pnt v1, v2;
+// std::list::iterator it;
+// TopoDS_Edge edge;
+//};
+std::list DrawUtil::sort_Edges(double tol3d, std::list& edges)
+{
+ tol3d = tol3d * tol3d;
+ std::list edge_points;
+ TopExp_Explorer xp;
+ for (std::list::iterator it = edges.begin(); it != edges.end(); ++it) {
+ EdgePoints ep;
+ xp.Init(*it,TopAbs_VERTEX);
+ ep.v1 = BRep_Tool::Pnt(TopoDS::Vertex(xp.Current()));
+ xp.Next();
+ ep.v2 = BRep_Tool::Pnt(TopoDS::Vertex(xp.Current()));
+ ep.it = it;
+ ep.edge = *it;
+ edge_points.push_back(ep);
+ }
+
+ if (edge_points.empty())
+ return std::list();
+
+ std::list sorted;
+ gp_Pnt gpChainFirst, gpChainLast;
+ gpChainFirst = edge_points.front().v1;
+ gpChainLast = edge_points.front().v2;
+
+ sorted.push_back(edge_points.front().edge);
+ edges.erase(edge_points.front().it);
+ edge_points.erase(edge_points.begin());
+
+ while (!edge_points.empty()) {
+ // search for adjacent edge
+ std::list::iterator itEdgePoint;
+ for (itEdgePoint = edge_points.begin(); itEdgePoint != edge_points.end(); ++itEdgePoint) {
+ if (itEdgePoint->v1.SquareDistance(gpChainLast) <= tol3d) {
+ //found a connection from end of chain to start of edge
+ gpChainLast = itEdgePoint->v2;
+ sorted.push_back(itEdgePoint->edge);
+ edges.erase(itEdgePoint->it);
+ edge_points.erase(itEdgePoint);
+ itEdgePoint = edge_points.begin();
+ break;
+ }
+ else if (itEdgePoint->v2.SquareDistance(gpChainFirst) <= tol3d) {
+ //found a connection from start of chain to end of edge
+ gpChainFirst = itEdgePoint->v1;
+ sorted.push_front(itEdgePoint->edge);
+ edges.erase(itEdgePoint->it);
+ edge_points.erase(itEdgePoint);
+ itEdgePoint = edge_points.begin();
+ break;
+ }
+ else if (itEdgePoint->v2.SquareDistance(gpChainLast) <= tol3d) {
+ //found a connection from end of chain to end of edge
+ gpChainLast = itEdgePoint->v1;
+ Standard_Real firstParam, lastParam;
+ const Handle(Geom_Curve) & curve = BRep_Tool::Curve(itEdgePoint->edge, firstParam, lastParam);
+ firstParam = curve->ReversedParameter(firstParam);
+ lastParam = curve->ReversedParameter(lastParam);
+ TopoDS_Edge edgeReversed = BRepBuilderAPI_MakeEdge(curve->Reversed(), firstParam, lastParam);
+ sorted.push_back(edgeReversed);
+ edges.erase(itEdgePoint->it);
+ edge_points.erase(itEdgePoint);
+ itEdgePoint = edge_points.begin();
+ break;
+ }
+ else if (itEdgePoint->v1.SquareDistance(gpChainFirst) <= tol3d) {
+ //found a connection from start of chain to start of edge
+ gpChainFirst = itEdgePoint->v2;
+ Standard_Real firstParam, lastParam;
+ const Handle(Geom_Curve) & curve = BRep_Tool::Curve(itEdgePoint->edge, firstParam, lastParam);
+ firstParam = curve->ReversedParameter(firstParam);
+ lastParam = curve->ReversedParameter(lastParam);
+ TopoDS_Edge edgeReversed = BRepBuilderAPI_MakeEdge(curve->Reversed(), firstParam, lastParam);
+ sorted.push_front(edgeReversed);
+ edges.erase(itEdgePoint->it);
+ edge_points.erase(itEdgePoint);
+ itEdgePoint = edge_points.begin();
+ break;
+ }
+ }
+
+ if ((itEdgePoint == edge_points.end()) || (gpChainLast.SquareDistance(gpChainFirst) <= tol3d)) {
+ // no adjacent edge found or polyline is closed
+ return sorted;
+ }
+ }
+
+ return sorted;
+}
+
// Supplementary mathematical functions
// ====================================
@@ -1367,7 +1591,7 @@ void DrawUtil::dumpCS3(const char* text,
gp_Dir baseX = CS.XDirection();
gp_Dir baseY = CS.YDirection();
gp_Pnt baseOrg = CS.Location();
- Base::Console().Message("DU::dumpCSF - %s Loc: %s Axis: %s X: %s Y: %s\n", text,
+ Base::Console().Message("DU::dumpCS3 - %s Loc: %s Axis: %s X: %s Y: %s\n", text,
DrawUtil::formatVector(baseOrg).c_str(),
DrawUtil::formatVector(baseAxis).c_str(),
DrawUtil::formatVector(baseX).c_str(),
diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h
index 772bab8c5f..3e4cc5b815 100644
--- a/src/Mod/TechDraw/App/DrawUtil.h
+++ b/src/Mod/TechDraw/App/DrawUtil.h
@@ -70,6 +70,13 @@
namespace TechDraw
{
+//used by sort_Edges
+struct EdgePoints {
+ gp_Pnt v1, v2;
+ std::list::iterator it;
+ TopoDS_Edge edge;
+};
+
/// Convenient utility functions for TechDraw Module
class TechDrawExport DrawUtil {
public:
@@ -122,10 +129,15 @@ class TechDrawExport DrawUtil {
double angle,
Base::Vector3d axis,
Base::Vector3d org = Base::Vector3d(0.0, 0.0, 0.0));
+
static Base::Vector3d closestBasis(Base::Vector3d v);
+ static gp_Vec closestBasis(gp_Vec inVec);
+ static Base::Vector3d closestBasis(Base::Vector3d vDir, gp_Ax2 coordSys);
+ static Base::Vector3d closestBasis(gp_Dir gDir, gp_Ax2 coordSys);
+
+ static double getWidthInDirection(gp_Dir direction, TopoDS_Shape& shape);
+
static double getDefaultLineWeight(std::string s);
-/* static Base::Vector3d vector23(const Base::Vector3d& v2) { return Base::Vector3d(v2.x, v2.y, 0.0); }*/
-/* static Base::Vector3d vector32(const Base::Vector3d& v3) { return Base::Vector3d(v3.x, v3.y); }*/
//! is pt between end1 and end2?
static bool isBetween(const Base::Vector3d pt, const Base::Vector3d end1, const Base::Vector3d end2);
//! find intersection in 2d for 2 lines in point+direction form
@@ -133,8 +145,10 @@ class TechDrawExport DrawUtil {
Base::Vector3d p2, Base::Vector3d d2);
static Base::Vector2d Intersect2d(Base::Vector2d p1, Base::Vector2d d1,
Base::Vector2d p2, Base::Vector2d d2);
- static Base::Vector3d gpPnt2V3(const gp_Pnt gp) { return Base::Vector3d(gp.X(), gp.Y(), gp.Z()); }
- static gp_Pnt V32gpPnt(const Base::Vector3d v) { return gp_Pnt(v.x, v.y, v.z); }
+ static Base::Vector3d toVector3d(const gp_Pnt gp) { return Base::Vector3d(gp.X(), gp.Y(), gp.Z()); }
+ static Base::Vector3d toVector3d(const gp_Dir gp) { return Base::Vector3d(gp.X(), gp.Y(), gp.Z()); }
+ static gp_Pnt togp_Pnt(const Base::Vector3d v) { return gp_Pnt(v.x, v.y, v.z); }
+ static gp_Dir togp_Dir(const Base::Vector3d v) { return gp_Dir(v.x, v.y, v.z); }
static std::string shapeToString(TopoDS_Shape s);
static TopoDS_Shape shapeFromString(std::string s);
static Base::Vector3d invertY(Base::Vector3d v);
@@ -148,7 +162,7 @@ class TechDrawExport DrawUtil {
static bool circulation(Base::Vector3d A, Base::Vector3d B, Base::Vector3d C);
static int countSubShapes(TopoDS_Shape shape, TopAbs_ShapeEnum subShape);
static void encodeXmlSpecialChars(std::string& inoutText);
-
+ static std::list sort_Edges(double tol3d, std::list& edges);
// Supplementary mathematical functions
static int sgn(double x);
diff --git a/src/Mod/TechDraw/App/DrawView.cpp b/src/Mod/TechDraw/App/DrawView.cpp
index 34516d80c8..57c4350fb2 100644
--- a/src/Mod/TechDraw/App/DrawView.cpp
+++ b/src/Mod/TechDraw/App/DrawView.cpp
@@ -118,6 +118,15 @@ void DrawView::onChanged(const App::Property* prop)
//Coding note: calling execute, recompute or recomputeFeature inside an onChanged
//method can create infinite loops if the called method changes a property. In general
//don't do this! There are situations where it is OK, but careful analysis is a must.
+
+ if (prop == &Scale &&
+ Scale.getValue() < Precision::Confusion()) {
+ //this is not supposed to happen since Scale has constraints, but it may
+ //happen during changes made in PropertyEditor?
+ Scale.setValue(1.0);
+ return;
+ }
+
if (isRestoring()) {
App::DocumentObject::onChanged(prop);
return;
diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp
index d3fda223ec..edecf51e56 100644
--- a/src/Mod/TechDraw/App/DrawViewPart.cpp
+++ b/src/Mod/TechDraw/App/DrawViewPart.cpp
@@ -25,29 +25,30 @@
#include "PreCompiled.h"
#ifndef _PreComp_
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#endif
#include
@@ -237,7 +238,7 @@ App::DocumentObjectExecReturn *DrawViewPart::execute(void)
TopoDS_Shape shape = getSourceShape();
if (shape.IsNull()) {
- Base::Console().Log("DVP::execute - %s - Source shape is Null.\n",
+ Base::Console().Message("DVP::execute - %s - Source shape is Null.\n",
getNameInDocument());
return DrawView::execute();
}
@@ -345,7 +346,7 @@ GeometryObject* DrawViewPart::makeGeometryForShape(TopoDS_Shape& shape)
}
//create a geometry object and trigger the HLR process in another thread
-TechDraw::GeometryObject* DrawViewPart::buildGeometryObject(TopoDS_Shape& shape, gp_Ax2& viewAxis)
+TechDraw::GeometryObject* DrawViewPart::buildGeometryObject(TopoDS_Shape& shape, const gp_Ax2 &viewAxis)
{
// Base::Console().Message("DVP::buildGeometryObject() - %s\n", getNameInDocument());
showProgressMessage(getNameInDocument(), "is finding hidden lines");
@@ -468,7 +469,7 @@ void DrawViewPart::extractFaces()
geometryObject->getVisibleFaceEdges(SmoothVisible.getValue(),SeamVisible.getValue());
if (goEdges.empty()) {
- Base::Console().Message("DVP::extractFaces - %s - no face edges available!\n", getNameInDocument());
+// Base::Console().Message("DVP::extractFaces - %s - no face edges available!\n", getNameInDocument()); //debug
return;
}
@@ -476,6 +477,13 @@ void DrawViewPart::extractFaces()
std::vector closedEdges;
std::vector cleanEdges = DrawProjectSplit::scrubEdges(goEdges, closedEdges);
+ if (cleanEdges.empty() &&
+ closedEdges.empty()) {
+ //how does this happen? something wrong somewhere
+// Base::Console().Message("DVP::extractFaces - no clean or closed wires\n"); //debug
+ return;
+ }
+
//use EdgeWalker to make wires from edges
EdgeWalker eWalker;
std::vector sortedWires;
@@ -892,8 +900,27 @@ BaseGeomPtr DrawViewPart::projectEdge(const TopoDS_Edge& e) const
projector.Add(e);
projector.Build();
TopoDS_Shape s = projector.Projection();
-// Base::Console().Message("DVP::projectEdge - s.IsNull: %d\n", s.IsNull());
- BaseGeomPtr result;
+ return BaseGeom::baseFactory(TopoDS::Edge(s));
+}
+
+//simple projection of inWire with conversion of the result to TD geometry
+BaseGeomPtrVector DrawViewPart::projectWire(const TopoDS_Wire& inWire) const
+{
+// Base::Console().Message("DVP::projectWire() - inWire.IsNull: %d\n", inWire.IsNull());
+ BaseGeomPtrVector result;
+ Base::Vector3d stdOrg(0.0, 0.0, 0.0);
+
+ TopoDS_Face paper = BRepBuilderAPI_MakeFace(gp_Pln(getProjectionCS(stdOrg)));
+ BRepAlgo_NormalProjection projector(paper);
+ projector.Add(inWire);
+ projector.Build();
+ BRepTools::Write(projector.Projection(), "DVPprojectedWire.brep"); //debug
+
+ TopExp_Explorer expShape(projector.Projection(), TopAbs_EDGE);
+ for (; expShape.More(); expShape.Next()) {
+ BaseGeomPtr edge = BaseGeom::baseFactory(TopoDS::Edge(expShape.Current()));
+ result.push_back(edge);
+ }
return result;
}
diff --git a/src/Mod/TechDraw/App/DrawViewPart.h b/src/Mod/TechDraw/App/DrawViewPart.h
index f75d9eef91..d4171ef309 100644
--- a/src/Mod/TechDraw/App/DrawViewPart.h
+++ b/src/Mod/TechDraw/App/DrawViewPart.h
@@ -142,11 +142,12 @@ public:
virtual Base::Vector3d projectPoint(const Base::Vector3d& pt,
bool invert = true) const;
virtual BaseGeomPtr projectEdge(const TopoDS_Edge& e) const;
+ virtual BaseGeomPtrVector projectWire(const TopoDS_Wire& inWire) const;
virtual gp_Ax2 getViewAxis(const Base::Vector3d& pt,
const Base::Vector3d& direction,
const bool flip=true) const;
- virtual gp_Ax2 getProjectionCS(Base::Vector3d pt) const;
+ virtual gp_Ax2 getProjectionCS(Base::Vector3d pt = Base::Vector3d(0.0, 0.0, 0.0)) const;
virtual Base::Vector3d getXDirection() const; //don't use XDirection.getValue()
virtual Base::Vector3d getOriginalCentroid() const;
virtual Base::Vector3d getCurrentCentroid() const;
@@ -223,7 +224,7 @@ protected:
void onChanged(const App::Property* prop) override;
void unsetupObject() override;
- virtual TechDraw::GeometryObject* buildGeometryObject(TopoDS_Shape& shape, gp_Ax2& viewAxis);
+ virtual TechDraw::GeometryObject* buildGeometryObject(TopoDS_Shape& shape, const gp_Ax2& viewAxis);
virtual TechDraw::GeometryObject* makeGeometryForShape(TopoDS_Shape& shape); //const??
void partExec(TopoDS_Shape& shape);
virtual void addShapes2d(void);
diff --git a/src/Mod/TechDraw/App/DrawViewSection.cpp b/src/Mod/TechDraw/App/DrawViewSection.cpp
index fe9b9e2daf..cd0d9944c7 100644
--- a/src/Mod/TechDraw/App/DrawViewSection.cpp
+++ b/src/Mod/TechDraw/App/DrawViewSection.cpp
@@ -22,33 +22,55 @@
* *
***************************************************************************/
+//DrawViewSection processing overview
+
+//execute
+// sectionExec(getShapeToCut())
+
+//sectionExec
+// makeSectionCut(baseShape)
+
+//makeSectionCut (separate thread)
+// m_cuttingTool = makeCuttingTool (DVSTool.brep)
+// m_cutPieces = (baseShape - m_cuttingTool) (DVSCutPieces.brep)
+
+//onSectionCutFinished
+// m_preparedShape = prepareShape(m_cutPieces) - centered, scaled, rotated
+// geometryObject = DVP::buildGeometryObject(m_preparedShape) (HLR)
+
+//postHlrTasks
+// faceIntersections = findSectionPlaneIntersections
+// m_sectionTopoDSFaces = alignSectionFaces(faceIntersections)
+// m_tdSectionFaces = makeTDSectionFaces(m_sectionTopoDSFaces)
+
#include "PreCompiled.h"
#ifndef _PreComp_
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include