From 0ff473a9b60894ec02337b997a2c2502d8f30b49 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Mon, 10 Oct 2022 10:10:42 -0400 Subject: [PATCH] [TD]Complex Section - initial implementation --- src/Gui/MDIView.cpp | 6 +- src/Mod/TechDraw/App/AppTechDraw.cpp | 12 +- src/Mod/TechDraw/App/CMakeLists.txt | 2 + src/Mod/TechDraw/App/DrawComplexSection.cpp | 1205 +++++++++++++++++ src/Mod/TechDraw/App/DrawComplexSection.h | 129 ++ src/Mod/TechDraw/App/DrawProjectSplit.cpp | 6 + src/Mod/TechDraw/App/DrawUtil.cpp | 310 ++++- src/Mod/TechDraw/App/DrawUtil.h | 24 +- src/Mod/TechDraw/App/DrawView.cpp | 9 + src/Mod/TechDraw/App/DrawViewPart.cpp | 83 +- src/Mod/TechDraw/App/DrawViewPart.h | 5 +- src/Mod/TechDraw/App/DrawViewSection.cpp | 429 ++++-- src/Mod/TechDraw/App/DrawViewSection.h | 42 +- src/Mod/TechDraw/App/DrawViewSpreadsheet.cpp | 8 +- src/Mod/TechDraw/App/Geometry.cpp | 12 +- src/Mod/TechDraw/App/GeometryObject.cpp | 152 ++- src/Mod/TechDraw/App/GeometryObject.h | 26 +- src/Mod/TechDraw/App/PreCompiled.h | 1 + src/Mod/TechDraw/App/Preferences.cpp | 21 + src/Mod/TechDraw/App/Preferences.h | 3 +- src/Mod/TechDraw/Gui/CMakeLists.txt | 5 + src/Mod/TechDraw/Gui/Command.cpp | 124 +- .../Gui/DlgPrefsTechDrawAnnotation.ui | 736 +++++----- .../Gui/DlgPrefsTechDrawAnnotationImp.cpp | 2 + src/Mod/TechDraw/Gui/PreferencesGui.cpp | 7 + src/Mod/TechDraw/Gui/PreferencesGui.h | 1 + src/Mod/TechDraw/Gui/QGIDecoration.h | 1 + src/Mod/TechDraw/Gui/QGISectionLine.cpp | 353 +++-- src/Mod/TechDraw/Gui/QGISectionLine.h | 31 +- src/Mod/TechDraw/Gui/QGIViewPart.cpp | 102 +- src/Mod/TechDraw/Gui/QGIViewPart.h | 1 + src/Mod/TechDraw/Gui/Resources/TechDraw.qrc | 3 + .../icons/actions/TechDraw_ComplexSection.svg | 767 +++++++++++ src/Mod/TechDraw/Gui/TaskComplexSection.cpp | 360 +++++ src/Mod/TechDraw/Gui/TaskComplexSection.h | 137 ++ src/Mod/TechDraw/Gui/TaskComplexSection.ui | 296 ++++ src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp | 10 +- src/Mod/TechDraw/Gui/ViewProviderViewPart.h | 5 +- src/Mod/TechDraw/Gui/Workbench.cpp | 3 + 39 files changed, 4656 insertions(+), 773 deletions(-) create mode 100644 src/Mod/TechDraw/App/DrawComplexSection.cpp create mode 100644 src/Mod/TechDraw/App/DrawComplexSection.h create mode 100644 src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg create mode 100644 src/Mod/TechDraw/Gui/TaskComplexSection.cpp create mode 100644 src/Mod/TechDraw/Gui/TaskComplexSection.h create mode 100644 src/Mod/TechDraw/Gui/TaskComplexSection.ui 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 #endif #include @@ -58,21 +80,32 @@ #include #include -#include "DrawViewSection.h" +#include + #include "DrawGeomHatch.h" #include "DrawHatch.h" #include "DrawProjGroupItem.h" +#include "DrawProjGroupItem.h" #include "DrawUtil.h" +#include "DrawUtil.h" +#include "EdgeWalker.h" +#include "Geometry.h" #include "Geometry.h" #include "GeometryObject.h" +#include "GeometryObject.h" +#include "HatchLine.h" +#include "DrawViewSection.h" using namespace TechDraw; +using DU = DrawUtil; + const char* DrawViewSection::SectionDirEnums[]= {"Right", "Left", "Up", "Down", + "Aligned", nullptr}; const char* DrawViewSection::CutSurfaceEnums[]= {"Hide", @@ -88,7 +121,8 @@ const char* DrawViewSection::CutSurfaceEnums[]= {"Hide", PROPERTY_SOURCE(TechDraw::DrawViewSection, TechDraw::DrawViewPart) DrawViewSection::DrawViewSection() : - m_waitingForCut(false) + m_waitingForCut(false), + m_shapeSize(0.0) { static const char *sgroup = "Section"; static const char *fgroup = "Cut Surface Format"; @@ -200,29 +234,37 @@ void DrawViewSection::onChanged(const App::Property* prop) DrawView::onChanged(prop); } +TopoDS_Shape DrawViewSection::getShapeToCut() +{ +// Base::Console().Message("DVS::getShapeToCut()\n"); + App::DocumentObject* base = BaseView.getValue(); + TechDraw::DrawViewPart* dvp = nullptr; + if (!base || + !base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { + //this can probably only happen with scripting + return TopoDS_Shape(); + } else { + dvp = static_cast(base); + } + TopoDS_Shape shapeToCut = dvp->getSourceShape(); + if (FuseBeforeCut.getValue()) { + shapeToCut = dvp->getSourceShapeFused(); + } + return shapeToCut; +} + App::DocumentObjectExecReturn *DrawViewSection::execute() { +// Base::Console().Message("DVS::execute() - %s\n", getNameInDocument()); if (!keepUpdated()) { return App::DocumentObject::StdReturn; } - App::DocumentObject* base = BaseView.getValue(); - if (!base) { + if (!isBaseValid()) { return new App::DocumentObjectExecReturn("BaseView object not found"); } - TechDraw::DrawViewPart* dvp = nullptr; - if (!base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - //this can probably only happen with scripting - return new App::DocumentObjectExecReturn("BaseView object is not a DrawViewPart object"); - } else { - dvp = static_cast(base); - } - - TopoDS_Shape baseShape = dvp->getSourceShape(); - if (FuseBeforeCut.getValue()) { - baseShape = dvp->getSourceShapeFused(); - } + TopoDS_Shape baseShape = getShapeToCut(); if (baseShape.IsNull()) { return DrawView::execute(); @@ -230,7 +272,6 @@ App::DocumentObjectExecReturn *DrawViewSection::execute() m_saveShape = baseShape; //save shape for 2nd pass -// checkXDirection(); bool haveX = checkXDirection(); if (!haveX) { //block touch/onChanged stuff @@ -246,6 +287,16 @@ App::DocumentObjectExecReturn *DrawViewSection::execute() return DrawView::execute(); } +bool DrawViewSection::isBaseValid() const +{ + App::DocumentObject* base = BaseView.getValue(); + if (base && + base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { + return true; + } + return false; +} + void DrawViewSection::sectionExec(TopoDS_Shape& baseShape) { // Base::Console().Message("DVS::sectionExec() - %s baseShape.IsNull: %d\n", @@ -289,112 +340,128 @@ void DrawViewSection::makeSectionCut(TopoDS_Shape &baseShape) Bnd_Box centerBox; BRepBndLib::AddOptimal(baseShape, centerBox); centerBox.SetGap(0.0); - gp_Pln pln = getSectionPlane(); - gp_Dir gpNormal = pln.Axis().Direction(); Base::Vector3d orgPnt = SectionOrigin.getValue(); if(!isReallyInBox(gp_Pnt(orgPnt.x, orgPnt.y, orgPnt.z), centerBox)) { Base::Console().Warning("DVS: SectionOrigin doesn't intersect part in %s\n", getNameInDocument()); } - - // make cutting tool - // Make the extrusion face - double dMax = sqrt(centerBox.SquareExtent()); - BRepBuilderAPI_MakeFace mkFace(pln, -dMax, dMax, -dMax, dMax); - TopoDS_Face aProjFace = mkFace.Face(); - if(aProjFace.IsNull()) { - Base::Console().Warning("DVS: Section face is NULL in %s\n", getNameInDocument()); - return; - } - gp_Vec extrudeDir = dMax * gp_Vec(gpNormal); - TopoDS_Shape prism = BRepPrimAPI_MakePrism(aProjFace, extrudeDir, false, true).Shape(); + m_shapeSize = sqrt(centerBox.SquareExtent()); // We need to copy the shape to not modify the BRepstructure BRepBuilderAPI_Copy BuilderCopy(baseShape); TopoDS_Shape myShape = BuilderCopy.Shape(); m_saveShape = myShape; //save shape for 2nd pass + if (debugSection()) { + BRepTools::Write(myShape, "DVSCopy.brep"); //debug + } + + m_cuttingTool = makeCuttingTool(m_shapeSize); + + if (debugSection()) { + BRepTools::Write(m_cuttingTool, "DVSTool.brep"); //debug + } + // perform cut BRep_Builder builder; - TopoDS_Compound pieces; - builder.MakeCompound(pieces); + TopoDS_Compound cutPieces; + builder.MakeCompound(cutPieces); TopExp_Explorer expl(myShape, TopAbs_SOLID); - int indb = 0; - int outdb = 0; for (; expl.More(); expl.Next()) { - indb++; const TopoDS_Solid& s = TopoDS::Solid(expl.Current()); - BRepAlgoAPI_Cut mkCut(s, prism); + BRepAlgoAPI_Cut mkCut(s, m_cuttingTool); if (!mkCut.IsDone()) { Base::Console().Warning("DVS: Section cut has failed in %s\n", getNameInDocument()); continue; } - TopoDS_Shape cut = mkCut.Shape(); - builder.Add(pieces, cut); - outdb++; + builder.Add(cutPieces, mkCut.Shape()); } - // pieces contains result of cutting each subshape in baseShape with tool - TopoDS_Shape rawShape = pieces; + // cutPieces contains result of cutting each subshape in baseShape with tool + m_cutPieces = cutPieces; if (debugSection()) { - BRepTools::Write(myShape, "DVSCopy.brep"); //debug - BRepTools::Write(aProjFace, "DVSFace.brep"); //debug - BRepTools::Write(prism, "DVSTool.brep"); //debug - BRepTools::Write(pieces, "DVSPieces.brep"); //debug + BRepTools::Write(cutPieces, "DVSCutPieces.brep"); //debug } // check for error in cut Bnd_Box testBox; - BRepBndLib::AddOptimal(rawShape, testBox); + BRepBndLib::AddOptimal(m_cutPieces, testBox); testBox.SetGap(0.0); if (testBox.IsVoid()) { //prism & input don't intersect. rawShape is garbage, don't bother. Base::Console().Warning("DVS::makeSectionCut - prism & input don't intersect - %s\n", Label.getValue()); return; - } -// build display geometry as in DVP, with minor mods - TopoDS_Shape centeredShape; + if (debugSection()) { + BRepTools::Write(m_cutPieces, "DVSRawShapeAfter.brep"); //debug + } + + waitingForCut(false); +} + +//position, scale and rotate shape for buildGeometryObject +TopoDS_Shape DrawViewSection::prepareShape(const TopoDS_Shape& rawShape, + double shapeSize) +{ +// Base::Console().Message("DVS::prepareShape - %s - rawShape.IsNull: %d shapeSize: %.3f\n", +// getNameInDocument(), rawShape.IsNull(), shapeSize); + (void) shapeSize; //shapeSize is not used in this base class, but is interesting for + //derived classes + // build display geometry as in DVP, with minor mods + TopoDS_Shape preparedShape; try { Base::Vector3d origin(0.0, 0.0, 0.0); - m_viewAxis = getProjectionCS(origin); + m_projectionCS = getProjectionCS(origin); gp_Pnt inputCenter; inputCenter = TechDraw::findCentroid(rawShape, - m_viewAxis); + m_projectionCS); Base::Vector3d centroid(inputCenter.X(), inputCenter.Y(), inputCenter.Z()); - centeredShape = TechDraw::moveShape(rawShape, + preparedShape = TechDraw::moveShape(rawShape, centroid * -1.0); - m_cutShape = centeredShape; + m_cutShape = preparedShape; m_saveCentroid = centroid; - m_scaledShape = TechDraw::scaleShape(centeredShape, - getScale()); + preparedShape = TechDraw::scaleShape(preparedShape, + getScale()); if (!DrawUtil::fpCompare(Rotation.getValue(), 0.0)) { - m_scaledShape = TechDraw::rotateShape(m_scaledShape, - m_viewAxis, + preparedShape = TechDraw::rotateShape(preparedShape, + m_projectionCS, Rotation.getValue()); } if (debugSection()) { BRepTools::Write(m_cutShape, "DVSmCutShape.brep"); //debug - BRepTools::Write(m_scaledShape, "DVSScaled.brep"); //debug // DrawUtil::dumpCS("DVS::makeSectionCut - CS to GO", viewAxis); } - m_rawShape = rawShape; //save for section face finding - } catch (Standard_Failure& e1) { - Base::Console().Warning("DVS::makeSectionCut - failed to build base shape %s - %s **\n", + Base::Console().Warning("DVS::prepareShape - failed to build shape %s - %s **\n", getNameInDocument(), e1.GetMessageString()); - return; } + return preparedShape; +} - waitingForCut(false); +TopoDS_Shape DrawViewSection::makeCuttingTool(double shapeSize) +{ +// Base::Console().Message("DVS::makeCuttingTool(%.3f) - %s\n", shapeSize, getNameInDocument()); + // Make the extrusion face + gp_Pln pln = getSectionPlane(); + gp_Dir gpNormal = pln.Axis().Direction(); + BRepBuilderAPI_MakeFace mkFace(pln, -shapeSize, shapeSize, -shapeSize, shapeSize); + TopoDS_Face aProjFace = mkFace.Face(); + if(aProjFace.IsNull()) { + return TopoDS_Shape(); + } + if (debugSection()){ + BRepTools::Write(aProjFace, "DVSSectionFace.brep"); //debug + } + gp_Vec extrudeDir = shapeSize * gp_Vec(gpNormal); + return BRepPrimAPI_MakePrism(aProjFace, extrudeDir, false, true).Shape(); } void DrawViewSection::onSectionCutFinished() @@ -404,10 +471,15 @@ void DrawViewSection::onSectionCutFinished() showProgressMessage(getNameInDocument(), "has finished making section cut"); + m_preparedShape = prepareShape(getShapeToPrepare(), m_shapeSize); + if (debugSection()) { + BRepTools::Write(m_preparedShape, "DVSPreparedShape.brep"); //debug + } + postSectionCutTasks(); //display geometry for cut shape is in geometryObject as in DVP - m_tempGeometryObject = buildGeometryObject(m_scaledShape, m_viewAxis); + m_tempGeometryObject = buildGeometryObject(m_preparedShape, getSectionCS()); } //activities that depend on updated geometry object @@ -430,78 +502,44 @@ void DrawViewSection::postHlrTasks(void) // build section face geometry - TopoDS_Compound faceIntersections = findSectionPlaneIntersections(m_rawShape); + TopoDS_Compound faceIntersections = findSectionPlaneIntersections(getShapeToIntersect()); if (faceIntersections.IsNull()) { requestPaint(); return; } - TopoDS_Shape centeredShapeF = TechDraw::moveShape(faceIntersections, + TopoDS_Shape centeredFaces = TechDraw::moveShape(faceIntersections, m_saveCentroid * -1.0); - TopoDS_Shape scaledSection = TechDraw::scaleShape(centeredShapeF, + TopoDS_Shape scaledSection = TechDraw::scaleShape(centeredFaces, getScale()); if (!DrawUtil::fpCompare(Rotation.getValue(), 0.0)) { scaledSection = TechDraw::rotateShape(scaledSection, - m_viewAxis, + getProjectionCS(), Rotation.getValue()); } if (debugSection()) { - BRepTools::Write(scaledSection, "DVSScaledFaces.brep"); //debug + BRepTools::Write(faceIntersections, "DVSFaceIntersections.brep"); //debug } - // scaledSection is compound of TopoDS_Face intersections, but aligned to pln(origin, sectionNormal) - // needs to be aligned to pln (origin, stdZ); - gp_Ax3 R3; - gp_Ax2 projCS = getSectionCS(); - gp_Ax3 proj3 = gp_Ax3(gp_Pnt(0.0, 0.0, 0.0), - projCS.Direction(), - projCS.XDirection()); - gp_Trsf fromR3; - fromR3.SetTransformation(R3, proj3); - BRepBuilderAPI_Transform xformer(fromR3); - xformer.Perform(scaledSection, true); - if (xformer.IsDone()) { - sectionTopoDSFaces = TopoDS::Compound(xformer.Shape()); - } else { - Base::Console().Message("DVS::sectionExec - face xform failed\n"); + m_sectionTopoDSFaces = alignSectionFaces(faceIntersections); + if (debugSection()) { + BRepTools::Write(m_sectionTopoDSFaces, "DVSTopoSectionFaces.brep"); //debug } + m_tdSectionFaces = makeTDSectionFaces(m_sectionTopoDSFaces); - sectionTopoDSFaces = TopoDS::Compound(GeometryObject::invertGeometry(sectionTopoDSFaces)); //handle Qt -y - - //turn section faces into TD geometry - tdSectionFaces.clear(); - TopExp_Explorer sectionExpl(sectionTopoDSFaces, TopAbs_FACE); - for (; sectionExpl.More(); sectionExpl.Next()) { - const TopoDS_Face& face = TopoDS::Face(sectionExpl.Current()); - TechDraw::FacePtr sectionFace(std::make_shared()); - TopExp_Explorer expFace(face, TopAbs_WIRE); - for ( ; expFace.More(); expFace.Next()) { - TechDraw::Wire* w = new TechDraw::Wire(); - const TopoDS_Wire& wire = TopoDS::Wire(expFace.Current()); - TopExp_Explorer expWire(wire, TopAbs_EDGE); - for ( ; expWire.More(); expWire.Next()) { - const TopoDS_Edge& edge = TopoDS::Edge(expWire.Current()); - TechDraw::BaseGeomPtr e = BaseGeom::baseFactory(edge); - if (e) { - w->geoms.push_back(e); - } - } - sectionFace->wires.push_back(w); - } - tdSectionFaces.push_back(sectionFace); - } TechDraw::DrawViewPart* dvp = dynamic_cast(BaseView.getValue()); if (dvp) { dvp->requestPaint(); //to refresh section line } - requestPaint(); + requestPaint(); //this will be a duplicate paint if we are making a standalone ComplexSection } //activities that depend on a valid section cut void DrawViewSection::postSectionCutTasks() { +// Base::Console().Message("DVS::postSectionCutTasks()\n"); std::vector children = getInList(); for (auto& c: children) { if (c->getTypeId().isDerivedFrom(DrawViewPart::getClassTypeId())) { @@ -529,16 +567,24 @@ gp_Pln DrawViewSection::getSectionPlane() const } //! tries to find the intersection of the section plane with the shape giving a collection of planar faces +//! the original algo finds the intersections first then transforms them to match the centered, rotated +//! and scaled cut shape. 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. TopoDS_Compound DrawViewSection::findSectionPlaneIntersections(const TopoDS_Shape& shape) { // Base::Console().Message("DVS::findSectionPlaneIntersections() - %s\n", getNameInDocument()); if(shape.IsNull()){ // this shouldn't happen - Base::Console().Warning("DrawViewSection::findSectionPlaneInter - %s - input shape is Null\n", getNameInDocument()); +// Base::Console().Warning("DrawViewSection::findSectionPlaneInter - %s - input shape is Null\n", getNameInDocument()); return TopoDS_Compound(); } gp_Pln plnSection = getSectionPlane(); + if (debugSection()) { + BRepBuilderAPI_MakeFace mkFace(plnSection, -m_shapeSize, m_shapeSize, -m_shapeSize, m_shapeSize); + BRepTools::Write(mkFace.Face(), "DVSSectionPlane.brep"); //debug + BRepTools::Write(shape, "DVSShapeToIntersect.brep)"); + } BRep_Builder builder; TopoDS_Compound result; builder.MakeCompound(result); @@ -558,6 +604,109 @@ TopoDS_Compound DrawViewSection::findSectionPlaneIntersections(const TopoDS_Shap return result; } +//move section faces to line up with cut shape +TopoDS_Compound DrawViewSection::alignSectionFaces(TopoDS_Shape faceIntersections) +{ +// Base::Console().Message("DVS::alignSectionFaces()\n"); + TopoDS_Compound sectionFaces; + TopoDS_Shape centeredShape = TechDraw::moveShape(faceIntersections, + getOriginalCentroid() * -1.0); + + TopoDS_Shape scaledSection = TechDraw::scaleShape(centeredShape, + getScale()); + if (!DrawUtil::fpCompare(Rotation.getValue(), 0.0)) { + scaledSection = TechDraw::rotateShape(scaledSection, + getSectionCS(), + Rotation.getValue()); + } + if (debugSection()) { + BRepTools::Write(scaledSection, "DVSScaledFaces.brep"); //debug + } + + return mapToPage(scaledSection); +} + +TopoDS_Compound DrawViewSection::mapToPage(TopoDS_Shape& shapeToAlign) +{ + // shapeToAlign is compound of TopoDS_Face intersections, but aligned to pln(origin, sectionNormal) + // needs to be aligned to paper plane (origin, stdZ); + //project the faces in the shapeToAlign, build new faces from the resulting wires and + //combine everything into a compound of faces + + BRep_Builder builder; + TopoDS_Compound result; + builder.MakeCompound(result); + + TopExp_Explorer expFace(shapeToAlign, TopAbs_FACE); + for (int iFace = 1; expFace.More(); expFace.Next(), iFace++) { + const TopoDS_Face& face = TopoDS::Face(expFace.Current()); + std::vector faceWires; + TopExp_Explorer expWires(face, TopAbs_WIRE); + for ( ; expWires.More(); expWires.Next()) { + const TopoDS_Wire& wire = TopoDS::Wire(expWires.Current()); + TopoDS_Shape projectedShape = GeometryObject::projectSimpleShape(wire, getSectionCS()); + std::vector wireEdges; + //projectedShape is just a bunch of edges. we have to rebuild the wire. + TopExp_Explorer expEdges(projectedShape, TopAbs_EDGE); + for ( ; expEdges.More(); expEdges.Next()) { + const TopoDS_Edge& edge = TopoDS::Edge(expEdges.Current()); + wireEdges.push_back(edge); + } + TopoDS_Wire cleanWire = EdgeWalker::makeCleanWire(wireEdges, 2.0 * EWTOLERANCE); + faceWires.push_back(cleanWire); + } + if (debugSection()) { + std::stringstream ss; + ss << "DVSFaceWires" << iFace << ".brep"; + BRepTools::Write(DrawUtil::vectorToCompound(faceWires), ss.str().c_str()); //debug + } + //first wire should be the outer boundary of the face + BRepBuilderAPI_MakeFace mkFace(faceWires.front()); + int wireCount = faceWires.size(); + for (int iWire = 1; iWire < wireCount; iWire++) { + //make holes in the face with the rest of the wires + mkFace.Add(faceWires.at(iWire)); + } + builder.Add(result, mkFace.Face()); + if (debugSection()) { + std::stringstream ss; + ss << "DVSFaceFromWires" << iFace << ".brep"; + BRepTools::Write(mkFace.Face(), ss.str().c_str()); //debug + } + } + + return result; +} + +//turn OCC section faces into TD geometry +std::vector DrawViewSection::makeTDSectionFaces(TopoDS_Compound topoDSFaces) +{ +// Base::Console().Message("DVS::makeTDSectionFaces()\n"); + std::vector tdSectionFaces; + TopExp_Explorer sectionExpl(topoDSFaces, TopAbs_FACE); + for (; sectionExpl.More(); sectionExpl.Next()) { + const TopoDS_Face& face = TopoDS::Face(sectionExpl.Current()); + TechDraw::FacePtr sectionFace(std::make_shared()); + TopExp_Explorer expFace(face, TopAbs_WIRE); + for ( ; expFace.More(); expFace.Next()) { + TechDraw::Wire* w = new TechDraw::Wire(); + const TopoDS_Wire& wire = TopoDS::Wire(expFace.Current()); + TopExp_Explorer expWire(wire, TopAbs_EDGE); + for ( ; expWire.More(); expWire.Next()) { + const TopoDS_Edge& edge = TopoDS::Edge(expWire.Current()); + TechDraw::BaseGeomPtr e = BaseGeom::baseFactory(edge); + if (e) { + w->geoms.push_back(e); + } + } + sectionFace->wires.push_back(w); + } + tdSectionFaces.push_back(sectionFace); + } + + return tdSectionFaces; +} + //calculate the ends of the section line in BaseView's coords std::pair DrawViewSection::sectionLineEnds() { @@ -662,8 +811,6 @@ void DrawViewSection::setCSFromBase(const std::string sectionName) gp_Ax2 DrawViewSection::getCSFromBase(const std::string sectionName) const { // Base::Console().Message("DVS::getCSFromBase(%s)\n", sectionName.c_str()); - Base::Vector3d sectionNormal; - Base::Vector3d sectionXDir; Base::Vector3d origin(0.0, 0.0, 0.0); Base::Vector3d sectOrigin = SectionOrigin.getValue(); @@ -693,6 +840,12 @@ gp_Ax2 DrawViewSection::getCSFromBase(const std::string sectionName) const } else if (sectionName == "Right") { dvsDir = dvpRight.Reversed(); dvsXDir = dvpDir; + } else if (sectionName == "Aligned") { + //if aligned, we don't get our direction from the base view + Base::Vector3d sectionNormal = SectionNormal.getValue(); + dvsDir = gp_Dir(sectionNormal.x, sectionNormal.y, sectionNormal.z); + Base::Vector3d sectionXDir = XDirection.getValue(); + dvsXDir = gp_Dir(sectionXDir.x, sectionXDir.y, sectionXDir.z); } else { Base::Console().Log("Error - DVS::getCSFromBase - bad sectionName: %s\n", sectionName.c_str()); dvsDir = dvpRight; @@ -752,7 +905,7 @@ std::vector DrawViewSection::getDrawableLines(int i) TopoDS_Face DrawViewSection::getSectionTopoDSFace(int i) { TopoDS_Face result; - TopExp_Explorer expl(sectionTopoDSFaces, TopAbs_FACE); + TopExp_Explorer expl(m_sectionTopoDSFaces, TopAbs_FACE); int count = 1; for (; expl.More(); expl.Next(), count++) { if (count == i+1) { diff --git a/src/Mod/TechDraw/App/DrawViewSection.h b/src/Mod/TechDraw/App/DrawViewSection.h index b7092a3616..fbd28fabdd 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.h +++ b/src/Mod/TechDraw/App/DrawViewSection.h @@ -94,26 +94,37 @@ public: short mustExecute() const override; void sectionExec(TopoDS_Shape& s); - void makeSectionCut(TopoDS_Shape &baseShape); + virtual void makeSectionCut(TopoDS_Shape &baseShape); void postHlrTasks(void) override; virtual void postSectionCutTasks(); void waitingForCut(bool s) { m_waitingForCut = s; } bool waitingForCut(void) const { return m_waitingForCut; } bool waitingForResult() const override; - std::vector getTDFaceGeometry() {return tdSectionFaces;} - + virtual TopoDS_Shape makeCuttingTool(double shapeSize); + virtual TopoDS_Shape getShapeToCut(); + virtual bool isBaseValid() const; + virtual TopoDS_Shape prepareShape(const TopoDS_Shape& rawShape, + double shapeSize); + virtual TopoDS_Shape getShapeToPrepare() const { return m_cutPieces; } + //CS related methods void setCSFromBase(const std::string sectionName); - gp_Ax2 getCSFromBase(const std::string sectionName) const; - + virtual gp_Ax2 getCSFromBase(const std::string sectionName) const; gp_Ax2 getSectionCS() const; Base::Vector3d getXDirection() const override; //don't use XDirection.getValue() TechDraw::DrawViewPart* getBaseDVP() const; TechDraw::DrawProjGroupItem* getBaseDPGI() const; - TopoDS_Compound getSectionTFaces() { return sectionTopoDSFaces;} + //section face related methods + TopoDS_Compound getSectionTFaces() { return m_sectionTopoDSFaces;} + std::vector getTDFaceGeometry() {return m_tdSectionFaces;} TopoDS_Face getSectionTopoDSFace(int i); + virtual TopoDS_Compound alignSectionFaces(TopoDS_Shape faceIntersections); + TopoDS_Compound mapToPage(TopoDS_Shape& shapeToAlign); + virtual std::vector makeTDSectionFaces(TopoDS_Compound topoDSFaces); + virtual TopoDS_Shape getShapeToIntersect() { return m_cutPieces; } + void makeLineSets(void) ; std::vector getDrawableLines(int i = 0); std::vector getDecodedSpecsFromFile(std::string fileSpec, std::string myPattern); @@ -123,7 +134,7 @@ public: static const char* SectionDirEnums[]; static const char* CutSurfaceEnums[]; - std::pair sectionLineEnds(); + virtual std::pair sectionLineEnds(); bool showSectionEdges(void); @@ -131,13 +142,13 @@ public Q_SLOTS: void onSectionCutFinished(void); protected: - TopoDS_Compound sectionTopoDSFaces; //needed for hatching + TopoDS_Compound m_sectionTopoDSFaces; //needed for hatching std::vector m_lineSets; - std::vector tdSectionFaces; + std::vector m_tdSectionFaces; - gp_Pln getSectionPlane() const; - TopoDS_Compound findSectionPlaneIntersections(const TopoDS_Shape& shape); + virtual gp_Pln getSectionPlane() const; + virtual TopoDS_Compound findSectionPlaneIntersections(const TopoDS_Shape& shape); void getParameters(); bool debugSection() const; int prefCutSurface() const; @@ -151,15 +162,16 @@ protected: void replaceSvgIncluded(std::string newSvgFile); void replacePatIncluded(std::string newPatFile); - TopoDS_Shape m_rawShape; - gp_Ax2 m_viewAxis; - TopoDS_Shape m_scaledShape; + TopoDS_Shape m_cutPieces; //the shape after cutting, but before centering & scaling + gp_Ax2 m_projectionCS; + TopoDS_Shape m_preparedShape; //the shape after cutting, centering, scaling etc QMetaObject::Connection connectCutWatcher; QFutureWatcher m_cutWatcher; QFuture m_cutFuture; bool m_waitingForCut; - + TopoDS_Shape m_cuttingTool; + double m_shapeSize; }; using DrawViewSectionPython = App::FeaturePythonT; diff --git a/src/Mod/TechDraw/App/DrawViewSpreadsheet.cpp b/src/Mod/TechDraw/App/DrawViewSpreadsheet.cpp index 3113cc3219..5636538f63 100644 --- a/src/Mod/TechDraw/App/DrawViewSpreadsheet.cpp +++ b/src/Mod/TechDraw/App/DrawViewSpreadsheet.cpp @@ -24,9 +24,9 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include +#include +#include +#include #endif #include @@ -34,10 +34,10 @@ #include #include -#include "DrawViewSpreadsheet.h" #include "DrawUtil.h" #include "Preferences.h" +#include "DrawViewSpreadsheet.h" using namespace TechDraw; diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp index 6cd59fd153..cf347049b6 100644 --- a/src/Mod/TechDraw/App/Geometry.cpp +++ b/src/Mod/TechDraw/App/Geometry.cpp @@ -1246,8 +1246,8 @@ bool BSpline::isLine() return false; } - Base::Vector3d vs = DrawUtil::gpPnt2V3(s); - Base::Vector3d ve = DrawUtil::gpPnt2V3(e); + Base::Vector3d vs = DrawUtil::toVector3d(s); + Base::Vector3d ve = DrawUtil::toVector3d(e); double endLength = (vs - ve).Length(); int low = 0; int high = spline->NbPoles() - 1; @@ -1256,9 +1256,9 @@ bool BSpline::isLine() double lenTotal = 0.0; for (int i = 0; i < high; i++) { gp_Pnt p1 = poles(i); - Base::Vector3d v1 = DrawUtil::gpPnt2V3(p1); + Base::Vector3d v1 = DrawUtil::toVector3d(p1); gp_Pnt p2 = poles(i+1); - Base::Vector3d v2 = DrawUtil::gpPnt2V3(p2); + Base::Vector3d v2 = DrawUtil::toVector3d(p2); lenTotal += (v2-v1).Length(); } @@ -1415,7 +1415,7 @@ TopoDS_Edge BSpline::asCircle(bool& arc) gp_Circ circle1 = gce_circ1.Value(); double radius1 = circle1.Radius(); gp_Pnt center1 = circle1.Location(); - Base::Vector3d vc1 = DrawUtil::gpPnt2V3(center1); + Base::Vector3d vc1 = DrawUtil::toVector3d(center1); gce_MakeCirc gce_circ2 = gce_MakeCirc(pcm, pc2, e); if (gce_circ2.Status() != gce_Done) { @@ -1424,7 +1424,7 @@ TopoDS_Edge BSpline::asCircle(bool& arc) gp_Circ circle2 = gce_circ2.Value(); double radius2 = circle2.Radius(); gp_Pnt center2 = circle2.Location(); - Base::Vector3d vc2 = DrawUtil::gpPnt2V3(center2); + Base::Vector3d vc2 = DrawUtil::toVector3d(center2); // compare radii & centers double allowError = 0.001; //mm^-3 good enough for printing diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp index 3c9f089f14..80a50ad969 100644 --- a/src/Mod/TechDraw/App/GeometryObject.cpp +++ b/src/Mod/TechDraw/App/GeometryObject.cpp @@ -24,13 +24,20 @@ #ifndef _PreComp_ #include -#include +#include #include #include #include +#include #include +#include +#include #include +#include +#include +#include #include +#include #include #include #include @@ -39,17 +46,17 @@ #include #include #include -#include +#include #include #include +#include #include -#include #include -#include #include -#include +#include #include #include +#include #include #include #endif // #ifndef _PreComp_ @@ -70,6 +77,8 @@ using namespace TechDraw; using namespace std; +using DU = DrawUtil; + struct EdgePoints { gp_Pnt v1, v2; TopoDS_Edge edge; @@ -180,6 +189,7 @@ void GeometryObject::projectShape(const TopoDS_Shape& inShape, visHard = hlrToShape.VCompound(); BRepLib::BuildCurves3d(visHard); visHard = invertGeometry(visHard); + BRepTools::Write(visHard, "GOvisHard.brep"); //debug } if (!hlrToShape.Rg1LineVCompound().IsNull()) { @@ -417,6 +427,46 @@ void GeometryObject::projectShapeWithPolygonAlgo(const TopoDS_Shape& input, makeTDGeometry(); } +//project the edges in shape onto XY.mirrored plane of CS. mimics the projection +//of the main hlr routine. Only the visible hard edges are returned, so this method +//is only suitable for simple shapes that have no hidden edges, like faces or wires. +//TODO: allow use of perspective projector +TopoDS_Shape GeometryObject::projectSimpleShape(const TopoDS_Shape &shape, + const gp_Ax2 &CS) +{ +// Base::Console().Message("GO::()\n"); + if(shape.IsNull()) { + throw Base::ValueError("GO::projectSimpleShape - input shape is NULL"); + } + + HLRBRep_Algo *brep_hlr = new HLRBRep_Algo(); + brep_hlr->Add(shape); + HLRAlgo_Projector projector( CS ); + brep_hlr->Projector(projector); + brep_hlr->Update(); + brep_hlr->Hide(); + + HLRBRep_HLRToShape hlrToShape(brep_hlr); + TopoDS_Shape hardEdges = hlrToShape.VCompound(); + BRepLib::BuildCurves3d(hardEdges); + hardEdges = invertGeometry(hardEdges); + + return hardEdges; +} + +//project the edges of a shape onto the XY plane of projCS. This does not give +//the same result as the hlr projections +TopoDS_Shape GeometryObject::simpleProjection(const TopoDS_Shape& shape, + const gp_Ax2& projCS) +{ + gp_Pln plane(projCS); + TopoDS_Face paper = BRepBuilderAPI_MakeFace(plane); + BRepAlgo_NormalProjection projector(paper); + projector.Add(shape); + projector.Build(); + return projector.Projection(); +} + TopoDS_Shape GeometryObject::projectFace(const TopoDS_Shape &face, const gp_Ax2 &CS) { @@ -510,11 +560,11 @@ void GeometryObject::addGeomFromCompound(TopoDS_Shape edgeCompound, edgeClass ca Base::Console().Log("GO::addGeomFromCompound - edge: %d is NULL\n", i); continue; } - if (DrawUtil::isZeroEdge(edge)) { + if (DU::isZeroEdge(edge)) { Base::Console().Log("GO::addGeomFromCompound - edge: %d is zeroEdge\n", i); continue; } - if (DrawUtil::isCrazy(edge)) { + if (DU::isCrazy(edge)) { Base::Console().Log("GO::addGeomFromCompound - edge: %d is crazy\n", i); continue; } @@ -860,7 +910,7 @@ gp_Ax2 TechDraw::getViewAxis(const Base::Vector3d origin, Base::Vector3d stdZ(0.0, 0.0, 1.0); Base::Vector3d stdOrg(0.0, 0.0, 0.0); Base::Vector3d cross = direction; - if (TechDraw::DrawUtil::checkParallel(direction, stdZ)) { + if (DU::checkParallel(direction, stdZ)) { cross = Base::Vector3d(1.0, 0.0, 0.0); } else { cross.Normalize(); @@ -913,7 +963,7 @@ gp_Ax2 TechDraw::legacyViewAxis1(const Base::Vector3d origin, } Base::Vector3d cross = flipDirection; // //special case - if (TechDraw::DrawUtil::checkParallel(flipDirection, stdZ)) { + if (DU::checkParallel(flipDirection, stdZ)) { cross = Base::Vector3d(1.0, 0.0, 0.0); } else { cross.Normalize(); @@ -940,6 +990,23 @@ gp_Ax2 TechDraw::legacyViewAxis1(const Base::Vector3d origin, return viewAxis; } +//! Returns the centroid of shape based on R3 +gp_Pnt TechDraw::findCentroid(const TopoDS_Shape& shape) +{ + Bnd_Box tBounds; + tBounds.SetGap(0.0); + BRepBndLib::AddOptimal(shape, tBounds, true, false); + + Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + tBounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); + + Standard_Real x = (xMin + xMax) / 2.0, + y = (yMin + yMax) / 2.0, + z = (zMin + zMax) / 2.0; + + return gp_Pnt(x, y, z); +} + //! Returns the centroid of shape, as viewed according to direction gp_Pnt TechDraw::findCentroid(const TopoDS_Shape &shape, const Base::Vector3d &direction) @@ -955,8 +1022,6 @@ gp_Pnt TechDraw::findCentroid(const TopoDS_Shape &shape, const gp_Ax2 &viewAxis) { // Base::Console().Message("GO::findCentroid() - 2\n"); -// Base::Vector3d origin(0.0, 0.0, 0.0); -// gp_Ax2 viewAxis = getViewAxis(origin, direction); gp_Trsf tempTransform; tempTransform.SetTransformation(viewAxis); @@ -1020,7 +1085,7 @@ TopoDS_Shape TechDraw::mirrorShape(const TopoDS_Shape &input, // mirror about the Y axis gp_Trsf tempTransform; //BRepBuilderAPI_Transform will loop forever if asked to use 0.0 as scale - if (!(scale > 0.0)) { + if (scale <= 0.0) { tempTransform.SetScale(inputCenter, 1.0); } else { tempTransform.SetScale(inputCenter, scale); @@ -1042,8 +1107,8 @@ TopoDS_Shape TechDraw::mirrorShape(const TopoDS_Shape &input, //!rotates a shape about a viewAxis TopoDS_Shape TechDraw::rotateShape(const TopoDS_Shape &input, - gp_Ax2& viewAxis, - double rotAngle) + const gp_Ax2 &viewAxis, + double rotAngle) { TopoDS_Shape transShape; if (input.IsNull()) { @@ -1103,3 +1168,62 @@ TopoDS_Shape TechDraw::moveShape(const TopoDS_Shape &input, } return transShape; } + + +//!moves a shape with restricts on directions +TopoDS_Shape TechDraw::moveShapeRestricted(const TopoDS_Shape &input, + const Base::Vector3d& motion, + bool allowX, + bool allowY, + bool allowZ) +{ + gp_Vec gMotion(allowX ? motion.x : 0.0, + allowY ? motion.y : 0.0, + allowZ ? motion.z : 0.0); + TopoDS_Shape transShape; + try { + gp_Trsf xlate; + xlate.SetTranslation(gMotion); + + BRepBuilderAPI_Transform mkTrf(input, xlate); + transShape = mkTrf.Shape(); + } + catch (...) { + Base::Console().Log("GeometryObject::moveShapeRestricted - move failed.\n"); + return transShape; + } + return transShape; +} + +//!moves a shape with restricts on directions +TopoDS_Shape TechDraw::moveShapeRestricted(const TopoDS_Shape &input, + const Base::Vector3d& motion, + const Base::Vector3d& mask) +{ + gp_Vec gMotion(mask.x ? motion.x : 0.0, + mask.y ? motion.y : 0.0, + mask.z ? motion.z : 0.0); + + TopoDS_Shape transShape; + try { + gp_Trsf xlate; + xlate.SetTranslation(gMotion); + + BRepBuilderAPI_Transform mkTrf(input, xlate); + transShape = mkTrf.Shape(); + } + catch (...) { + Base::Console().Log("GeometryObject::moveShapeRestricted - move failed.\n"); + return transShape; + } + return transShape; +} + +TopoDS_Shape TechDraw::moveShapeRestricted(const TopoDS_Shape &input, + const gp_Vec& motion, + const gp_Vec& mask) +{ + return moveShapeRestricted(input, + DU::toVector3d(motion), + DU::toVector3d(mask)); +} diff --git a/src/Mod/TechDraw/App/GeometryObject.h b/src/Mod/TechDraw/App/GeometryObject.h index 82cecc0447..8ab04671b3 100644 --- a/src/Mod/TechDraw/App/GeometryObject.h +++ b/src/Mod/TechDraw/App/GeometryObject.h @@ -67,13 +67,24 @@ TopoDS_Shape TechDrawExport mirrorShape(const TopoDS_Shape &input, TopoDS_Shape TechDrawExport scaleShape(const TopoDS_Shape &input, double scale); TopoDS_Shape TechDrawExport rotateShape(const TopoDS_Shape &input, - gp_Ax2& viewAxis, - double rotAngle); + const gp_Ax2& viewAxis, + double rotAngle); TopoDS_Shape TechDrawExport moveShape(const TopoDS_Shape &input, const Base::Vector3d& motion); - +TopoDS_Shape TechDrawExport moveShapeRestricted(const TopoDS_Shape &input, + const Base::Vector3d& motion, + bool allowX = true, + bool allowY = true, + bool allowZ = true); +TopoDS_Shape TechDrawExport moveShapeRestricted(const TopoDS_Shape &input, + const Base::Vector3d& motion, + const Base::Vector3d& mask); +TopoDS_Shape TechDrawExport moveShapeRestricted(const TopoDS_Shape &input, + const gp_Vec& motion, + const gp_Vec& mask); //! Returns the centroid of shape, as viewed according to direction +gp_Pnt TechDrawExport findCentroid(const TopoDS_Shape& shape); gp_Pnt TechDrawExport findCentroid(const TopoDS_Shape &shape, const Base::Vector3d &direction); gp_Pnt TechDrawExport findCentroid(const TopoDS_Shape &shape, @@ -118,9 +129,12 @@ public: const gp_Ax2 &viewAxis); void projectShapeWithPolygonAlgo(const TopoDS_Shape &input, const gp_Ax2 &viewAxis); - TopoDS_Shape projectFace(const TopoDS_Shape &face, - const gp_Ax2 &CS); - + static TopoDS_Shape projectSimpleShape(const TopoDS_Shape &shape, + const gp_Ax2 &CS); + static TopoDS_Shape simpleProjection(const TopoDS_Shape& shape, + const gp_Ax2& projCS); + static TopoDS_Shape projectFace(const TopoDS_Shape &face, + const gp_Ax2 &CS); void makeTDGeometry(); void extractGeometry(edgeClass category, bool visible); void addFaceGeom(FacePtr f); diff --git a/src/Mod/TechDraw/App/PreCompiled.h b/src/Mod/TechDraw/App/PreCompiled.h index f922ddc982..526ec7f2d1 100644 --- a/src/Mod/TechDraw/App/PreCompiled.h +++ b/src/Mod/TechDraw/App/PreCompiled.h @@ -56,6 +56,7 @@ #include #include #include + #include #include #include diff --git a/src/Mod/TechDraw/App/Preferences.cpp b/src/Mod/TechDraw/App/Preferences.cpp index 59b87dbd80..aeae9fde3b 100644 --- a/src/Mod/TechDraw/App/Preferences.cpp +++ b/src/Mod/TechDraw/App/Preferences.cpp @@ -132,7 +132,28 @@ double Preferences::vertexScale() return result; } +int Preferences::scaleType() +{ + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/General"); + int result = hGrp->GetInt("DefaultScaleType", 0); + return result; +} +double Preferences::scale() +{ + int prefScaleType = scaleType(); + if (prefScaleType == 0) { //page scale + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/General"); + return hGrp->GetFloat("DefaultPageScale", 1.0); + } else if (prefScaleType == 1) { //custom scale + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/General"); + return hGrp->GetFloat("DefaultViewScale", 1.0); + } + return 1.0; +} //lightgray #D3D3D3 diff --git a/src/Mod/TechDraw/App/Preferences.h b/src/Mod/TechDraw/App/Preferences.h index aaafd5fa5e..9a61ec95df 100644 --- a/src/Mod/TechDraw/App/Preferences.h +++ b/src/Mod/TechDraw/App/Preferences.h @@ -52,7 +52,8 @@ static App::Color selectColor(); static App::Color preselectColor(); static App::Color vertexColor(); static double vertexScale(); - +static int scaleType(); +static double scale(); static bool useGlobalDecimals(); static bool keepPagesUpToDate(); diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index 3322504464..f67788e043 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -87,6 +87,7 @@ set(TechDrawGui_UIC_SRCS SymbolChooser.ui TaskMoveView.ui TaskProjection.ui + TaskComplexSection.ui ) if(BUILD_QT5) @@ -222,6 +223,9 @@ SET(TechDrawGui_SRCS TaskProjection.cpp TaskProjection.h TaskProjection.ui + TaskComplexSection.cpp + TaskComplexSection.h + TaskComplexSection.ui ) SET(TechDrawGuiView_SRCS @@ -419,6 +423,7 @@ SET(TechDrawGuiTaskDlgs_SRCS TaskCustomizeFormat.ui TaskMoveView.ui TaskProjection.ui + TaskComplexSection.ui ) SOURCE_GROUP("MRTE" FILES ${MRTE_SRCS}) diff --git a/src/Mod/TechDraw/Gui/Command.cpp b/src/Mod/TechDraw/Gui/Command.cpp index c880df5c6e..c634f17e58 100644 --- a/src/Mod/TechDraw/Gui/Command.cpp +++ b/src/Mod/TechDraw/Gui/Command.cpp @@ -30,8 +30,10 @@ #include #include #include + #include #include + #include #include #include @@ -43,14 +45,18 @@ #include #include #include + #include + +#include #include #include #include +#include #include #include +#include #include -#include #include #include #include @@ -62,6 +68,7 @@ #include "QGVPage.h" #include "Rez.h" #include "TaskActiveView.h" +#include "TaskComplexSection.h" #include "TaskDetail.h" #include "TaskProjGroup.h" #include "TaskProjection.h" @@ -512,6 +519,120 @@ bool CmdTechDrawSectionView::isActive() return (havePage && haveView && !taskInProgress); } +//=========================================================================== +// TechDraw_ComplexSection +//=========================================================================== + +DEF_STD_CMD_A(CmdTechDrawComplexSection) + +CmdTechDrawComplexSection::CmdTechDrawComplexSection() + : Command("TechDraw_ComplexSection") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert Complex Section"); + sToolTipText = QT_TR_NOOP("Insert a Complex Section"); + sWhatsThis = "TechDraw_ComplexSection"; + sStatusTip = sToolTipText; + sPixmap = "actions/TechDraw_ComplexSection"; +} + +void CmdTechDrawComplexSection::activated(int iMsg) +{ + Q_UNUSED(iMsg); + TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); + if (!page) { + return; + } + std::string PageName = page->getNameInDocument(); + + TechDraw::DrawViewPart* baseView(nullptr); + std::vector shapes; + std::vector xShapes; + App::DocumentObject* profileObject(nullptr); + std::vector profileSubs; + Gui::ResolveMode resolve = Gui::ResolveMode::OldStyleElement; //mystery + bool single = false; //mystery + auto selection = getSelection().getSelectionEx(nullptr, + App::DocumentObject::getClassTypeId(), + resolve, + single); + for (auto& sel: selection) { + bool is_linked = false; + auto obj = sel.getObject(); + if (obj->isDerivedFrom(TechDraw::DrawPage::getClassTypeId())) { + continue; + } + if (obj->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { + //use the dvp's Sources as sources for this ComplexSection & + //check the subelement(s) to see if they can be used as a profile + baseView = static_cast(obj); + if (!sel.getSubNames().empty()) { + //need to add profile subs as parameter + profileObject = baseView; + profileSubs = sel.getSubNames(); + } + continue; + } + if (obj->isDerivedFrom(App::LinkElement::getClassTypeId()) || + obj->isDerivedFrom(App::LinkGroup::getClassTypeId()) || + obj->isDerivedFrom(App::Link::getClassTypeId()) ) { + is_linked = true; + } + // If parent of the obj is a link to another document, we possibly need to treat non-link obj as linked, too + // 1st, is obj in another document? + if (obj->getDocument() != this->getDocument()) { + std::set parents = obj->getInListEx(true); + for (auto& parent : parents) { + // Only consider parents in the current document, i.e. possible links in this View's document + if (parent->getDocument() != this->getDocument()) { + continue; + } + // 2nd, do we really have a link to obj? + if (parent->isDerivedFrom(App::LinkElement::getClassTypeId()) || + parent->isDerivedFrom(App::LinkGroup::getClassTypeId()) || + parent->isDerivedFrom(App::Link::getClassTypeId())) { + // We have a link chain from this document to obj, and obj is in another document -> it's an XLink target + is_linked = true; + } + } + } + if (is_linked) { + xShapes.push_back(obj); + continue; + } + //not a Link and not null. assume to be drawable. Undrawables will be + // skipped later. + if (TechDraw::DrawComplexSection::isProfileObject(obj)) { + profileObject = obj; + } else { + shapes.push_back(obj); + } + } + + if (shapes.empty() && + xShapes.empty() && + !baseView) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("No Base View, Shapes, Groups or Links in this selection")); + return; + } + if (!profileObject) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("No profile object found in selection")); + return; + } + + Gui::Control().showDialog(new TaskDlgComplexSection(page, baseView, + shapes, xShapes, + profileObject, profileSubs)); +} + +bool CmdTechDrawComplexSection::isActive() +{ + return DrawGuiUtil::needPage(this); +} + //=========================================================================== // TechDraw_DetailView //=========================================================================== @@ -1510,4 +1631,5 @@ void CreateTechDrawCommands() rcCmdMgr.addCommand(new CmdTechDrawSpreadsheetView()); rcCmdMgr.addCommand(new CmdTechDrawBalloon()); rcCmdMgr.addCommand(new CmdTechDrawProjectShape()); + rcCmdMgr.addCommand(new CmdTechDrawComplexSection()); } diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui index ecec6074dd..f6e75910e7 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui @@ -6,8 +6,8 @@ 0 0 - 440 - 447 + 450 + 541 @@ -33,21 +33,34 @@ - - - + + + - true + false - Section Line Standard + Detail View Outline Shape - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + 0 @@ -56,43 +69,137 @@ - 184 - 22 + 0 + 0 + + + + + true + + + + Show arc center marks in views + + + Show Center Marks + + + true + + + ShowCenterMarks + + + Mod/TechDraw/Decorations + + + + + + + + 0 + 0 + + + + + 0 + 0 - Standard to be used to draw section lines + Show arc centers in printed output - - 1 + + Print Center Marks - SectionLineStandard + PrintCenterMarks - Mod/TechDraw/Standards + Mod/TechDraw/Decorations + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Style for balloon leader line ends + + + BalloonArrow + + + Mod/TechDraw/Decorations + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Outline shape for detail views + + + MattingStyle + + + /Mod/TechDraw/Decorations - ANSI + Circle + + + + :/icons/circular.svg:/icons/circular.svg - ISO + Square + + + + :/icons/square.svg:/icons/square.svg - - + + true - Section Line Style + Balloon Shape @@ -178,76 +285,87 @@ - - + + true - Section Cut Surface + Section Line Standard - - + + + + + 0 + 0 + + - 0 + 184 22 - Default appearance of cut surface in section view + Standard to be used to draw section lines - 2 + 1 - CutSurfaceDisplay + SectionLineStandard - /Mod/TechDraw/Decorations + Mod/TechDraw/Standards - Hide + ANSI - Solid Color - - - - - SVG Hatch - - - - - PAT Hatch + ISO - - + + true - Line group used to set line widths + Line style of detail highlight on base view - Line Width Group + Detail Highlight Style - + + + + + true + + + + Length of horizontal portion of Balloon leader + + + Ballon Leader Kink Length + + + + @@ -272,77 +390,7 @@ - - - - - false - - - - Detail View Outline Shape - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Outline shape for detail views - - - MattingStyle - - - /Mod/TechDraw/Decorations - - - - Circle - - - - :/icons/circular.svg:/icons/circular.svg - - - - - Square - - - - :/icons/square.svg:/icons/square.svg - - - - - - - - - true - - - - Line style of detail highlight on base view - - - Detail Highlight Style - - - - + @@ -415,33 +463,8 @@ - - - - - true - - - - Center Line Style - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + 0 @@ -455,86 +478,66 @@ - Type for centerlines + Length of balloon leader line kink - - 2 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 5.000000000000000 - CenterLine + BalloonKink - /Mod/TechDraw/Decorations + Mod/TechDraw/Dimensions - - - NeverShow - - - - :/icons/arrownone.svg:/icons/arrownone.svg - - - - - Continuous - - - - :/icons/continuous-line.svg:/icons/continuous-line.svg - - - - - Dash - - - - :/icons/dash-line.svg:/icons/dash-line.svg - - - - - Dot - - - - :/icons/dot-line.svg:/icons/dot-line.svg - - - - - DashDot - - - - :/icons/dashDot-line.svg:/icons/dashDot-line.svg - - - - - DashDotDot - - - - :/icons/dashDotDot-line.svg:/icons/dashDotDot-line.svg - - - - + + true - Balloon Shape + Balloon Leader End - + + + + + 0 + 0 + + + + + 0 + 0 + + + + Restrict Filled Triangle line end to vertical or horizontal directions + + + Balloon Orthogonal Triangle + + + true + + + PyramidOrtho + + + Mod/TechDraw/Decorations + + + + @@ -631,121 +634,101 @@ - - + + + + + 0 + 0 + + + + + 0 + 0 + + + + Type for centerlines + + + 2 + + + CenterLine + + + /Mod/TechDraw/Decorations + + + + NeverShow + + + + :/icons/arrownone.svg:/icons/arrownone.svg + + + + + Continuous + + + + :/icons/continuous-line.svg:/icons/continuous-line.svg + + + + + Dash + + + + :/icons/dash-line.svg:/icons/dash-line.svg + + + + + Dot + + + + :/icons/dot-line.svg:/icons/dot-line.svg + + + + + DashDot + + + + :/icons/dashDot-line.svg:/icons/dashDot-line.svg + + + + + DashDotDot + + + + :/icons/dashDotDot-line.svg:/icons/dashDotDot-line.svg + + + + + + true - Balloon Leader End + Section Cut Surface - - - - - 0 - 0 - - - - - 0 - 0 - - - - Style for balloon leader line ends - - - BalloonArrow - - - Mod/TechDraw/Decorations - - - - - - - - true - - - - Length of horizontal portion of Balloon leader - - - Ballon Leader Kink Length - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Length of balloon leader line kink - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 5.000000000000000 - - - BalloonKink - - - Mod/TechDraw/Dimensions - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Restrict Filled Triangle line end to vertical or horizontal directions - - - Balloon Orthogonal Triangle - - - true - - - PyramidOrtho - - - Mod/TechDraw/Decorations - - - - + @@ -776,71 +759,112 @@ - - - - - 0 - 0 - + + + + + true + + + Section Line Style + + + + + + + + true + + + + Center Line Style + + + + + 0 - 0 + 22 + + Default appearance of cut surface in section view + + + 2 + + + CutSurfaceDisplay + + + /Mod/TechDraw/Decorations + + + + Hide + + + + + Solid Color + + + + + SVG Hatch + + + + + PAT Hatch + + + + + + true - Show arc center marks in views + Line group used to set line widths - Show Center Marks + Line Width Group + + + + + + + + true + + + + Show or hide marks at direction changes on ComplexSection lines. + + + Complex Section Line Marks true - ShowCenterMarks + SectionLineMarks Mod/TechDraw/Decorations - - - - - 0 - 0 - - - - - 0 - 0 - - - - Show arc centers in printed output - - - Print Center Marks - - - PrintCenterMarks - - - Mod/TechDraw/Decorations - - - - + @@ -854,7 +878,7 @@ - <html><head/><body><p><span style=" font-weight:600;">Note:</span> Items in <span style=" font-style:italic;">italics</span> are default values for new objects. They have no effect on existing objects.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Note:</span> Items in <span style=" font-style:italic;">italics</span> are default values for new objects. They have no effect on existing objects.</p></body></html> true diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp index 863294f18f..622df06b9a 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp @@ -62,6 +62,7 @@ void DlgPrefsTechDrawAnnotationImp::saveSettings() ui->cbPrintCenterMarks->onSave(); ui->cbPyramidOrtho->onSave(); ui->cbSectionLineStd->onSave(); + ui->cbComplexMarks->onSave(); ui->cbShowCenterMarks->onSave(); ui->pcbLineGroup->onSave(); ui->pcbBalloonArrow->onSave(); @@ -100,6 +101,7 @@ void DlgPrefsTechDrawAnnotationImp::loadSettings() ui->cbPrintCenterMarks->onRestore(); ui->cbPyramidOrtho->onRestore(); ui->cbSectionLineStd->onRestore(); + ui->cbComplexMarks->onRestore(); ui->cbShowCenterMarks->onRestore(); ui->pcbLineGroup->onRestore(); ui->pcbBalloonArrow->onRestore(); diff --git a/src/Mod/TechDraw/Gui/PreferencesGui.cpp b/src/Mod/TechDraw/Gui/PreferencesGui.cpp index 61e3d04627..09d3103607 100644 --- a/src/Mod/TechDraw/Gui/PreferencesGui.cpp +++ b/src/Mod/TechDraw/Gui/PreferencesGui.cpp @@ -181,6 +181,13 @@ Qt::PenStyle PreferencesGui::sectionLineStyle() return sectStyle; } +bool PreferencesGui::sectionLineMarks() +{ + Base::Reference hGrp = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")-> + GetGroup("Mod/TechDraw/Decorations"); + return hGrp->GetBool("SectionLineMarks", true); +} QString PreferencesGui::weldingDirectory() { diff --git a/src/Mod/TechDraw/Gui/PreferencesGui.h b/src/Mod/TechDraw/Gui/PreferencesGui.h index 7ea07d762b..a42d1ca900 100644 --- a/src/Mod/TechDraw/Gui/PreferencesGui.h +++ b/src/Mod/TechDraw/Gui/PreferencesGui.h @@ -62,6 +62,7 @@ static double dimArrowSize(); static double edgeFuzz(); static Qt::PenStyle sectionLineStyle(); +static bool sectionLineMarks(); static QString weldingDirectory(); diff --git a/src/Mod/TechDraw/Gui/QGIDecoration.h b/src/Mod/TechDraw/Gui/QGIDecoration.h index a32228b494..3e9cb8a328 100644 --- a/src/Mod/TechDraw/Gui/QGIDecoration.h +++ b/src/Mod/TechDraw/Gui/QGIDecoration.h @@ -55,6 +55,7 @@ public: virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = nullptr ); virtual void draw(); void setWidth(double w); + double getWidth() { return m_width; } void setStyle(Qt::PenStyle s); void setColor(QColor c); QColor getColor(void) { return m_colNormal; } diff --git a/src/Mod/TechDraw/Gui/QGISectionLine.cpp b/src/Mod/TechDraw/Gui/QGISectionLine.cpp index 20bdfcecd5..e6dd3ec762 100644 --- a/src/Mod/TechDraw/Gui/QGISectionLine.cpp +++ b/src/Mod/TechDraw/Gui/QGISectionLine.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include +#include #include #include #include @@ -43,23 +44,30 @@ #include "QGIView.h" #include "PreferencesGui.h" #include "Rez.h" +#include "ZVALUE.h" #define ANSISTANDARD 0 #define ISOSTANDARD 1 +#define SINGLEDIRECTIONMODE 0 //both arrows point along the section normal +#define MULTIDIRECTIONMODE 1 //the arrows point along the normal of their section line segment using namespace TechDrawGui; using namespace TechDraw; -QGISectionLine::QGISectionLine() +QGISectionLine::QGISectionLine() : + m_pathMode(false), + m_arrowMode() { m_symbol = ""; m_symSize = 0.0; - m_extLen = 1.5 * Rez::guiX(QGIArrow::getPrefArrowSize()); + m_extLen = 1.5 * Rez::guiX(QGIArrow::getPrefArrowSize()); //is there a standard for this?? m_arrowSize = QGIArrow::getPrefArrowSize(); m_line = new QGraphicsPathItem(); addToGroup(m_line); + m_extend = new QGraphicsPathItem(); + addToGroup(m_extend); m_arrow1 = new QGIArrow(); addToGroup(m_arrow1); m_arrow2 = new QGIArrow(); @@ -69,7 +77,7 @@ QGISectionLine::QGISectionLine() m_symbol2 = new QGCustomText(); addToGroup(m_symbol2); - setWidth(Rez::guiX(0.75)); + setWidth(Rez::guiX(0.75)); //a default? setStyle(getSectionStyle()); setColor(getSectionColor()); @@ -85,14 +93,25 @@ void QGISectionLine::draw() extensionEndsISO(); } - makeLine(); + if (!pathMode()) { + makeSectionLine(); + } + makeExtensionLine(); makeArrows(); makeSymbols(); + makeChangePointMarks(); update(); } -void QGISectionLine::makeLine() +void QGISectionLine::makeExtensionLine() { + QPen extendPen; + extendPen.setWidthF(getWidth()); + extendPen.setColor(getSectionColor()); + extendPen.setStyle(Qt::SolidLine); + extendPen.setCapStyle(Qt::FlatCap); + m_extend->setPen(extendPen); + QPainterPath pp; pp.moveTo(m_beginExt1); @@ -100,7 +119,13 @@ void QGISectionLine::makeLine() pp.moveTo(m_beginExt2); pp.lineTo(m_endExt2); + m_extend->setPath(pp); +} +//make the traditional straight section line +void QGISectionLine::makeSectionLine() +{ + QPainterPath pp; pp.moveTo(m_start); pp.lineTo(m_end); m_line->setPath(pp); @@ -119,62 +144,56 @@ void QGISectionLine::makeArrows() //make Euro (ISO) Arrows void QGISectionLine::makeArrowsISO() { - double arrowRotation = 0.0; - m_arrowDir.Normalize(); - double angle = atan2f(m_arrowDir.y, m_arrowDir.x); - if (angle < 0.0) { - angle = 2 * M_PI + angle; - } - arrowRotation = 360.0 - angle * (180.0/M_PI); //convert to Qt rotation (clockwise degrees) - m_arrow1->setStyle(0); m_arrow1->setSize(QGIArrow::getPrefArrowSize()); m_arrow1->setPos(m_start); - m_arrow1->draw(); - m_arrow1->setRotation(arrowRotation); //rotation = 0 ==> -> horizontal, pointing right - m_arrow2->setStyle(0); m_arrow2->setSize(QGIArrow::getPrefArrowSize()); m_arrow2->setPos(m_end); + + if (m_arrowMode == SINGLEDIRECTIONMODE) { + double arrowRotation = getArrowRotation(m_arrowDir); + m_arrow1->setRotation(arrowRotation); //rotation = 0 ==> -> horizontal, pointing right + m_arrow2->setRotation(arrowRotation); + } else { + double arrowRotation1 = getArrowRotation(m_arrowDir1); + m_arrow1->setRotation(arrowRotation1); + double arrowRotation2 = getArrowRotation(m_arrowDir2); + m_arrow2->setRotation(arrowRotation2); + } + m_arrow1->draw(); m_arrow2->draw(); - m_arrow2->setRotation(arrowRotation); } //make traditional (ASME) section arrows void QGISectionLine::makeArrowsTrad() { - double arrowRotation = 0.0; - m_arrowDir.Normalize(); - double angle = atan2f(m_arrowDir.y, m_arrowDir.x); - if (angle < 0.0) { - angle = 2 * M_PI + angle; - } - arrowRotation = 360.0 - angle * (180.0/M_PI); //convert to Qt rotation (clockwise degrees) - - QPointF posArrow1, posArrow2; - QPointF offsetDir(m_arrowDir.x, -m_arrowDir.y); //remember Y dir is flipped - - double oblique = 1.0; - if ( !DrawUtil::fpCompare((m_arrowDir.x + m_arrowDir.y), 1.0) ) { - oblique = 1.25; - } - double offsetLength = (m_extLen * oblique) + Rez::guiX(QGIArrow::getPrefArrowSize()); - QPointF offsetVec = offsetLength * offsetDir; - - posArrow1 = m_start + offsetVec; - posArrow2 = m_end + offsetVec; - m_arrow1->setStyle(0); m_arrow1->setSize(QGIArrow::getPrefArrowSize()); - m_arrow1->setPos(posArrow1); - m_arrow1->draw(); - m_arrow1->setRotation(arrowRotation); //rotation = 0 ==> -> horizontal, pointing right - m_arrow2->setStyle(0); m_arrow2->setSize(QGIArrow::getPrefArrowSize()); - m_arrow2->setPos(posArrow2); + + if (m_arrowMode == SINGLEDIRECTIONMODE) { + double arrowRotation = getArrowRotation(m_arrowDir); + m_arrow1->setRotation(arrowRotation); //rotation = 0 ==> -> horizontal, pointing right + m_arrow2->setRotation(arrowRotation); + m_arrowPos1 = getArrowPosition(m_arrowDir, m_start); + m_arrow1->setPos(m_arrowPos1); + m_arrowPos2 = getArrowPosition(m_arrowDir, m_end); + m_arrow2->setPos(m_arrowPos2); + } else { + double arrowRotation1 = getArrowRotation(m_arrowDir1); + m_arrow1->setRotation(arrowRotation1); + m_arrowPos1 = getArrowPosition(m_arrowDir1, m_start); + m_arrow1->setPos(m_arrowPos1); + double arrowRotation2 = getArrowRotation(m_arrowDir2); + m_arrow2->setRotation(arrowRotation2); + m_arrowPos2 = getArrowPosition(m_arrowDir2, m_end); + m_arrow2->setPos(m_arrowPos2); + } + + m_arrow1->draw(); m_arrow2->draw(); - m_arrow2->setRotation(arrowRotation); } void QGISectionLine::makeSymbols() @@ -187,10 +206,10 @@ void QGISectionLine::makeSymbols() } } +//arrows go at arrowhead position. void QGISectionLine::makeSymbolsTrad() { prepareGeometryChange(); -// m_symFont.setPixelSize(QGIView::calculateFontPixelSize(m_symSize)); int fontSize = QGIView::exactFontSize(Base::Tools::toStdString(m_symFont.family()), m_symSize); m_symFont.setPixelSize(fontSize); m_symbol1->setFont(m_symFont); @@ -199,33 +218,28 @@ void QGISectionLine::makeSymbolsTrad() m_symbol2->setPlainText(QString::fromUtf8(m_symbol)); QRectF symRect = m_symbol1->boundingRect(); - double symWidth = symRect.width(); double symHeight = symRect.height(); - double symbolFudge = 0.75; - double angle = atan2f(m_arrowDir.y, m_arrowDir.x); - if (angle < 0.0) { - angle = 2 * M_PI + angle; - } - Base::Vector3d adjustVector(cos(angle) * symWidth, sin(angle) * symHeight, 0.0); - adjustVector = DrawUtil::invertY(adjustVector) * symbolFudge; - QPointF qAdjust(adjustVector.x, adjustVector.y); + double gap = 0.5 * symHeight; //symHeight as surrogate for char box - QPointF posSymbol1 = m_arrow1->pos() + qAdjust; - m_symbol1->centerAt(posSymbol1); + QPointF motion1(m_arrowDir1.x, -m_arrowDir1.y); //move in same direction as arrow + QPointF motion2(m_arrowDir2.x, -m_arrowDir2.y); //Qt y coords! - QPointF posSymbol2 = m_arrow2->pos() + qAdjust; - m_symbol2->centerAt(posSymbol2); + QPointF symPos1 = m_arrowPos1 + motion1 * gap; + QPointF symPos2 = m_arrowPos2 + motion2 * gap; - m_symbol1->setTransformOriginPoint(m_symbol1->mapFromParent(posSymbol1)); - m_symbol1->setRotation(360.0 - rotation()); - m_symbol2->setTransformOriginPoint(m_symbol2->mapFromParent(posSymbol2)); + m_symbol1->centerAt(symPos1); + m_symbol2->centerAt(symPos2); + + m_symbol1->setTransformOriginPoint(m_symbol1->mapFromParent(symPos1)); + m_symbol1->setRotation(360.0 - rotation()); //to Qt angle + m_symbol2->setTransformOriginPoint(m_symbol2->mapFromParent(symPos2)); m_symbol2->setRotation(360.0 - rotation()); } +//symbols go at ends of extensions void QGISectionLine::makeSymbolsISO() { prepareGeometryChange(); -// m_symFont.setPixelSize(QGIView::calculateFontPixelSize(m_symSize)); int fontSize = QGIView::exactFontSize(Base::Tools::toStdString(m_symFont.family()), m_symSize); m_symFont.setPixelSize(fontSize); m_symbol1->setFont(m_symFont); @@ -233,79 +247,111 @@ void QGISectionLine::makeSymbolsISO() m_symbol2->setFont(m_symFont); m_symbol2->setPlainText(QString::fromUtf8(m_symbol)); - QPointF symPosStart, symPosEnd; - //no normalize() for QPointF - QPointF dist = (m_start - m_end); - double lenDist = sqrt(dist.x()*dist.x() + dist.y()*dist.y()); - QPointF offsetDir = dist / lenDist; - QRectF symRect = m_symbol1->boundingRect(); - double symWidth = symRect.width(); double symHeight = symRect.height(); + double gap = 0.5 * symHeight; //symHeight as surrogate for char box - double symbolFudge = 0.75; - double angle = atan2f(offsetDir.y(), offsetDir.x()); - if (angle < 0.0) { - angle = 2.0 * M_PI + angle; - } - Base::Vector3d adjustVector(cos(angle) * symWidth, sin(angle) * symHeight, 0.0); - adjustVector = adjustVector * symbolFudge; - QPointF qAdjust(adjustVector.x, adjustVector.y); + QPointF motion1(-m_arrowDir1.x, m_arrowDir1.y); //move away from extension end + QPointF motion2(-m_arrowDir2.x, m_arrowDir2.y); //Qt y coords! - symPosStart = m_start + qAdjust; - symPosEnd = m_end - qAdjust; + QPointF symPos1 = m_endExt1 + motion1 * gap; + QPointF symPos2 = m_endExt2 + motion2 * gap; - m_symbol1->centerAt(symPosStart); - m_symbol2->centerAt(symPosEnd); + m_symbol1->centerAt(symPos1); + m_symbol2->centerAt(symPos2); - m_symbol1->setTransformOriginPoint(m_symbol1->mapFromParent(symPosStart)); + m_symbol1->setTransformOriginPoint(m_symbol1->mapFromParent(symPos1)); m_symbol1->setRotation(360.0 - rotation()); - m_symbol2->setTransformOriginPoint(m_symbol2->mapFromParent(symPosEnd)); + m_symbol2->setTransformOriginPoint(m_symbol2->mapFromParent(symPos2)); m_symbol2->setRotation(360.0 - rotation()); - } +//extension lines are on the stock side of the section line void QGISectionLine::extensionEndsTrad() { - QPointF offsetDir(m_arrowDir.x, -m_arrowDir.y); + if (m_arrowMode == SINGLEDIRECTIONMODE) { + QPointF offsetDir(m_arrowDir.x, -m_arrowDir.y); //inverted Y + offsetDir = normalizeQPointF(offsetDir); - //extensions for oblique section line needs to be a bit longer - double oblique = 1.0; - if ( !DrawUtil::fpCompare((m_arrowDir.x + m_arrowDir.y), 1.0) ) { - oblique = 1.25; + //draw from section line endpoint + QPointF offsetEnd = m_extLen * offsetDir; + m_beginExt1 = m_start; + m_endExt1 = m_start + offsetEnd; + m_beginExt2 = m_end; + m_endExt2 = m_end + offsetEnd; + } else { + //extension lines run from point on section line to arrowhead + m_beginExt1 = m_start; + m_endExt1 = getArrowPosition(m_arrowDir1, m_start); + m_beginExt2 = m_end; + m_endExt2 = getArrowPosition(m_arrowDir2, m_end); } - - //draw from section line endpoint - QPointF offsetEnd = oblique * m_extLen * offsetDir; - m_beginExt1 = m_start; - m_endExt1 = m_start + offsetEnd; - m_beginExt2 = m_end; - m_endExt2 = m_end + offsetEnd; } +//the extension lines are on the waste side of the section line! void QGISectionLine::extensionEndsISO() { - //lines are offset to other side of section line! - QPointF offsetDir(m_arrowDir.x, -m_arrowDir.y); - offsetDir = offsetDir * -1.0; + if (m_arrowMode == SINGLEDIRECTIONMODE) { + QPointF offsetDir(-m_arrowDir.x, m_arrowDir.y); //reversed and inverted y + offsetDir = normalizeQPointF(offsetDir); - //extensions for oblique section line needs to be a bit longer? - //this is just esthetics - double oblique = 1.0; - if ( !DrawUtil::fpCompare((m_arrowDir.x + m_arrowDir.y), 1.0) ) { - oblique = 1.10; + //draw from section line endpoint less arrow length + QPointF offsetStart = offsetDir * Rez::guiX(QGIArrow::getPrefArrowSize()); + QPointF offsetEnd = m_extLen * offsetDir; + + m_beginExt1 = m_start + offsetStart; + m_endExt1 = m_start + offsetStart + offsetEnd; + m_beginExt2 = m_end + offsetStart; + m_endExt2 = m_end + offsetStart + offsetEnd; + } else { + //extension lines run in reverse of arrow direction from base of arrowhead for distance m_extLen + QPointF offsetDir1(-m_arrowDir1.x, m_arrowDir1.y); //reversed and inverted y + offsetDir1 = normalizeQPointF(offsetDir1); + QPointF offsetStart1 = offsetDir1 * Rez::guiX(QGIArrow::getPrefArrowSize()); + QPointF offsetEnd1 = m_extLen * offsetDir1; + m_beginExt1 = m_start + offsetStart1; + m_endExt1 = m_start + offsetStart1 + offsetEnd1; + + QPointF offsetDir2(-m_arrowDir2.x, m_arrowDir2.y); //reversed and inverted y + offsetDir2 = normalizeQPointF(offsetDir2); + QPointF offsetStart2 = offsetDir2 * Rez::guiX(QGIArrow::getPrefArrowSize()); + QPointF offsetEnd2 = m_extLen * offsetDir2; + m_beginExt2 = m_end + offsetStart2; + m_endExt2 = m_end + offsetStart2 + offsetEnd2; } - - //draw from section line endpoint less arrow length - QPointF offsetStart = offsetDir * Rez::guiX(QGIArrow::getPrefArrowSize()); - QPointF offsetEnd = oblique * m_extLen * offsetDir; - - m_beginExt1 = m_start + offsetStart; - m_endExt1 = m_start + offsetStart + offsetEnd; - m_beginExt2 = m_end + offsetStart; - m_endExt2 = m_end + offsetStart + offsetEnd; } +void QGISectionLine::makeChangePointMarks() +{ +// Base::Console().Message("QGISL::makeChangePointMarks()\n"); + double segmentLength = 0.50 * QGIArrow::getPrefArrowSize(); + QPen cPointPen; + //TODO: this should really be 2.0 * thickLineWidth, but we only have one + //width available (which should be 'thin', for the section line) + cPointPen.setWidthF(2.0 * getWidth()); + cPointPen.setColor(getSectionColor()); + cPointPen.setStyle(Qt::SolidLine); + for (auto& cPoint : m_changePointData) { + QGraphicsPathItem* cPointItem = new QGraphicsPathItem(); + addToGroup(cPointItem); + + QPainterPath pPath; + QPointF location = cPoint.getLocation(); + QPointF start = location + cPoint.getPreDirection() * segmentLength; + QPointF end = location + cPoint.getPostDirection() * segmentLength; + pPath.moveTo(Rez::guiPt(start)); + pPath.lineTo(Rez::guiPt(location)); + pPath.lineTo(Rez::guiPt(end)); + + cPointItem->setPath(pPath); + cPointItem->setPen(cPointPen); + cPointItem->setZValue(ZVALUE::SECTIONLINE + 1); + cPointItem->setPos(0.0, 0.0); + m_changePointMarks.push_back(cPointItem); + } +} + + void QGISectionLine::setEnds(Base::Vector3d l1, Base::Vector3d l2) { m_l1 = l1; @@ -333,8 +379,46 @@ void QGISectionLine::setDirection(double xDir, double yDir) void QGISectionLine::setDirection(Base::Vector3d dir) { + m_arrowMode = SINGLEDIRECTIONMODE; m_arrowDir = dir; m_arrowDir.Normalize(); + m_arrowDir1 = dir; + m_arrowDir1.Normalize(); + m_arrowDir2 = dir; + m_arrowDir2.Normalize(); +} + +void QGISectionLine::setArrowDirections(Base::Vector3d dir1, Base::Vector3d dir2) +{ + m_arrowMode = MULTIDIRECTIONMODE; + m_arrowDir1 = dir1; + m_arrowDir1.Normalize(); + m_arrowDir2 = dir2; + m_arrowDir2.Normalize(); +} + +//convert an arrow direction vector into a Qt rotation angle degrees +double QGISectionLine::getArrowRotation(Base::Vector3d arrowDir) +{ + arrowDir.Normalize(); + double angle = atan2f(arrowDir.y, arrowDir.x); + if (angle < 0.0) { + angle = 2 * M_PI + angle; + } + double arrowRotation = 360.0 - angle * (180.0/M_PI); //convert to Qt rotation (clockwise degrees) + return arrowRotation; +} + +QPointF QGISectionLine::getArrowPosition(Base::Vector3d arrowDir, QPointF refPoint) +{ + QPointF qArrowDir(arrowDir.x, -arrowDir.y); //remember Y dir is flipped + qArrowDir = normalizeQPointF(qArrowDir); + + double offsetLength = m_extLen + Rez::guiX(QGIArrow::getPrefArrowSize()); + QPointF offsetVec = offsetLength * qArrowDir; + + QPointF arrowPos = refPoint + offsetVec; + return arrowPos; } void QGISectionLine::setFont(QFont f, double fsize) @@ -343,6 +427,44 @@ void QGISectionLine::setFont(QFont f, double fsize) m_symSize = fsize; } +void QGISectionLine::setPath(QPainterPath& path) +{ + m_line->setPath(path); +} + +void QGISectionLine::setChangePoints(TechDraw::ChangePointVector changePointData) +{ + m_changePointData = changePointData; + clearChangePointMarks(); +} + +void QGISectionLine::clearChangePoints() +{ + clearChangePointMarks(); + m_changePointData.clear(); +} + +void QGISectionLine::clearChangePointMarks() +{ + if (!m_changePointMarks.empty()) { + for (auto& cPoint : m_changePointMarks) { + cPoint->hide(); + scene()->removeItem(cPoint); + delete cPoint; + } + m_changePointMarks.clear(); + } +} + +//QPointF does not have length or normalize methods +QPointF QGISectionLine::normalizeQPointF(QPointF inPoint) +{ + double x2 = inPoint.x() * inPoint.x(); + double y2 = inPoint.y() * inPoint.y(); + double root = sqrt(x2 + y2); + return inPoint / root; +} + void QGISectionLine::setSectionColor(QColor c) { setColor(c); @@ -416,11 +538,6 @@ void QGISectionLine::setTools() m_line->setPen(m_pen); -// m_arrow1->setPen(m_pen); -// m_arrow2->setPen(m_pen); -// m_arrow1->setBrush(m_brush); -// m_arrow2->setBrush(m_brush); - m_arrow1->setNormalColor(m_colCurrent); m_arrow1->setFillColor(m_colCurrent); m_arrow1->setPrettyNormal(); diff --git a/src/Mod/TechDraw/Gui/QGISectionLine.h b/src/Mod/TechDraw/Gui/QGISectionLine.h index fa23302ad3..c2bd2e1767 100644 --- a/src/Mod/TechDraw/Gui/QGISectionLine.h +++ b/src/Mod/TechDraw/Gui/QGISectionLine.h @@ -28,9 +28,12 @@ #include #include #include +#include #include +#include + #include "QGCustomText.h" #include "QGIDecoration.h" @@ -53,34 +56,46 @@ public: void setEnds(Base::Vector3d l1, Base::Vector3d l2); void setBounds(double x1, double y1, double x2, double y2); + void setPath(QPainterPath& path); void setSymbol(char* sym); void setDirection(double xDir, double yDir); void setDirection(Base::Vector3d dir); + void setArrowDirections(Base::Vector3d dir1, Base::Vector3d dir2); void setFont(QFont f, double fsize); void setSectionStyle(int style); void setSectionColor(QColor c); - + void setPathMode(bool mode) { m_pathMode = mode; } + bool pathMode() { return m_pathMode; } + void setChangePoints(TechDraw::ChangePointVector changePoints); + void clearChangePoints(); virtual void draw(); protected: QColor getSectionColor(); Qt::PenStyle getSectionStyle(); - void makeLine(); + void makeSectionLine(); + void makeExtensionLine(); void makeArrows(); void makeArrowsTrad(); void makeArrowsISO(); void makeSymbols(); void makeSymbolsTrad(); void makeSymbolsISO(); + void makeChangePointMarks(); void setTools(); int getPrefSectionStandard(); void extensionEndsISO(); void extensionEndsTrad(); + double getArrowRotation(Base::Vector3d arrowDir); + QPointF getArrowPosition(Base::Vector3d arrowDir, QPointF refPoint); + void clearChangePointMarks(); + static QPointF normalizeQPointF(QPointF inPoint); private: char* m_symbol; - QGraphicsPathItem* m_line; //primpath? + QGraphicsPathItem* m_line; + QGraphicsPathItem* m_extend; QGIArrow* m_arrow1; QGIArrow* m_arrow2; QGCustomText* m_symbol1; @@ -92,15 +107,21 @@ private: QFont m_symFont; double m_symSize; double m_arrowSize; - //QColor m_color; double m_extLen; -// int m_sectionFormat; //0 = ASME, 1 = ISO Base::Vector3d m_l1; //end of main section line Base::Vector3d m_l2; //end of main section line QPointF m_beginExt1; //start of extension line 1 QPointF m_endExt1; //end of extension line 1 QPointF m_beginExt2; //start of extension line 2 QPointF m_endExt2; //end of extension line 1 + bool m_pathMode; //use external path for line + int m_arrowMode; //0 = 1 direction for both arrows, 1 = direction for each arrow + Base::Vector3d m_arrowDir1; + Base::Vector3d m_arrowDir2; + QPointF m_arrowPos1; + QPointF m_arrowPos2; + std::vector m_changePointMarks; + TechDraw::ChangePointVector m_changePointData; }; } diff --git a/src/Mod/TechDraw/Gui/QGIViewPart.cpp b/src/Mod/TechDraw/Gui/QGIViewPart.cpp index 6f56bf32f5..5c900a432a 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewPart.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -446,10 +447,10 @@ void QGIViewPart::drawViewPart() if (!vp) return; - float lineWidth = vp->LineWidth.getValue() * lineScaleFactor; - float lineWidthHid = vp->HiddenWidth.getValue() * lineScaleFactor; - float lineWidthIso = vp->IsoWidth.getValue() * lineScaleFactor; -// float lineWidthExtra = viewPart->ExtraWidth.getValue() * lineScaleFactor; + float lineWidth = vp->LineWidth.getValue() * lineScaleFactor; //thick + float lineWidthHid = vp->HiddenWidth.getValue() * lineScaleFactor; //thin + float lineWidthIso = vp->IsoWidth.getValue() * lineScaleFactor; //graphic +// float lineWidthExtra = viewPart->ExtraWidth.getValue() * lineScaleFactor; //extra bool showAll = vp->ShowAllEdges.getValue(); prepareGeometryChange(); @@ -803,7 +804,11 @@ void QGIViewPart::drawAllSectionLines() if (vp->ShowSectionLine.getValue()) { auto refs = viewPart->getSectionRefs(); for (auto& r:refs) { - drawSectionLine(r, true); + if (r->isDerivedFrom(DrawComplexSection::getClassTypeId())) { + drawComplexSectionLine(r, true); + } else { + drawSectionLine(r, true); + } } } } @@ -820,8 +825,10 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b return; auto vp = static_cast(getViewProvider(getViewObject())); - if (!vp) + if (!vp) { return; + } + float lineWidthThin = vp->HiddenWidth.getValue() * lineScaleFactor; //thin if (b) { QGISectionLine* sectionLine = new QGISectionLine(); @@ -829,7 +836,7 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b sectionLine->setSymbol(const_cast(viewSection->SectionSymbol.getValue())); sectionLine->setSectionStyle(vp->SectionLineStyle.getValue()); sectionLine->setSectionColor(vp->SectionLineColor.getValue().asValue()); - + sectionLine->setPathMode(false); //find the ends of the section line double scale = viewPart->getScale(); std::pair sLineEnds = viewSection->sectionLineEnds(); @@ -839,11 +846,8 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b //which way to the arrows point? Base::Vector3d lineDir = l2 - l1; lineDir.Normalize(); - Base::Vector3d normalDir = viewSection->SectionNormal.getValue(); - Base::Vector3d projNormal = viewPart->projectPoint(normalDir); - projNormal.Normalize(); Base::Vector3d arrowDir = viewSection->SectionNormal.getValue(); - arrowDir = - viewPart->projectPoint(arrowDir); //arrows point reverse of sectionNormal(extrusion dir) + arrowDir = - viewPart->projectPoint(arrowDir); //arrows point reverse of sectionNormal sectionLine->setDirection(arrowDir.x, -arrowDir.y); //invert Y //make the section line a little longer @@ -853,7 +857,7 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b //set the general parameters sectionLine->setPos(0.0, 0.0); - sectionLine->setWidth(Rez::guiX(vp->LineWidth.getValue())); + sectionLine->setWidth(lineWidthThin); double fontSize = Preferences::dimFontSizeMM(); sectionLine->setFont(getFont(), fontSize); sectionLine->setZValue(ZVALUE::SECTIONLINE); @@ -862,6 +866,73 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b } } +void QGIViewPart::drawComplexSectionLine(TechDraw::DrawViewSection* viewSection, bool b) +{ + Q_UNUSED(b); + TechDraw::DrawViewPart *viewPart = static_cast(getViewObject()); + if (!viewPart) + return; + if (!viewSection) + return; + auto vp = static_cast(getViewProvider(getViewObject())); + if (!vp) { + return; + } + float lineWidthThin = vp->HiddenWidth.getValue() * lineScaleFactor; //thin + + auto dcs = static_cast(viewSection); + BaseGeomPtrVector edges = dcs->makeSectionLineGeometry(); + QPainterPath wirePath; + QPainterPath firstSeg = drawPainterPath(edges.front()); + wirePath.connectPath(firstSeg); + int edgeCount = edges.size(); + //NOTE: if the edges are not in nose to tail order, Qt will insert extra segments + //that will overlap the segments we add. for interupted line styles, this + //will make the line look continuous. This is prevented in + //DrawComplexSection::makeSectionLineGeometry by calling makeNoseToTailWire + for (int i = 1; i < edgeCount; i++) { + QPainterPath edgePath = drawPainterPath(edges.at(i)); + wirePath.connectPath(edgePath); + } + + std::pair ends = dcs->sectionLineEnds(); + Base::Vector3d vStart = Rez::guiX(ends.first); //already scaled by dcs + Base::Vector3d vEnd = Rez::guiX(ends.second); + + QGISectionLine* sectionLine = new QGISectionLine(); + addToGroup(sectionLine); + sectionLine->setSymbol(const_cast(viewSection->SectionSymbol.getValue())); + sectionLine->setSectionStyle(vp->SectionLineStyle.getValue()); + sectionLine->setSectionColor(vp->SectionLineColor.getValue().asValue()); + sectionLine->setPathMode(true); + sectionLine->setPath(wirePath); + sectionLine->setEnds(vStart, vEnd); + if (vp->SectionLineMarks.getValue()) { + sectionLine->setChangePoints(dcs->getChangePointsFromSectionLine()); + } else { + sectionLine->clearChangePoints(); + } + if (dcs->ProjectionStrategy.isValue("Single")) { + Base::Vector3d arrowDirSingle = viewSection->SectionNormal.getValue(); + arrowDirSingle = - viewPart->projectPoint(arrowDirSingle); //arrows are opposite section normal + sectionLine->setDirection(arrowDirSingle.x, -arrowDirSingle.y); //invert y for Qt + } else { + std::pair dirsPiecewise = dcs->sectionArrowDirs(); + dirsPiecewise.first = DrawUtil::invertY(dirsPiecewise.first); + dirsPiecewise.second = DrawUtil::invertY(dirsPiecewise.second); + sectionLine->setArrowDirections(dirsPiecewise.first, dirsPiecewise.second); + } + + //set the general parameters + sectionLine->setPos(0.0, 0.0); + sectionLine->setWidth(lineWidthThin); + double fontSize = Preferences::dimFontSizeMM(); + sectionLine->setFont(getFont(), fontSize); + sectionLine->setZValue(ZVALUE::SECTIONLINE); + sectionLine->setRotation(- viewPart->Rotation.getValue()); + sectionLine->draw(); +} + //TODO: use Cosmetic::CenterLine object for this to make it usable for dims. void QGIViewPart::drawCenterLines(bool b) { @@ -885,35 +956,28 @@ void QGIViewPart::drawCenterLines(bool b) centerLine = new QGICenterLine(); addToGroup(centerLine); centerLine->setPos(0.0, 0.0); - //this should work from the viewPart's bbox, not the border -// double scale = viewPart->getScale(); double width = Rez::guiX(viewPart->getBoxX()); sectionSpan = width + sectionFudge; -// sectionSpan = m_border->rect().width() + sectionFudge; xVal = sectionSpan / 2.0; yVal = 0.0; centerLine->setIntersection(horiz && vert); centerLine->setBounds(-xVal, -yVal, xVal, yVal); centerLine->setWidth(Rez::guiX(vp->HiddenWidth.getValue())); centerLine->setZValue(ZVALUE::SECTIONLINE); -// centerLine->setRotation(viewPart->Rotation.getValue()); centerLine->draw(); } if (vert) { centerLine = new QGICenterLine(); addToGroup(centerLine); centerLine->setPos(0.0, 0.0); -// double scale = viewPart->getScale(); double height = Rez::guiX(viewPart->getBoxY()); sectionSpan = height + sectionFudge; -// sectionSpan = (m_border->rect().height() - m_label->boundingRect().height()) + sectionFudge; xVal = 0.0; yVal = sectionSpan / 2.0; centerLine->setIntersection(horiz && vert); centerLine->setBounds(-xVal, -yVal, xVal, yVal); centerLine->setWidth(Rez::guiX(vp->HiddenWidth.getValue())); centerLine->setZValue(ZVALUE::SECTIONLINE); -// centerLine->setRotation(viewPart->Rotation.getValue()); centerLine->draw(); } } diff --git a/src/Mod/TechDraw/Gui/QGIViewPart.h b/src/Mod/TechDraw/Gui/QGIViewPart.h index b502d34cad..46d7c1e7df 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.h +++ b/src/Mod/TechDraw/Gui/QGIViewPart.h @@ -68,6 +68,7 @@ public: QRectF boundingRect() const override; virtual void drawAllSectionLines(); virtual void drawSectionLine(TechDraw::DrawViewSection* s, bool b); + virtual void drawComplexSectionLine(TechDraw::DrawViewSection* viewSection, bool b); virtual void drawCenterLines(bool b); virtual void drawHighlight(TechDraw::DrawViewDetail* viewDetail, bool b); virtual void drawMatting(); diff --git a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc index eaa2d3845e..3e1c093eea 100644 --- a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc +++ b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc @@ -51,6 +51,7 @@ icons/actions/TechDraw_View.svg icons/actions/TechDraw_WeldSymbol.svg icons/actions/TechDraw_SurfaceFinishSymbols.svg + icons/actions/TechDraw_ComplexSection.svg icons/arrow-ccw.svg icons/arrow-cw.svg icons/arrow-down.svg @@ -180,6 +181,8 @@ icons/preferences-techdraw.svg + + translations/TechDraw_af.qm translations/TechDraw_ar.qm diff --git a/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg new file mode 100644 index 0000000000..ab7fde1d26 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg @@ -0,0 +1,767 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [agryson] Alexander Gryson + + + http://agryson.net + + TechDraw_View + 2016-01-14 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_View.svg + + + FreeCAD LGPL2+ + + + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp new file mode 100644 index 0000000000..bd0afe5296 --- /dev/null +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp @@ -0,0 +1,360 @@ +/*************************************************************************** + * 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 * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif // #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 "DrawGuiUtil.h" + +#include "TaskComplexSection.h" + +using namespace Gui; +using namespace TechDraw; +using namespace TechDrawGui; + +TaskComplexSection::TaskComplexSection(TechDraw::DrawPage* page, + TechDraw::DrawViewPart* baseView, + std::vector shapes, + std::vector xShapes, + App::DocumentObject* profileObject, + std::vector profileSubs) : + ui(new Ui_TaskComplexSection), + m_page(page), + m_baseView(baseView), + m_section(nullptr), + m_shapes(shapes), + m_xShapes(xShapes), + m_profileObject(profileObject), + m_profileSubs(profileSubs), + m_sectionName(std::string()) +{ + ui->setupUi(this); + setUiPrimary(); + + connect(ui->pbSectionObjects, SIGNAL(clicked()), this, SLOT(onSectionObjectsUseSelectionClicked())); + connect(ui->pbProfileObject, SIGNAL(clicked()), this, SLOT(onProfileObjectsUseSelectionClicked())); +} + +void TaskComplexSection::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } +} + +void TaskComplexSection::setUiPrimary() +{ + setWindowTitle(QObject::tr("New Complex Section")); + + ui->dsbXNormal->setEnabled(true); + ui->dsbYNormal->setEnabled(true); + ui->dsbZNormal->setEnabled(true); + std::pair dirs = DrawGuiUtil::get3DDirAndRot(); + ui->dsbXNormal->setValue(dirs.first.x); + ui->dsbYNormal->setValue(dirs.first.y); + ui->dsbZNormal->setValue(dirs.first.z); + m_saveXDir = dirs.second; + + ui->leSectionObjects->setText(sourcesToString()); + ui->leProfileObject->setText(Base::Tools::fromStdString(m_profileObject->getNameInDocument()) + + QString::fromUtf8(" / ") + + Base::Tools::fromStdString(m_profileObject->Label.getValue())); + if (m_baseView) { + ui->dsbScale->setValue(m_baseView->getScale()); + ui->cmbScaleType->setCurrentIndex(m_baseView->ScaleType.getValue()); + } else { + ui->dsbScale->setValue(Preferences::scale()); + ui->cmbScaleType->setCurrentIndex(Preferences::scaleType()); + } +} + +void TaskComplexSection::onSectionObjectsUseSelectionClicked() +{ + std::vector selection = Gui::Selection().getSelectionEx(); + std::vector newSelection; + std::vector newXSelection; + for (auto& sel : selection) { + if (sel.getObject()->isDerivedFrom(App::LinkElement::getClassTypeId()) || + sel.getObject()->isDerivedFrom(App::LinkGroup::getClassTypeId()) || + sel.getObject()->isDerivedFrom(App::Link::getClassTypeId()) ) { + newXSelection.push_back(sel.getObject()); + } else { + newSelection.push_back(sel.getObject()); + } + } + m_shapes = newSelection; + m_xShapes = newXSelection; + ui->leSectionObjects->setText(sourcesToString()); +} + +void TaskComplexSection::onProfileObjectsUseSelectionClicked() +{ + std::vector selection = Gui::Selection().getSelectionEx(); + //check for single selection and ability to make profile from selected object + if (!selection.empty()) { + m_profileObject = selection.front().getObject(); + ui->leProfileObject->setText(Base::Tools::fromStdString(m_profileObject->getNameInDocument()) + + QString::fromUtf8(" / ") + + Base::Tools::fromStdString(m_profileObject->Label.getValue())); + } +} + +QString TaskComplexSection::sourcesToString() +{ + QString result; + if (m_baseView) { + for (auto& obj : m_baseView->Source.getValues()) { + result += Base::Tools::fromStdString(obj->getNameInDocument()) + + QString::fromUtf8(" / ") + + Base::Tools::fromStdString(obj->Label.getValue()) + + QString::fromUtf8(", "); + } + for (auto& obj : m_baseView->XSource.getValues()) { + result += Base::Tools::fromStdString(obj->getNameInDocument()) + + QString::fromUtf8(" / ") + + Base::Tools::fromStdString(obj->Label.getValue()) + + QString::fromUtf8(", "); + } + } else { + for (auto& obj : m_shapes) { + result += Base::Tools::fromStdString(obj->getNameInDocument()) + + QString::fromUtf8(" / ") + + Base::Tools::fromStdString(obj->Label.getValue()) + + QString::fromUtf8(", "); + } + for (auto& obj : m_xShapes) { + result += Base::Tools::fromStdString(obj->getNameInDocument()) + + QString::fromUtf8(" / ") + + Base::Tools::fromStdString(obj->Label.getValue()) + + QString::fromUtf8(", "); + } + } + return result; +} + +void TaskComplexSection::updateUi() +{ +} + +//pointer to created view is not returned, but stored in m_section +void TaskComplexSection::createComplexSection() +{ +// Base::Console().Message("TCS::createComplexSection()\n"); + + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create ComplexSection")); + if (!m_section) { + m_sectionName = m_page->getDocument()->getUniqueObjectName("ComplexSection"); + std::string sectionType = "TechDraw::DrawComplexSection"; + + Command::doCommand(Command::Doc, "App.ActiveDocument.addObject('%s', '%s')", + sectionType.c_str(), m_sectionName.c_str()); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.addView(App.ActiveDocument.%s)", + m_page->getNameInDocument(), m_sectionName.c_str()); + + QString qTemp = ui->leSymbol->text(); + std::string temp = Base::Tools::toStdString(qTemp); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.SectionSymbol = '%s'", + m_sectionName.c_str(), + temp.c_str()); + std::string lblText = "Section " + + temp + + " - " + + temp; + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Label = '%s'", + m_sectionName.c_str(), + lblText.c_str()); + + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.6f", + m_sectionName.c_str(), + ui->dsbScale->value()); + int scaleType = ui->cmbScaleType->currentIndex(); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", + m_sectionName.c_str(), scaleType); + int projectionStrategy = ui->cmbStrategy->currentIndex(); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ProjectionStrategy = %d", + m_sectionName.c_str(), projectionStrategy); + + Command::doCommand(Command::Doc, "App.activeDocument().%s.Direction = FreeCAD.Vector(%.3f, %.3f, %.3f)", + m_sectionName.c_str(), ui->dsbXNormal->value(), + ui->dsbYNormal->value(), ui->dsbZNormal->value()); + Command::doCommand(Command::Doc, "App.activeDocument().%s.SectionNormal = FreeCAD.Vector(%.3f, %.3f, %.3f)", + m_sectionName.c_str(), ui->dsbXNormal->value(), + ui->dsbYNormal->value(), ui->dsbZNormal->value()); + Command::doCommand(Command::Doc, "App.activeDocument().%s.XDirection = FreeCAD.Vector(%.3f, %.3f, %.3f)", + m_sectionName.c_str(), m_saveXDir.x, m_saveXDir.y, m_saveXDir.z); + Command::doCommand(Command::Doc, "App.activeDocument().%s.SectionOrigin = FreeCAD.Vector(0.0, 0.0, 0.0)", + m_sectionName.c_str()); + Command::doCommand(Command::Doc, "App.activeDocument().%s.SectionDirection = 'Aligned'", + m_sectionName.c_str()); + + App::DocumentObject* newObj = m_page->getDocument()->getObject(m_sectionName.c_str()); + m_section = dynamic_cast(newObj); + if (!newObj || !m_section) { + throw Base::RuntimeError("TaskComplexSection - new section object not found"); + } + if (m_baseView) { + m_section->Source.setValues(m_baseView->Source.getValues()); + m_section->XSource.setValues(m_baseView->XSource.getValues()); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.BaseView = App.ActiveDocument.%s", + m_sectionName.c_str(), m_baseView->getNameInDocument()); + + } else { + m_section->Source.setValues(m_shapes); + m_section->XSource.setValues(m_xShapes); + } + m_section->CuttingToolWireObject.setValue(m_profileObject); + } + Gui::Command::commitCommand(); + if (m_section) { + m_section->recomputeFeature(); + } + return; +} + +void TaskComplexSection::saveButtons(QPushButton* btnOK, + QPushButton* btnCancel) +{ + m_btnOK = btnOK; + m_btnCancel = btnCancel; +} + +void TaskComplexSection::enableTaskButtons(bool button) +{ + m_btnOK->setEnabled(button); + m_btnCancel->setEnabled(button); +} + +//****************************************************************************** +bool TaskComplexSection::accept() +{ + Gui::Document* doc = Gui::Application::Instance->getDocument(m_page->getDocument()); + if (!doc) + return false; + + createComplexSection(); + + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + + return true; +} + +bool TaskComplexSection::reject() +{ + Gui::Document* doc = Gui::Application::Instance->getDocument(m_page->getDocument()); + if (!doc) + return false; + + //make sure any dangling objects are cleaned up + Gui::Command::doCommand(Gui::Command::Gui, "App.activeDocument().recompute()"); + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TaskDlgComplexSection::TaskDlgComplexSection(TechDraw::DrawPage* page, + TechDraw::DrawViewPart *baseView, + std::vector shapes, + std::vector xShapes, + App::DocumentObject* profileObject, + std::vector profileSubs) + : TaskDialog() +{ + widget = new TaskComplexSection(page, baseView, shapes, xShapes, profileObject, profileSubs); + taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("actions/TechDraw_ComplexSection"), + widget->windowTitle(), true, nullptr); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + +TaskDlgComplexSection::~TaskDlgComplexSection() +{ +} + +void TaskDlgComplexSection::update() +{ +// widget->updateTask(); +} + +void TaskDlgComplexSection::modifyStandardButtons(QDialogButtonBox* box) +{ + QPushButton* btnOK = box->button(QDialogButtonBox::Ok); + QPushButton* btnCancel = box->button(QDialogButtonBox::Cancel); + widget->saveButtons(btnOK, btnCancel); +} + +//==== calls from the TaskView =============================================================== +void TaskDlgComplexSection::open() +{ +} + +void TaskDlgComplexSection::clicked(int) +{ +} + +bool TaskDlgComplexSection::accept() +{ + widget->accept(); + return true; +} + +bool TaskDlgComplexSection::reject() +{ + widget->reject(); + return true; +} + +#include diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.h b/src/Mod/TechDraw/Gui/TaskComplexSection.h new file mode 100644 index 0000000000..744936ea2b --- /dev/null +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.h @@ -0,0 +1,137 @@ +/*************************************************************************** + * 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 TECHDRAWGUI_TASKCOMPLEXSECTION_H +#define TECHDRAWGUI_TASKCOMPLEXSECTION_H + +#include + +#include + +#include +#include +#include + +namespace App +{ +class DocumentObject; +} + +namespace TechDraw +{ +class DrawPage; +class DrawView; +class DrawViewPart; +class DrawComplexSection; +} + +namespace TechDrawGui +{ +class Ui_TaskComplexSection; + +class TaskComplexSection : public QWidget +{ + Q_OBJECT + +public: + TaskComplexSection(TechDraw::DrawPage* page, + TechDraw::DrawViewPart* baseView, + std::vector shapes, + std::vector xShapes, + App::DocumentObject* profileObject, + std::vector profileSubs); + ~TaskComplexSection() = default; + + virtual bool accept(); + virtual bool reject(); + void saveButtons(QPushButton* btnOK, + QPushButton* btnCancel); + void enableTaskButtons(bool button); +public Q_SLOTS: + void onSectionObjectsUseSelectionClicked(); + void onProfileObjectsUseSelectionClicked(); + +protected: + void changeEvent(QEvent *event) override; + + void setUiPrimary(); + void updateUi(); + +private: + void createComplexSection(); + QString sourcesToString(); + std::unique_ptr ui; + + TechDraw::DrawPage* m_page; + TechDraw::DrawViewPart* m_baseView; + TechDraw::DrawComplexSection* m_section; + std::vector m_shapes; + std::vector m_xShapes; + App::DocumentObject* m_profileObject; + std::vector m_profileSubs; + std::string m_sectionName; + Base::Vector3d m_saveXDir; + + QPushButton* m_btnOK; + QPushButton* m_btnCancel; + +}; + +class TaskDlgComplexSection : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskDlgComplexSection(TechDraw::DrawPage* page, + TechDraw::DrawViewPart* baseView, + std::vector shapes, + std::vector xShapes, + App::DocumentObject* profileObject, + std::vector profileSubs); + ~TaskDlgComplexSection() override; + +public: + /// is called the TaskView when the dialog is opened + void open() override; + /// is called by the framework if an button is clicked which has no accept or reject role + void clicked(int) override; + /// is called by the framework if the dialog is accepted (Ok) + bool accept() override; + /// is called by the framework if the dialog is rejected (Cancel) + bool reject() override; + /// is called by the framework if the user presses the help button + bool isAllowedAlterDocument() const override + { return false; } + void update(); + + void modifyStandardButtons(QDialogButtonBox* box) override; + +protected: + +private: + TaskComplexSection * widget; + Gui::TaskView::TaskBox* taskbox; +}; + +} //namespace TechDrawGui + +#endif // #ifndef TECHDRAWGUI_TASKCOMPLEXSECTION_H diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.ui b/src/Mod/TechDraw/Gui/TaskComplexSection.ui new file mode 100644 index 0000000000..85ab665226 --- /dev/null +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.ui @@ -0,0 +1,296 @@ + + + TechDrawGui::TaskComplexSection + + + + 0 + 0 + 370 + 478 + + + + + 0 + 0 + + + + Complex Section + + + + :/icons/actions/TechDraw_ComplexSection.svg:/icons/actions/TechDraw_ComplexSection.svg + + + + + + + 0 + 0 + + + + Object Selection + + + + + + + + Objects to section + + + + + + + Use Selection + + + + + + + Profile object + + + + + + + true + + + + + + + true + + + + + + + Use Selection + + + + + + + + + + + + Section Parameters + + + + + + + + Identifier + + + + + + + + 0 + 26 + + + + Scale Page/Auto/Custom + + + + Page + + + + + Automatic + + + + + Custom + + + + + + + + + 0 + 26 + + + + 1.000000000000000 + + + + + + + Scale Type + + + + + + + Scale + + + + + + + + 0 + 26 + + + + Identifier for this section + + + + + + + Projection Strategy + + + + + + + + 0 + 0 + + + + + 0 + 26 + + + + + 0 + 0 + + + + Single + + + 0 + + + + Single + + + + + Piecewise + + + + + NoParallel + + + + + + + + + + + + + Section Normal + + + + + + + + Z + + + + + + + X + + + + + + + Y + + + + + + + -2147483647.000000000000000 + + + 2147483647.000000000000000 + + + + + + + -2147483647.000000000000000 + + + 2147483647.000000000000000 + + + + + + + -2147483647.000000000000000 + + + 2147483647.000000000000000 + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp b/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp index 1e8e806db6..dc7fb6c202 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp @@ -76,6 +76,7 @@ ViewProviderViewPart::ViewProviderViewPart() static const char *group = "Lines"; static const char *dgroup = "Decoration"; static const char *hgroup = "Highlight"; + static const char *sgroup = "Section Line"; //default line weights @@ -104,12 +105,14 @@ ViewProviderViewPart::ViewProviderViewPart() ADD_PROPERTY_TYPE(CenterScale, (defScale), dgroup, App::Prop_None, "Center mark size adjustment, if enabled"); //properties that affect Section Line - ADD_PROPERTY_TYPE(ShowSectionLine ,(true) ,dgroup, App::Prop_None, "Show/hide section line if applicable"); + ADD_PROPERTY_TYPE(ShowSectionLine ,(true) ,sgroup, App::Prop_None, "Show/hide section line if applicable"); SectionLineStyle.setEnums(LineStyleEnums); - ADD_PROPERTY_TYPE(SectionLineStyle, (PreferencesGui::sectionLineStyle()), dgroup, App::Prop_None, + ADD_PROPERTY_TYPE(SectionLineStyle, (PreferencesGui::sectionLineStyle()), sgroup, App::Prop_None, "Set section line style if applicable"); - ADD_PROPERTY_TYPE(SectionLineColor, (prefSectionColor()), dgroup, App::Prop_None, + ADD_PROPERTY_TYPE(SectionLineColor, (prefSectionColor()), sgroup, App::Prop_None, "Set section line color if applicable"); + ADD_PROPERTY_TYPE(SectionLineMarks, (PreferencesGui::sectionLineMarks()), sgroup, App::Prop_None, + "Show marks at direction changes for ComplexSection"); //properties that affect Detail Highlights HighlightLineStyle.setEnums(LineStyleEnums); @@ -139,6 +142,7 @@ void ViewProviderViewPart::onChanged(const App::Property* prop) prop == &(ShowSectionLine) || prop == &(SectionLineStyle) || prop == &(SectionLineColor) || + prop == &(SectionLineMarks) || prop == &(HighlightLineStyle) || prop == &(HighlightLineColor) || prop == &(HorizCenterLine) || diff --git a/src/Mod/TechDraw/Gui/ViewProviderViewPart.h b/src/Mod/TechDraw/Gui/ViewProviderViewPart.h index c0ec6602ac..69bfa031c0 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderViewPart.h +++ b/src/Mod/TechDraw/Gui/ViewProviderViewPart.h @@ -22,8 +22,8 @@ #ifndef DRAWINGGUI_VIEWPROVIDERVIEWPART_H #define DRAWINGGUI_VIEWPROVIDERVIEWPART_H - -#include + +#include #include @@ -55,6 +55,7 @@ public: App::PropertyBool ShowSectionLine; App::PropertyEnumeration SectionLineStyle; App::PropertyColor SectionLineColor; + App::PropertyBool SectionLineMarks; App::PropertyEnumeration HighlightLineStyle; App::PropertyColor HighlightLineColor; App::PropertyFloat HighlightAdjust; diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index 3c41a7d4a7..ec7e6628aa 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -189,6 +189,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *draw << "TechDraw_ActiveView"; *draw << "TechDraw_ProjectionGroup"; *draw << "TechDraw_SectionView"; + *draw << "TechDraw_ComplexSection"; *draw << "TechDraw_DetailView"; *draw << "Separator"; *draw << "TechDraw_DraftView"; @@ -246,6 +247,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *views << "TechDraw_ActiveView"; *views << "TechDraw_ProjectionGroup"; *views << "TechDraw_SectionView"; + *views << "TechDraw_ComplexSection"; *views << "TechDraw_DetailView"; *views << "TechDraw_DraftView"; *views << "TechDraw_ArchView"; @@ -392,6 +394,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const // *views << "TechDraw_NewMulti"; //deprecated *views << "TechDraw_ProjectionGroup"; *views << "TechDraw_SectionView"; + *views << "TechDraw_ComplexSection"; *views << "TechDraw_DetailView"; *views << "TechDraw_DraftView"; *views << "TechDraw_SpreadsheetView";