From 99f67b8a4011847404d217cf739cd6159cbdc0ff Mon Sep 17 00:00:00 2001 From: wandererfan Date: Thu, 13 Oct 2022 10:59:54 -0400 Subject: [PATCH] [TD]SectionView/ComplexSection - preview, direction selection - add preview/live update to TaskSectionView & TaskComplexSection - add view direction selector to uis - use SectionNormal as Direction. Make Direction read-only - simplify section line end point calculation - section group command in toolbar - make section and complex section icons consistent - fix compsolid cutting tool - terminology: single/piecewise to offset/aligned --- src/Mod/TechDraw/App/DrawComplexSection.cpp | 568 +++++------ src/Mod/TechDraw/App/DrawComplexSection.h | 92 +- src/Mod/TechDraw/App/DrawUtil.cpp | 19 +- src/Mod/TechDraw/App/DrawViewPart.cpp | 84 +- src/Mod/TechDraw/App/DrawViewPart.h | 4 + src/Mod/TechDraw/App/DrawViewSection.cpp | 138 ++- src/Mod/TechDraw/App/DrawViewSection.h | 11 +- src/Mod/TechDraw/App/GeometryObject.cpp | 2 +- src/Mod/TechDraw/Gui/CMakeLists.txt | 6 + src/Mod/TechDraw/Gui/Command.cpp | 234 +++-- src/Mod/TechDraw/Gui/QGIViewPart.cpp | 16 +- .../icons/actions/TechDraw_ComplexSection.svg | 952 ++++++++++-------- .../icons/actions/TechDraw_SectionView.svg | 890 +++++++++++++--- src/Mod/TechDraw/Gui/TaskComplexSection.cpp | 610 +++++++++-- src/Mod/TechDraw/Gui/TaskComplexSection.h | 68 +- src/Mod/TechDraw/Gui/TaskComplexSection.ui | 278 +++-- src/Mod/TechDraw/Gui/TaskSectionView.cpp | 344 ++++--- src/Mod/TechDraw/Gui/TaskSectionView.h | 22 +- src/Mod/TechDraw/Gui/TaskSectionView.ui | 92 +- .../TechDraw/Gui/ViewProviderViewSection.cpp | 9 +- .../Gui/Widgets/CompassDialWidget.cpp | 242 +++++ .../TechDraw/Gui/Widgets/CompassDialWidget.h | 71 ++ .../TechDraw/Gui/Widgets/CompassWidget.cpp | 254 +++++ src/Mod/TechDraw/Gui/Widgets/CompassWidget.h | 105 ++ .../TechDraw/Gui/Widgets/VectorEditWidget.cpp | 265 +++++ .../TechDraw/Gui/Widgets/VectorEditWidget.h | 107 ++ src/Mod/TechDraw/Gui/Workbench.cpp | 11 +- 27 files changed, 4104 insertions(+), 1390 deletions(-) create mode 100644 src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.cpp create mode 100644 src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.h create mode 100644 src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp create mode 100644 src/Mod/TechDraw/Gui/Widgets/CompassWidget.h create mode 100644 src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.cpp create mode 100644 src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.h diff --git a/src/Mod/TechDraw/App/DrawComplexSection.cpp b/src/Mod/TechDraw/App/DrawComplexSection.cpp index a2559dd2b1..802ae65c35 100644 --- a/src/Mod/TechDraw/App/DrawComplexSection.cpp +++ b/src/Mod/TechDraw/App/DrawComplexSection.cpp @@ -20,8 +20,8 @@ * * ***************************************************************************/ //DrawComplexSection processing overview -//for Strategy = Single, DCS is much the same as DVS -//for Strategy = PieceWise, there are many differences +//for Strategy = Offset, DCS is much the same as DVS +//for Strategy = Aligned, there are many differences //execute // sectionExec(getShapeToCut()*) @@ -30,7 +30,7 @@ // makeSectionCut(baseShape) //makeSectionCut (separate thread) -// note that the section cut is not required for Piecewise strategy, +// note that the section cut is not required for Aligned strategy, // but it is useful for debugging // m_cuttingTool = makeCuttingTool* (DVSTool.brep) // m_cutPieces = (baseShape - m_cuttingTool) (DVSCutPieces.brep) @@ -44,10 +44,10 @@ // m_sectionTopoDSFaces = alignSectionFaces(faceIntersections) // m_tdSectionFaces = makeTDSectionFaces(m_sectionTopoDSFaces) -//* for Piecewise, we use a different ShapeToCut, as the standard one will +//* for Aligned, 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 +//* for Aligned, preparing the shape is much different than Offset 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 @@ -57,44 +57,46 @@ #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 +#include +#include +#include +#include +#include #endif -#define _USE_MATH_DEFINES // for C++ +#define _USE_MATH_DEFINES #include #include @@ -104,17 +106,17 @@ #include #include #include -#include #include +#include #include #include #include #include +#include "DrawComplexSection.h" #include "DrawUtil.h" #include "GeometryObject.h" -#include "DrawComplexSection.h" using namespace TechDraw; using namespace std; @@ -151,27 +153,26 @@ void ChangePoint::scale(double scaleFactor) PROPERTY_SOURCE(TechDraw::DrawComplexSection, TechDraw::DrawViewSection) -const char* DrawComplexSection::ProjectionStrategyEnums[] = { "Single", - "Piecewise", - "NoParallel", - nullptr }; +const char *DrawComplexSection::ProjectionStrategyEnums[] = {"Offset", "Aligned", "NoParallel", + nullptr}; -DrawComplexSection::DrawComplexSection() : - m_toolFaceShape(TopoDS_Shape()), - m_profileWire(TopoDS_Wire()) +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"); + 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"); + 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(); + // Base::Console().Message("DCS::getShapeToCut()\n"); + App::DocumentObject *base = BaseView.getValue(); TopoDS_Shape shapeToCut; if (base && base == this) { shapeToCut = getSourceShape(); @@ -180,8 +181,9 @@ TopoDS_Shape DrawComplexSection::getShapeToCut() } return shapeToCut; } - if (!base || - !base->getTypeId().isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { //is second clause necessary? + 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()) { @@ -195,15 +197,15 @@ TopoDS_Shape DrawComplexSection::getShapeToCut() void DrawComplexSection::makeSectionCut(TopoDS_Shape &baseShape) { -// Base::Console().Message("DCS::makeSectionCut() - %s\n", getNameInDocument(), baseShape.IsNull()); + // Base::Console().Message("DCS::makeSectionCut() - %s\n", getNameInDocument(), baseShape.IsNull()); if (ProjectionStrategy.getValue() == 0) { - //Single. Use regular section behaviour + //Offset. Use regular section behaviour DrawViewSection::makeSectionCut(baseShape); return; } - //Piecewise strategy + //Aligned strategy if (debugSection()) { - //only useful for debugging with Piecewise strategy + //only useful for debugging with Aligned strategy DrawViewSection::makeSectionCut(baseShape); } return; @@ -211,8 +213,8 @@ void DrawComplexSection::makeSectionCut(TopoDS_Shape &baseShape) TopoDS_Shape DrawComplexSection::makeCuttingTool(double dMax) { -// Base::Console().Message("DCS::makeCuttingTool()\n"); - App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); + // Base::Console().Message("DCS::makeCuttingTool()\n"); + App::DocumentObject *toolObj = CuttingToolWireObject.getValue(); if (!isProfileObject(toolObj)) { return TopoDS_Shape(); } @@ -220,16 +222,14 @@ TopoDS_Shape DrawComplexSection::makeCuttingTool(double dMax) m_profileWire = profileWire; BRepBuilderAPI_Copy BuilderCopy(profileWire); m_profileWire = TopoDS::Wire(BuilderCopy.Shape()); - if (debugSection()){ + if (debugSection()) { //the nose to tail version of the profile - BRepTools::Write(m_profileWire, "DCSProfileWire.brep"); //debug + 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); + 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", @@ -242,12 +242,12 @@ TopoDS_Shape DrawComplexSection::makeCuttingTool(double dMax) TopLoc_Location loc(mov); profileWire.Move(loc); - gp_Vec extrudeDir(0.0, 0.0, 1.0); //arbitrary default + 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()) { + if (toolFace.IsNull()) { return TopoDS_Shape(); } gp_Dir gpNormal = getFaceNormal(toolFace); @@ -256,78 +256,90 @@ TopoDS_Shape DrawComplexSection::makeCuttingTool(double dMax) } //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 + //in the direction of gClosestBasis , then extrude the face in the direction of the section normal m_toolFaceShape = extrudeWireToFace(profileWire, gClosestBasis, 2.0 * dMax); - if (debugSection()){ - BRepTools::Write(m_toolFaceShape, "DCSToolFaceShape.brep"); //debug + if (debugSection()) { + BRepTools::Write(m_toolFaceShape, "DCSToolFaceShape.brep");//debug } extrudeDir = dMax * sectionCS.Direction(); + TopoDS_Shape roughTool = BRepPrimAPI_MakePrism(m_toolFaceShape, extrudeDir).Shape(); + if (roughTool.ShapeType() == TopAbs_COMPSOLID) { + //Composite Solids do not cut well if they contain "solids" with no volume. This + //happens if the profile has segments parallel to the extrude direction. + //We need to disassemble it and only keep the real solids. + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + TopExp_Explorer expSolids(roughTool, TopAbs_SOLID); + for (; expSolids.More(); expSolids.Next()) { + TopoDS_Solid solid = TopoDS::Solid(expSolids.Current()); + GProp_GProps gprops; + BRepGProp::VolumeProperties(solid, gprops); + double volume = gprops.Mass(); + if (volume > EWTOLERANCE) { + builder.Add(comp, solid); + } + } + return comp; + } + return BRepPrimAPI_MakePrism(m_toolFaceShape, extrudeDir).Shape(); } TopoDS_Shape DrawComplexSection::getShapeToPrepare() const { if (ProjectionStrategy.getValue() == 0) { - //Single. Use regular section behaviour + //Offset. Use regular section behaviour return DrawViewSection::getShapeToPrepare(); } - //Piecewise strategy - return m_saveShape; //the original input shape + //Aligned 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) +TopoDS_Shape DrawComplexSection::prepareShape(const TopoDS_Shape &cutShape, double shapeSize) { -// Base::Console().Message("DCS::prepareShape() - strat: %d\n", ProjectionStrategy.getValue()); + // Base::Console().Message("DCS::prepareShape() - strat: %d\n", ProjectionStrategy.getValue()); if (ProjectionStrategy.getValue() == 0) { - //Single. Use regular section behaviour + //Offset. Use regular section behaviour return DrawViewSection::prepareShape(cutShape, shapeSize); } - //"Piecewise" projection (Aligned Section) - TopoDS_Shape alignedResult = makeAlignedPieces(cutShape, - m_toolFaceShape, - shapeSize); + //"Aligned" 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; + return scaleShape(alignedResult, getScale()); } -//for Piecewise strategy, cut the rawShape by each segment of the tool +//for Aligned 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"); + // 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 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; + //make a "real" CS from the section projection CS 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 + 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()); + Base::Console().Error( + "DCS::makeAlignedPieces - %s - profile is parallel to SectionNormal\n", + getNameInDocument()); throw Base::RuntimeError("Profile orientation error"); } @@ -344,12 +356,12 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, isVertical = false; } - double leftToRight = 1.0; //profile vector points to right, so we move to right + 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 + 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; @@ -369,8 +381,8 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, 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) { + if (segmentNormal.Dot(-gProjectionUnit) != 0.0 + && segmentNormal.Angle(gProjectionUnit) <= M_PI_2) { segmentNormal.Reverse(); } @@ -378,45 +390,26 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, 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); + 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 + 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 @@ -427,11 +420,7 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, 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); @@ -446,11 +435,9 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, //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" + 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; + 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()); @@ -461,11 +448,6 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, 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); @@ -500,67 +482,62 @@ TopoDS_Shape DrawComplexSection::makeAlignedPieces(const TopoDS_Shape &rawShape, 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); - } + 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); + TopoDS_Shape centeredCompound = moveShape(comp, centerVector); if (debugSection()) { - BRepTools::Write(centeredCompound, "DCSfinCenteredCompound.brep"); //debug + 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 + BRepTools::Write(alignedCompound, "DCSAlignedResult.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 +//! Aligned 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) +TopoDS_Compound +DrawComplexSection::findSectionPlaneIntersections(const TopoDS_Shape &shapeToIntersect) { -// Base::Console().Message("DCS::findSectionPlaneIntersections() - %s\n", getNameInDocument()); - if(cutShape.IsNull()){ + // Base::Console().Message("DCS::findSectionPlaneIntersections() - %s\n", getNameInDocument()); + if (shapeToIntersect.IsNull()) { // this shouldn't happen - Base::Console().Warning("DCS::findSectionPlaneInter - %s - cut shape is Null\n", getNameInDocument()); + Base::Console().Warning("DCS::findSectionPlaneInter - %s - cut shape is Null\n", + getNameInDocument()); return TopoDS_Compound(); } - if (ProjectionStrategy.getValue() == 0) { //Single - return singleToolIntersections(cutShape); + if (ProjectionStrategy.getValue() == 0) {//Offset + return singleToolIntersections(shapeToIntersect); } - return piecewiseToolIntersections(cutShape); + return piecewiseToolIntersections(shapeToIntersect); } //Intersect cutShape with each segment of the cutting tool -TopoDS_Compound DrawComplexSection::singleToolIntersections(const TopoDS_Shape& cutShape) +TopoDS_Compound DrawComplexSection::singleToolIntersections(const TopoDS_Shape &cutShape) { -// Base::Console().Message("DCS::singleToolInterSections()\n"); - App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); + // 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"); + // Base::Console().Message("DCS::singleToolIntersection - profile has curves\n"); } BRep_Builder builder; @@ -568,8 +545,8 @@ TopoDS_Compound DrawComplexSection::singleToolIntersections(const TopoDS_Shape& builder.MakeCompound(result); if (debugSection()) { - BRepTools::Write(cutShape, "DCSSingleCutShape.brep"); //debug - BRepTools::Write(m_toolFaceShape, "DCSSingleCuttingToolFace.brep"); //debug + BRepTools::Write(cutShape, "DCSOffsetCutShape.brep"); //debug + BRepTools::Write(m_toolFaceShape, "DCSOffsetCuttingToolFace.brep");//debug } if (m_toolFaceShape.IsNull()) { @@ -577,66 +554,60 @@ TopoDS_Compound DrawComplexSection::singleToolIntersections(const TopoDS_Shape& } TopExp_Explorer expFaces(cutShape, TopAbs_FACE); - for ( ; expFaces.More(); expFaces.Next()) { + 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); } + 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) +TopoDS_Compound DrawComplexSection::piecewiseToolIntersections(const TopoDS_Shape &cutShape) { -// Base::Console().Message("DCS::piecewiseToolIntersections()\n"); + // Base::Console().Message("DCS::piecewiseToolIntersections()\n"); BRep_Builder builder; TopoDS_Compound result; builder.MakeCompound(result); - App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); + App::DocumentObject *toolObj = CuttingToolWireObject.getValue(); if (!isLinearProfile(toolObj)) { //TODO: special handling here -// Base::Console().Message("DCS::pieceWiseToolIntersection - profile has curves\n"); + // 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()); + -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()) { + 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); } + 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 + if (debugSection()) { + BRepTools::Write(cuttingFace, "DCSAlignedCuttingFace.brep"); //debug + BRepTools::Write(cutShape, "DCSAlignedCutShape.brep"); //debug + BRepTools::Write(result, "DCSAlignedIntersectionResult.brep");//debug } return result; } TopoDS_Compound DrawComplexSection::alignSectionFaces(TopoDS_Shape faceIntersections) { -// Base::Console().Message("DCS::alignSectionFaces()\n"); + // Base::Console().Message("DCS::alignSectionFaces()\n"); if (ProjectionStrategy.getValue() == 0) { - //Single. Use regular section behaviour + //Offset. Use regular section behaviour return DrawViewSection::alignSectionFaces(faceIntersections); } @@ -645,14 +616,14 @@ TopoDS_Compound DrawComplexSection::alignSectionFaces(TopoDS_Shape faceIntersect TopoDS_Shape DrawComplexSection::getShapeToIntersect() { - if (ProjectionStrategy.getValue() == 0) { //Single + if (ProjectionStrategy.getValue() == 0) {//Offset return DrawViewSection::getShapeToIntersect(); } - //Piecewise - return m_preparedShape; + //Aligned + return m_preparedShape; } -TopoDS_Wire DrawComplexSection::makeProfileWire(App::DocumentObject* toolObj) +TopoDS_Wire DrawComplexSection::makeProfileWire(App::DocumentObject *toolObj) { TopoDS_Shape toolShape = Part::Feature::getShape(toolObj); if (toolShape.IsNull()) { @@ -662,7 +633,8 @@ TopoDS_Wire DrawComplexSection::makeProfileWire(App::DocumentObject* toolObj) 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 + } + 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(); } @@ -674,32 +646,30 @@ TopoDS_Wire DrawComplexSection::makeProfileWire(App::DocumentObject* toolObj) //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"); + // 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()) { + for (; expEdges.More(); expEdges.Next()) { projectedSegment = TopoDS::Edge(expEdges.Current()); break; } - if (debugSection()){ - BRepTools::Write(projectedSegment, "DCSprojectedSegment.brep"); //debug + 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_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); @@ -708,15 +678,15 @@ gp_Dir DrawComplexSection::projectProfileWire(TopoDS_Wire profileWire, gp_Ax3 pa //make drawable td geometry for section line BaseGeomPtrVector DrawComplexSection::makeSectionLineGeometry() { -// Base::Console().Message("DCS::makeSectionLineGeometry()\n"); + // Base::Console().Message("DCS::makeSectionLineGeometry()\n"); BaseGeomPtrVector result; - DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue()); + DrawViewPart *baseDvp = dynamic_cast(BaseView.getValue()); if (baseDvp) { TopoDS_Wire lineWire = makeSectionLineWire(); - TopoDS_Shape projectedWire = GeometryObject::projectSimpleShape(lineWire, - baseDvp->getProjectionCS()); + TopoDS_Shape projectedWire = + GeometryObject::projectSimpleShape(lineWire, baseDvp->getProjectionCS()); TopExp_Explorer expEdges(projectedWire, TopAbs_EDGE); - for ( ; expEdges.More(); expEdges.Next()) { + for (; expEdges.More(); expEdges.Next()) { BaseGeomPtr edge = BaseGeom::baseFactory(TopoDS::Edge(expEdges.Current())); result.push_back(edge); } @@ -727,7 +697,7 @@ BaseGeomPtrVector DrawComplexSection::makeSectionLineGeometry() //get the end points of the section wire std::pair DrawComplexSection::sectionLineEnds() { -// Base::Console().Message("DCS::sectionLineEnds()\n"); + // Base::Console().Message("DCS::sectionLineEnds()\n"); std::pair result; TopoDS_Wire lineWire = makeSectionLineWire(); if (lineWire.IsNull()) { @@ -735,13 +705,13 @@ std::pair DrawComplexSection::sectionLineEnds() } TopoDS_Vertex vFirst, vLast; - TopExp::Vertices (lineWire, 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()); + DrawViewPart *baseDvp = dynamic_cast(BaseView.getValue()); if (baseDvp) { first = baseDvp->projectPoint(first); last = baseDvp->projectPoint(last); @@ -754,22 +724,21 @@ std::pair DrawComplexSection::sectionLineEnds() //get directions of the section line arrows std::pair DrawComplexSection::sectionArrowDirs() { -// Base::Console().Message("DCS::sectionArrowDirs()\n"); + // Base::Console().Message("DCS::sectionArrowDirs()\n"); std::pair result; - App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); + App::DocumentObject *toolObj = CuttingToolWireObject.getValue(); TopoDS_Wire profileWire = makeProfileWire(toolObj); if (profileWire.IsNull()) { return result; } TopoDS_Vertex tvFirst, tvLast; - TopExp::Vertices (profileWire, 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); + gp_Vec gSectionNormal = gp_Vec(DU::togp_Dir(SectionNormal.getValue())); + gp_Vec gExtrudeVector = (gSectionNormal.Crossed(gProfileVector)).Normalized(); Base::Vector3d vClosestBasis = DrawUtil::closestBasis(gp_Dir(gExtrudeVector), getSectionCS()); gp_Dir gExtrudeDir = gp_Dir(vClosestBasis.x, vClosestBasis.y, vClosestBasis.z); @@ -778,28 +747,32 @@ std::pair DrawComplexSection::sectionArrowDirs() 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())); - } + 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())); + if (gDir0.Dot(gSectionNormal) > 0.0) { + //face normal is pointing generally in section normal direction, so we + //want the reverse for a view direction + gDir0.Reverse(); + } + if (gDir1.Dot(gSectionNormal) > 0.0) { + //face normal is pointing generally in section normal direction, so we + //want the reverse for a view direction + gDir1.Reverse(); + } //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()); + Base::Vector3d vDir0 = DU::toVector3d(gDir0); + Base::Vector3d vDir1 = DU::toVector3d(gDir1); vDir0.Normalize(); vDir1.Normalize(); - DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue()); + DrawViewPart *baseDvp = dynamic_cast(BaseView.getValue()); if (baseDvp) { - vDir0 = baseDvp->projectPoint(vDir0); - vDir1 = baseDvp->projectPoint(vDir1); + vDir0 = baseDvp->projectPoint(vDir0, true); + vDir1 = baseDvp->projectPoint(vDir1, true); } result.first = vDir0; @@ -811,26 +784,26 @@ std::pair DrawComplexSection::sectionArrowDirs() TopoDS_Wire DrawComplexSection::makeSectionLineWire() { TopoDS_Wire lineWire; - App::DocumentObject* toolObj = CuttingToolWireObject.getValue(); - DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue()); + 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()); + 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) { + } + else if (sScaled.ShapeType() == TopAbs_EDGE) { TopoDS_Edge edge = TopoDS::Edge(sScaled); lineWire = BRepBuilderAPI_MakeWire(edge).Wire(); - } else { + } + 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()); + Base::Console().Message("DCS::makeSectionLineGeometry - profile is type: %d\n", + sScaled.ShapeType()); return TopoDS_Wire(); } } @@ -841,21 +814,21 @@ TopoDS_Wire DrawComplexSection::makeSectionLineWire() //of the profile before and after the point ChangePointVector DrawComplexSection::getChangePointsFromSectionLine() { -// Base::Console().Message("DCS::getChangePointsFromSectionLine()\n"); + // Base::Console().Message("DCS::getChangePointsFromSectionLine()\n"); ChangePointVector result; std::vector allPoints; - DrawViewPart* baseDvp = dynamic_cast(BaseView.getValue()); + DrawViewPart *baseDvp = dynamic_cast(BaseView.getValue()); if (baseDvp) { TopoDS_Wire lineWire = makeSectionLineWire(); - TopoDS_Shape projectedWire = GeometryObject::projectSimpleShape(lineWire, - baseDvp->getProjectionCS()); + 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()) { + 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)) { @@ -891,27 +864,29 @@ ChangePointVector DrawComplexSection::getChangePointsFromSectionLine() return result; } - gp_Ax2 DrawComplexSection::getCSFromBase(const std::string sectionName) const +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? + // Base::Console().Message("DCS::getCSFromBase()\n"); + App::DocumentObject *base = BaseView.getValue(); + if (!base + || !base->getTypeId().isDerivedFrom( + TechDraw::DrawViewPart::getClassTypeId())) {//is second clause necessary? + //if this DCS does not have a baseView, we must use the existing SectionCS return getSectionCS(); } return DrawViewSection::getCSFromBase(sectionName); } -//get the "effective" (flattened) section plane for Piecewise and -//the regular sectionPlane for Single. +//get the "effective" (flattened) section plane for Aligned and +//the regular sectionPlane for Offset. gp_Pln DrawComplexSection::getSectionPlane() const { if (ProjectionStrategy.getValue() == 0) { - //Single. Use regular section behaviour + //Offset. Use regular section behaviour return DrawViewSection::getSectionPlane(); } - //"Piecewise" projection (Aligned Section) + //"Aligned" 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); @@ -923,7 +898,7 @@ gp_Pln DrawComplexSection::getSectionPlane() const bool DrawComplexSection::isBaseValid() const { - App::DocumentObject* base = BaseView.getValue(); + App::DocumentObject *base = BaseView.getValue(); if (!base) { //complex section is not based on an existing DVP return true; @@ -940,14 +915,13 @@ bool DrawComplexSection::isBaseValid() const //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 +bool DrawComplexSection::validateProfilePosition(TopoDS_Wire profileWire, gp_Ax2 sectionCS, + gp_Dir &gClosestBasis) const { -// Base::Console().Message("DCS::validateProfilePosition()\n"); + // 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 + 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()); @@ -970,14 +944,14 @@ bool DrawComplexSection::validateProfilePosition(TopoDS_Wire profileWire, spanLow = yMin; spanHigh = yMax; spanCheck = gpFirst.Y(); - } else if (gClosestBasis.IsParallel(sectionCS.Direction(), Precision::Angular())) { + } + else if (gClosestBasis.IsParallel(sectionCS.Direction(), Precision::Angular())) { spanLow = zMin; spanHigh = zMax; spanCheck = gpFirst.Z(); } - if (spanLow > spanCheck || - spanHigh < spanCheck) { + if (spanLow > spanCheck || spanHigh < spanCheck) { //profile is not within span of shape return false; } @@ -988,7 +962,7 @@ bool DrawComplexSection::validateProfilePosition(TopoDS_Wire profileWire, bool DrawComplexSection::showSegment(gp_Dir segmentNormal) const { if (ProjectionStrategy.getValue() < 2) { - //Single or Piecewise are always true + //Offset or Aligned are always true return true; } @@ -1006,8 +980,7 @@ bool DrawComplexSection::showSegment(gp_Dir segmentNormal) const //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, +TopoDS_Shape DrawComplexSection::extrudeWireToFace(TopoDS_Wire &wire, gp_Dir extrudeDir, double extrudeDist) { gp_Trsf mov; @@ -1022,54 +995,56 @@ TopoDS_Shape DrawComplexSection::extrudeWireToFace(TopoDS_Wire& wire, //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) +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 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); + gp_Dir normalDir(0.0, 0.0, 1.0);//default if (prop.IsNormalDefined()) { normalDir = prop.Normal(); } return normalDir; } -bool DrawComplexSection::boxesIntersect(TopoDS_Face& face, TopoDS_Shape& shape) +bool DrawComplexSection::boxesIntersect(TopoDS_Face &face, TopoDS_Shape &shape) { Bnd_Box box0, box1; BRepBndLib::Add(face, box0); - box0.SetGap(0.1); //generous + box0.SetGap(0.1);//generous BRepBndLib::Add(shape, box1); box1.SetGap(0.1); if (box0.IsOut(box1)) { - return false; //boxes don't intersect + return false;//boxes don't intersect } return true; } -TopoDS_Shape DrawComplexSection::shapeShapeIntersect(const TopoDS_Shape &shape0, const TopoDS_Shape &shape1) +TopoDS_Shape DrawComplexSection::shapeShapeIntersect(const TopoDS_Shape &shape0, + const TopoDS_Shape &shape1) { BRepAlgoAPI_Common anOp; - anOp.SetFuzzyValue (EWTOLERANCE); + anOp.SetFuzzyValue(EWTOLERANCE); TopTools_ListOfShape anArg1, anArg2; - anArg1.Append (shape0); - anArg2.Append (shape1); - anOp.SetArguments (anArg1); - anOp.SetTools (anArg2); + anArg1.Append(shape0); + anArg2.Append(shape1); + anOp.SetArguments(anArg1); + anOp.SetTools(anArg2); anOp.Build(); - return anOp.Shape(); //always a compound + 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) +std::vector DrawComplexSection::faceShapeIntersect(const TopoDS_Face &face, + const TopoDS_Shape &shape) { -// Base::Console().Message("DCS::faceShapeIntersect()\n"); + // Base::Console().Message("DCS::faceShapeIntersect()\n"); TopoDS_Shape intersect = shapeShapeIntersect(face, shape); if (intersect.IsNull()) { return std::vector(); @@ -1091,43 +1066,40 @@ TopoDS_Wire DrawComplexSection::makeNoseToTailWire(TopoDS_Wire inWire) std::list inList; TopExp_Explorer expEdges(inWire, TopAbs_EDGE); - for ( ; expEdges.More(); expEdges.Next()) { + 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) { + if (inList.empty() || inList.size() == 1) { return inWire; - } else { + } + else { sortedList = DrawUtil::sort_Edges(EWTOLERANCE, inList); } BRepBuilderAPI_MakeWire mkWire; - for (auto& edge : sortedList) { - mkWire.Add(edge); - } + for (auto &edge : sortedList) { mkWire.Add(edge); } return mkWire.Wire(); } //static -bool DrawComplexSection::isProfileObject(App::DocumentObject* obj) +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) { + 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) +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); @@ -1141,10 +1113,10 @@ bool DrawComplexSection::isMultiSegmentProfile(App::DocumentObject* obj) if (shape.ShapeType() == TopAbs_WIRE) { std::vector edgesInWire; TopExp_Explorer expEdges(shape, TopAbs_EDGE); - for ( ; expEdges.More(); expEdges.Next()) { + for (; expEdges.More(); expEdges.Next()) { TopoDS_Edge edge = TopoDS::Edge(expEdges.Current()); BRepAdaptor_Curve adapt(edge); - if (adapt.GetType() == GeomAbs_Line ) { + if (adapt.GetType() == GeomAbs_Line) { edgesInWire.push_back(edge); } } @@ -1156,7 +1128,7 @@ bool DrawComplexSection::isMultiSegmentProfile(App::DocumentObject* obj) } //check if the profile has curves in it -bool DrawComplexSection::isLinearProfile(App::DocumentObject* obj) +bool DrawComplexSection::isLinearProfile(App::DocumentObject *obj) { TopoDS_Shape shape = Part::Feature::getShape(obj); if (shape.IsNull()) { @@ -1166,8 +1138,8 @@ bool DrawComplexSection::isLinearProfile(App::DocumentObject* obj) //only have 1 edge TopoDS_Edge edge = TopoDS::Edge(shape); BRepAdaptor_Curve adapt(edge); - if (adapt.GetType() == GeomAbs_Line ) { - return true; + if (adapt.GetType() == GeomAbs_Line) { + return true; } return false; } @@ -1175,10 +1147,10 @@ bool DrawComplexSection::isLinearProfile(App::DocumentObject* obj) if (shape.ShapeType() == TopAbs_WIRE) { std::vector edgesInWire; TopExp_Explorer expEdges(shape, TopAbs_EDGE); - for ( ; expEdges.More(); expEdges.Next()) { + for (; expEdges.More(); expEdges.Next()) { TopoDS_Edge edge = TopoDS::Edge(expEdges.Current()); BRepAdaptor_Curve adapt(edge); - if (adapt.GetType() != GeomAbs_Line ) { + if (adapt.GetType() != GeomAbs_Line) { return false; } } @@ -1192,14 +1164,16 @@ bool DrawComplexSection::isLinearProfile(App::DocumentObject* obj) // Python Drawing feature --------------------------------------------------------- -namespace App { +namespace App +{ /// @cond DOXERR PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawComplexSectionPython, TechDraw::DrawComplexSection) -template<> const char* TechDraw::DrawComplexSectionPython::getViewProviderName() const { +template<> const char *TechDraw::DrawComplexSectionPython::getViewProviderName() const +{ return "TechDrawGui::ViewProviderDrawingView"; } /// @endcond // explicit template instantiation template class TechDrawExport FeaturePythonT; -} +}// namespace App diff --git a/src/Mod/TechDraw/App/DrawComplexSection.h b/src/Mod/TechDraw/App/DrawComplexSection.h index 5d973954d6..f12b18ec8c 100644 --- a/src/Mod/TechDraw/App/DrawComplexSection.h +++ b/src/Mod/TechDraw/App/DrawComplexSection.h @@ -25,10 +25,10 @@ #include -#include -#include #include #include +#include +#include #include "DrawViewSection.h" @@ -56,7 +56,7 @@ private: using ChangePointVector = std::vector; -class TechDrawExport DrawComplexSection : public DrawViewSection +class TechDrawExport DrawComplexSection: public DrawViewSection { PROPERTY_HEADER_WITH_OVERRIDE(Part::DrawComplexSection); @@ -64,66 +64,60 @@ public: DrawComplexSection(); ~DrawComplexSection() = default; - App::PropertyLink CuttingToolWireObject; - App::PropertyEnumeration ProjectionStrategy; //Single or Piecewise + App::PropertyLink CuttingToolWireObject; + App::PropertyEnumeration ProjectionStrategy;//Offset or Aligned - 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; + 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); + 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(); + 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(); + 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; + 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); + 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); + gp_Dir getFaceNormal(TopoDS_Face &face); TopoDS_Shape m_toolFaceShape; - TopoDS_Wire m_profileWire; + TopoDS_Wire m_profileWire; - static const char* ProjectionStrategyEnums[]; + static const char *ProjectionStrategyEnums[]; }; using DrawComplexSectionPython = App::FeaturePythonT; -} //namespace TechDraw +}//namespace TechDraw #endif - diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index b826bead0a..1d011c010b 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include "DrawUtil.h" @@ -350,19 +351,19 @@ Base::Vector3d DrawUtil::vertex2Vector(const TopoDS_Vertex& v) return Base::Vector3d(gp.X(), gp.Y(), gp.Z()); } +//TODO: make formatVector using toVector3d std::string DrawUtil::formatVector(const Base::Vector3d& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.x << ", " << v.y << ", " << v.z << ") "; -// builder << " (" << setw(6) << v.x << ", " << setw(6) << v.y << ", " << setw(6) << v.z << ") "; return builder.str(); } std::string DrawUtil::formatVector(const gp_Dir& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.X() << ", " << v.Y() << ", " << v.Z() << ") "; return builder.str(); } @@ -370,14 +371,14 @@ std::string DrawUtil::formatVector(const gp_Dir& v) std::string DrawUtil::formatVector(const gp_Dir2d& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.X() << ", " << v.Y() << ") "; return builder.str(); } std::string DrawUtil::formatVector(const gp_Vec& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.X() << ", " << v.Y() << ", " << v.Z() << ") "; return builder.str(); } @@ -385,7 +386,7 @@ std::string DrawUtil::formatVector(const gp_Vec& v) std::string DrawUtil::formatVector(const gp_Pnt& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.X() << ", " << v.Y() << ", " << v.Z() << ") "; return builder.str(); } @@ -393,7 +394,7 @@ std::string DrawUtil::formatVector(const gp_Pnt& v) std::string DrawUtil::formatVector(const gp_Pnt2d& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.X() << ", " << v.Y() << ") "; return builder.str(); } @@ -401,7 +402,7 @@ std::string DrawUtil::formatVector(const gp_Pnt2d& v) std::string DrawUtil::formatVector(const QPointF& v) { std::stringstream builder; - builder << std::fixed << std::setprecision(3) ; + builder << std::fixed << std::setprecision(Base::UnitsApi::getDecimals()) ; builder << " (" << v.x() << ", " << v.y() << ") "; return builder.str(); } @@ -587,7 +588,7 @@ Base::Vector3d DrawUtil::closestBasis(Base::Vector3d vDir, gp_Ax2 coordSys) Base::Vector3d DrawUtil::closestBasis(gp_Dir gDir, gp_Ax2 coordSys) { - gp_Dir xCS = coordSys.XDirection(); //these are unit vectors? + gp_Dir xCS = coordSys.XDirection(); gp_Dir yCS = coordSys.YDirection(); gp_Dir zCS = coordSys.Direction(); diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index edecf51e56..2cb20d6f82 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -382,11 +384,17 @@ void DrawViewPart::onHlrFinished(void) // Base::Console().Message("DVP::onHlrFinished() - %s\n", getNameInDocument()); //now that the new GeometryObject is fully populated, we can replace the old one - if (geometryObject) { - delete geometryObject; + if (geometryObject && + m_tempGeometryObject) { + delete geometryObject; //remove the old + } + if (m_tempGeometryObject) { + geometryObject = m_tempGeometryObject; //replace with new + m_tempGeometryObject = nullptr; //superfluous? + } + if (!geometryObject) { + throw Base::RuntimeError("DrawViewPart has lost its geometry"); } - geometryObject = m_tempGeometryObject; - m_tempGeometryObject = nullptr; //superfluous //the last hlr related task is to make a bbox of the results bbox = geometryObject->calcBoundingBox(); @@ -869,10 +877,55 @@ QRectF DrawViewPart::getRect() const return result; } +//returns a compound of all the visible projected edges +TopoDS_Shape DrawViewPart::getShape() const +{ + BRep_Builder builder; + TopoDS_Compound result; + builder.MakeCompound(result); + if (geometryObject) { + if (!geometryObject->getVisHard().IsNull()) { + builder.Add(result, geometryObject->getVisHard()); + } + if (!geometryObject->getVisOutline().IsNull()) { + builder.Add(result, geometryObject->getVisOutline()); + } + if (!geometryObject->getVisSeam().IsNull()) { + builder.Add(result, geometryObject->getVisSeam()); + } + if (!geometryObject->getVisSmooth().IsNull()) { + builder.Add(result, geometryObject->getVisSmooth()); + } + } + return result; +} + +//returns the (unscaled) size of the visible lines along the alignment vector +double DrawViewPart::getSizeAlongVector(Base::Vector3d alignmentVector) +{ + gp_Ax3 projectedCS3(getProjectionCS()); + projectedCS3.SetXDirection(DrawUtil::togp_Dir(alignmentVector)); + gp_Ax3 stdCS; //OXYZ + + gp_Trsf xPieceAlign; + xPieceAlign.SetTransformation(stdCS, projectedCS3); + BRepBuilderAPI_Transform mkTransAlign(getShape(), xPieceAlign); + TopoDS_Shape shapeAligned = mkTransAlign.Shape(); + + Bnd_Box shapeBox; + shapeBox.SetGap(0.0); + BRepBndLib::AddOptimal(shapeAligned, shapeBox); + double xMin = 0, xMax = 0, yMin = 0, yMax = 0, zMin = 0, zMax = 0; + shapeBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); + double shapeWidth((xMax - xMin) / getScale()); + return shapeWidth; +} + //used to project a pt (ex SectionOrigin) onto paper plane Base::Vector3d DrawViewPart::projectPoint(const Base::Vector3d& pt, bool invert) const { -// Base::Console().Message("DVP::projectPoint()\n"); +// Base::Console().Message("DVP::projectPoint(%s, %d\n", +// DrawUtil::formatVector(pt).c_str(), invert); Base::Vector3d stdOrg(0.0, 0.0, 0.0); gp_Ax2 viewAxis = getProjectionCS(stdOrg); gp_Pnt gPt(pt.x, pt.y, pt.z); @@ -953,6 +1006,27 @@ bool DrawViewPart::hasGeometry(void) const return false; } +//convert a vector in local XY coords into a coordinate sytem in global +//coordinates aligned to the vector. +//Note that this CS may not have the ideal XDirection for the derived view +//(likely a DrawViewSection) and the user may need to adjust the XDirection +//in the derived view. +gp_Ax2 DrawViewPart::localVectorToCS(const Base::Vector3d localUnit) const +{ + gp_Pnt stdOrigin(0.0, 0.0, 0.0); + gp_Ax2 dvpCS = getProjectionCS(DrawUtil::toVector3d(stdOrigin)); + gp_Vec gLocalUnit = DrawUtil::togp_Dir(localUnit); + gp_Vec gLocalX(-gLocalUnit.Y(), gLocalUnit.X(), 0.0); //clockwise perp for 2d + + gp_Ax3 OXYZ; + gp_Trsf xLocalOXYZ; + xLocalOXYZ.SetTransformation(OXYZ, gp_Ax3(dvpCS)); + gp_Vec gLocalUnitOXYZ = gLocalUnit.Transformed(xLocalOXYZ); + gp_Vec gLocalXOXYZ = gLocalX.Transformed(xLocalOXYZ); + + return { stdOrigin, gp_Dir(gLocalUnitOXYZ), gp_Dir(gLocalXOXYZ) }; +} + gp_Ax2 DrawViewPart::getProjectionCS(const Base::Vector3d pt) const { // Base::Console().Message("DVP::getProjectionCS() - %s - %s\n", getNameInDocument(), Label.getValue()); diff --git a/src/Mod/TechDraw/App/DrawViewPart.h b/src/Mod/TechDraw/App/DrawViewPart.h index d4171ef309..19e3caa1b4 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.h +++ b/src/Mod/TechDraw/App/DrawViewPart.h @@ -154,6 +154,7 @@ public: virtual Base::Vector3d getLegacyX(const Base::Vector3d& pt, const Base::Vector3d& axis, const bool flip = true) const; + gp_Ax2 localVectorToCS(const Base::Vector3d localUnit) const; bool handleFaces(); @@ -167,6 +168,9 @@ public: virtual TopoDS_Shape getSourceShapeFused() const; virtual std::vector getSourceShape2d() const; + TopoDS_Shape getShape() const; + double getSizeAlongVector(Base::Vector3d alignmentVector); + virtual void postHlrTasks(void); bool isIso() const; diff --git a/src/Mod/TechDraw/App/DrawViewSection.cpp b/src/Mod/TechDraw/App/DrawViewSection.cpp index cd0d9944c7..70638819e6 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.cpp +++ b/src/Mod/TechDraw/App/DrawViewSection.cpp @@ -84,16 +84,10 @@ #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 "DrawProjGroupItem.h" #include "GeometryObject.h" -#include "GeometryObject.h" -#include "HatchLine.h" #include "DrawViewSection.h" @@ -126,21 +120,29 @@ DrawViewSection::DrawViewSection() : { static const char *sgroup = "Section"; static const char *fgroup = "Cut Surface Format"; + static const char *ggroup = "Cut Operation"; + //general section properties ADD_PROPERTY_TYPE(SectionSymbol ,(""), sgroup, App::Prop_None, "The identifier for this section"); ADD_PROPERTY_TYPE(BaseView ,(nullptr), sgroup, App::Prop_None, "2D View source for this Section"); BaseView.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(SectionNormal ,(0, 0,1.0) ,sgroup, App::Prop_None, "Section Plane normal direction"); //direction of extrusion of cutting prism ADD_PROPERTY_TYPE(SectionOrigin ,(0, 0,0) ,sgroup, App::Prop_None, "Section Plane Origin"); - SectionDirection.setEnums(SectionDirEnums); - ADD_PROPERTY_TYPE(SectionDirection, ((long)0), sgroup, App::Prop_None, "Direction in Base View for this Section"); - ADD_PROPERTY_TYPE(FuseBeforeCut ,(false), sgroup, App::Prop_None, "Merge Source(s) into a single shape before cutting"); + //TODO: SectionDirection is a legacy from when SectionViews were only available along + //cardinal directions. It should be made obsolete and replaced with Aligned sections and + //local unit vectors. + SectionDirection.setEnums(SectionDirEnums); + ADD_PROPERTY_TYPE(SectionDirection, ((long)0), sgroup, App::Prop_None, "Orientation of this Section in the Base View"); + + //properties related to the cut operation + ADD_PROPERTY_TYPE(FuseBeforeCut ,(false), ggroup, App::Prop_None, "Merge Source(s) into a single shape before cutting"); + ADD_PROPERTY_TYPE(TrimAfterCut ,(false), ggroup, App::Prop_None, "Trim the resulting shape after the section cut"); + + //properties related to the display of the cut surface CutSurfaceDisplay.setEnums(CutSurfaceEnums); ADD_PROPERTY_TYPE(CutSurfaceDisplay, (prefCutSurface()), fgroup, App::Prop_None, "Appearance of Cut Surface"); - -//initialize these to defaults ADD_PROPERTY_TYPE(FileHatchPattern ,(DrawHatch::prefSvgHatch()), fgroup, App::Prop_None, "The hatch pattern file for the cut surface"); ADD_PROPERTY_TYPE(FileGeomPattern ,(DrawGeomHatch::prefGeomHatchFile()), fgroup, App::Prop_None, "The PAT pattern file for geometric hatching"); @@ -160,6 +162,8 @@ DrawViewSection::DrawViewSection() : SvgIncluded.setStatus(App::Property::ReadOnly, true); PatIncluded.setStatus(App::Property::ReadOnly, true); + //SectionNormal is used instead to Direction + Direction.setStatus(App::Property::ReadOnly, true); } DrawViewSection::~DrawViewSection() @@ -202,7 +206,9 @@ void DrawViewSection::onChanged(const App::Property* prop) return; } - if (prop == &SectionSymbol) { + if (prop == &SectionNormal) { + Direction.setValue(SectionNormal.getValue()); + } else if (prop == &SectionSymbol) { std::string lblText = "Section " + std::string(SectionSymbol.getValue()) + " - " + @@ -379,9 +385,19 @@ void DrawViewSection::makeSectionCut(TopoDS_Shape &baseShape) // cutPieces contains result of cutting each subshape in baseShape with tool m_cutPieces = cutPieces; - if (debugSection()) { - BRepTools::Write(cutPieces, "DVSCutPieces.brep"); //debug + BRepTools::Write(cutPieces, "DVSCutPieces1.brep"); //debug + } + + //second cut if requested. Sometimes the first cut includes extra uncut pieces. + if (trimAfterCut()) { + BRepAlgoAPI_Cut mkCut2(cutPieces, m_cuttingTool); + if (mkCut2.IsDone()) { + m_cutPieces = mkCut2.Shape(); + if (debugSection()) { + BRepTools::Write(m_cutPieces, "DVSCutPieces2.brep"); //debug + } + } } // check for error in cut @@ -393,10 +409,6 @@ void DrawViewSection::makeSectionCut(TopoDS_Shape &baseShape) return; } - if (debugSection()) { - BRepTools::Write(m_cutPieces, "DVSRawShapeAfter.brep"); //debug - } - waitingForCut(false); } @@ -434,7 +446,7 @@ TopoDS_Shape DrawViewSection::prepareShape(const TopoDS_Shape& rawShape, Rotation.getValue()); } if (debugSection()) { - BRepTools::Write(m_cutShape, "DVSmCutShape.brep"); //debug + BRepTools::Write(m_cutShape, "DVSCutShape.brep"); //debug // DrawUtil::dumpCS("DVS::makeSectionCut - CS to GO", viewAxis); } @@ -518,9 +530,6 @@ void DrawViewSection::postHlrTasks(void) getProjectionCS(), Rotation.getValue()); } - if (debugSection()) { - BRepTools::Write(faceIntersections, "DVSFaceIntersections.brep"); //debug - } m_sectionTopoDSFaces = alignSectionFaces(faceIntersections); if (debugSection()) { @@ -568,7 +577,7 @@ 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 +//! and scaled cut shape. Aligned 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) { @@ -619,9 +628,6 @@ TopoDS_Compound DrawViewSection::alignSectionFaces(TopoDS_Shape faceIntersection getSectionCS(), Rotation.getValue()); } - if (debugSection()) { - BRepTools::Write(scaledSection, "DVSScaledFaces.brep"); //debug - } return mapToPage(scaledSection); } @@ -655,11 +661,7 @@ TopoDS_Compound DrawViewSection::mapToPage(TopoDS_Shape& shapeToAlign) 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(); @@ -726,20 +728,9 @@ std::pair DrawViewSection::sectionLineEnds() Base::Vector3d sectionOrg = SectionOrigin.getValue() - getBaseDVP()->getOriginalCentroid(); sectionOrg = getBaseDVP()->projectPoint(sectionOrg); //convert to base view CS - - //get the unscaled X and Y ranges of the base view geometry - auto bbx = getBaseDVP()->getBoundingBox(); - double xRange = bbx.MaxX - bbx.MinX; - xRange /= getBaseDVP()->getScale(); - double yRange = bbx.MaxY - bbx.MinY; - yRange /= getBaseDVP()->getScale(); - - sectionOrg = rotator.multVec(sectionOrg); - sectionLineDir = rotator.multVec(sectionLineDir); - - result = DrawUtil::boxIntersect2d(sectionOrg, sectionLineDir, xRange, yRange); //unscaled - result.first = unrotator.multVec(result.first); - result.second = unrotator.multVec(result.second); + double halfSize = getBaseDVP()->getSizeAlongVector(sectionLineDir) / 2.0; + result.first = sectionOrg + sectionLineDir * halfSize; + result.second = sectionOrg - sectionLineDir * halfSize; return result; } @@ -808,6 +799,37 @@ void DrawViewSection::setCSFromBase(const std::string sectionName) XDirection.setValue(vXDir); } +//set the section CS based on an XY vector in BaseViews CS +void DrawViewSection::setCSFromBase(const Base::Vector3d localUnit) +{ +// Base::Console().Message("DVS::setCSFromBase(%s)\n", DrawUtil::formatVector(localUnit).c_str()); + gp_Ax2 newSectionCS = getBaseDVP()->localVectorToCS(localUnit); + + Base::Vector3d vDir(newSectionCS.Direction().X(), + newSectionCS.Direction().Y(), + newSectionCS.Direction().Z()); + Direction.setValue(vDir); + SectionNormal.setValue(vDir); + Base::Vector3d vXDir(newSectionCS.XDirection().X(), + newSectionCS.XDirection().Y(), + newSectionCS.XDirection().Z()); + XDirection.setValue(vXDir); +} + +//reset the section CS based on an XY vector in current section CS +void DrawViewSection::setCSFromLocalUnit(const Base::Vector3d localUnit) +{ +// Base::Console().Message("DVS::setCSFromLocalUnit(%s)\n", DrawUtil::formatVector(localUnit).c_str()); + gp_Dir verticalDir = getSectionCS().YDirection(); + gp_Ax1 verticalAxis(DrawUtil::togp_Pnt(SectionOrigin.getValue()), verticalDir); + gp_Dir oldNormal = getSectionCS().Direction(); + gp_Dir newNormal = DrawUtil::togp_Dir(projectPoint(localUnit)); + double angle = oldNormal.AngleWithRef(newNormal, verticalDir); + gp_Ax2 newCS = getSectionCS().Rotated(verticalAxis, angle); + SectionNormal.setValue(DrawUtil::toVector3d(newCS.Direction())); + XDirection.setValue(DrawUtil::toVector3d(newCS.XDirection())); +} + gp_Ax2 DrawViewSection::getCSFromBase(const std::string sectionName) const { // Base::Console().Message("DVS::getCSFromBase(%s)\n", sectionName.c_str()); @@ -892,6 +914,26 @@ gp_Ax2 DrawViewSection::getSectionCS() const return sectionCS; } +gp_Ax2 DrawViewSection::getProjectionCS(const Base::Vector3d pt) const +{ + Base::Vector3d vNormal = SectionNormal.getValue(); + gp_Dir gNormal(vNormal.x, + vNormal.y, + vNormal.z); + Base::Vector3d vXDir = getXDirection(); + gp_Dir gXDir(vXDir.x, + vXDir.y, + vXDir.z); + if (DrawUtil::fpCompare(fabs(gNormal.Dot(gXDir)), 1.0)) { + //can not build a gp_Ax2 from these values + throw Base::RuntimeError("DVS::getProjectionCS - SectionNormal and XDirection are parallel"); + } + gp_Pnt gOrigin(pt.x, + pt.y, + pt.z); + return { gOrigin, gNormal, gXDir }; +} + std::vector DrawViewSection::getDrawableLines(int i) { // Base::Console().Message("DVS::getDrawableLines(%d) - lineSets: %d\n", i, m_lineSets.size()); @@ -1120,6 +1162,10 @@ bool DrawViewSection::showSectionEdges(void) return (hGrp->GetBool("ShowSectionEdges", true)); } +bool DrawViewSection::trimAfterCut() const +{ + return TrimAfterCut.getValue(); +} // Python Drawing feature --------------------------------------------------------- namespace App { diff --git a/src/Mod/TechDraw/App/DrawViewSection.h b/src/Mod/TechDraw/App/DrawViewSection.h index fbd28fabdd..00896d230a 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.h +++ b/src/Mod/TechDraw/App/DrawViewSection.h @@ -69,8 +69,10 @@ public: App::PropertyLink BaseView; App::PropertyVector SectionNormal; App::PropertyVector SectionOrigin; - App::PropertyEnumeration SectionDirection; + App::PropertyString SectionSymbol; + + App::PropertyEnumeration SectionDirection; //to be made obsolete eventually App::PropertyEnumeration CutSurfaceDisplay; //new v019 App::PropertyFile FileHatchPattern; App::PropertyFile FileGeomPattern; //new v019 @@ -79,8 +81,8 @@ public: App::PropertyString NameGeomPattern; App::PropertyFloat HatchScale; - App::PropertyString SectionSymbol; App::PropertyBool FuseBeforeCut; + App::PropertyBool TrimAfterCut; //new v021 bool isReallyInBox (const Base::Vector3d v, const Base::BoundBox3d bb) const; bool isReallyInBox (const gp_Pnt p, const Bnd_Box& bb) const; @@ -107,8 +109,12 @@ public: virtual TopoDS_Shape prepareShape(const TopoDS_Shape& rawShape, double shapeSize); virtual TopoDS_Shape getShapeToPrepare() const { return m_cutPieces; } + //CS related methods + gp_Ax2 getProjectionCS(Base::Vector3d pt = Base::Vector3d(0.0, 0.0, 0.0)) const override; void setCSFromBase(const std::string sectionName); + void setCSFromBase(Base::Vector3d localUnit); + void setCSFromLocalUnit(const Base::Vector3d localUnit); virtual gp_Ax2 getCSFromBase(const std::string sectionName) const; gp_Ax2 getSectionCS() const; Base::Vector3d getXDirection() const override; //don't use XDirection.getValue() @@ -152,6 +158,7 @@ protected: void getParameters(); bool debugSection() const; int prefCutSurface() const; + bool trimAfterCut() const; TopoDS_Shape m_cutShape; diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp index 80a50ad969..00d75dbcb2 100644 --- a/src/Mod/TechDraw/App/GeometryObject.cpp +++ b/src/Mod/TechDraw/App/GeometryObject.cpp @@ -189,7 +189,7 @@ void GeometryObject::projectShape(const TopoDS_Shape& inShape, visHard = hlrToShape.VCompound(); BRepLib::BuildCurves3d(visHard); visHard = invertGeometry(visHard); - BRepTools::Write(visHard, "GOvisHard.brep"); //debug +// BRepTools::Write(visHard, "GOvisHard.brep"); //debug } if (!hlrToShape.Rg1LineVCompound().IsNull()) { diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index f67788e043..2fdb6d46ec 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -226,6 +226,12 @@ SET(TechDrawGui_SRCS TaskComplexSection.cpp TaskComplexSection.h TaskComplexSection.ui + Widgets/CompassDialWidget.cpp + Widgets/CompassDialWidget.h + Widgets/CompassWidget.cpp + Widgets/CompassWidget.h + Widgets/VectorEditWidget.cpp + Widgets/VectorEditWidget.h ) SET(TechDrawGuiView_SRCS diff --git a/src/Mod/TechDraw/Gui/Command.cpp b/src/Mod/TechDraw/Gui/Command.cpp index c634f17e58..52947210ed 100644 --- a/src/Mod/TechDraw/Gui/Command.cpp +++ b/src/Mod/TechDraw/Gui/Command.cpp @@ -21,10 +21,11 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -# include +#include +#include +#include +#include +#include #endif #include @@ -36,6 +37,7 @@ #include #include +#include #include #include #include @@ -76,6 +78,8 @@ #include "ViewProviderViewPart.h" #include "TaskSectionView.h" +void execSimpleSection(Gui::Command* cmd); +void execComplexSection(Gui::Command* cmd); class Vertex; using namespace TechDrawGui; @@ -469,6 +473,98 @@ bool CmdTechDrawActiveView::isActive() return DrawGuiUtil::needPage(this, true); } +//=========================================================================== +// TechDraw_SectionGroup +//=========================================================================== + +DEF_STD_CMD_ACL(CmdTechDrawSectionGroup) + +CmdTechDrawSectionGroup::CmdTechDrawSectionGroup() + : Command("TechDraw_SectionGroup") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert a simple or complex Section View"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_SectionGroup"; + sStatusTip = sToolTipText; +} + +void CmdTechDrawSectionGroup::activated(int iMsg) +{ +// Base::Console().Message("CMD::SectionGrp - activated(%d)\n", iMsg); + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); + return; + } + + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); + switch(iMsg) { + case 0: + execSimpleSection(this); + break; + case 1: + execComplexSection(this); + break; + default: + Base::Console().Message("CMD::SectionGrp - invalid iMsg: %d\n", iMsg); + }; +} + +Gui::Action * CmdTechDrawSectionGroup::createAction() +{ + Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); + pcAction->setDropDownMenu(true); + applyCommandData(this->className(), pcAction); + + QAction* p1 = pcAction->addAction(QString()); + p1->setIcon(Gui::BitmapFactory().iconFromTheme("actions/TechDraw_SectionView")); + p1->setObjectName(QString::fromLatin1("TechDraw_SectionView")); + p1->setWhatsThis(QString::fromLatin1("TechDraw_SectionView")); + QAction* p2 = pcAction->addAction(QString()); + p2->setIcon(Gui::BitmapFactory().iconFromTheme("actions/TechDraw_ComplexSection")); + p2->setObjectName(QString::fromLatin1("TechDraw_ComplexSection")); + p2->setWhatsThis(QString::fromLatin1("TechDraw_ComplexSection")); + + _pcAction = pcAction; + languageChange(); + + pcAction->setIcon(p1->icon()); + int defaultId = 0; + pcAction->setProperty("defaultAction", QVariant(defaultId)); + + return pcAction; +} + +void CmdTechDrawSectionGroup::languageChange() +{ + Command::languageChange(); + + if (!_pcAction) + return; + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + QList a = pcAction->actions(); + + QAction* arc1 = a[0]; + arc1->setText(QApplication::translate("CmdTechDrawSectionGroup", "Section View")); + arc1->setToolTip(QApplication::translate("TechDraw_SectionView", "Insert simple Section View")); + arc1->setStatusTip(arc1->toolTip()); + QAction* arc2 = a[1]; + arc2->setText(QApplication::translate("CmdTechDrawSectionGroup", "Complex Section")); + arc2->setToolTip(QApplication::translate("TechDraw_ComplexSection", "Insert complex Section View")); + arc2->setStatusTip(arc2->toolTip()); +} + +bool CmdTechDrawSectionGroup::isActive() +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this, false); + return (havePage && haveView); +} + //=========================================================================== // TechDraw_SectionView //=========================================================================== @@ -490,22 +586,14 @@ CmdTechDrawSectionView::CmdTechDrawSectionView() void CmdTechDrawSectionView::activated(int iMsg) { Q_UNUSED(iMsg); - TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); - if (!page) { + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); return; } - std::vector baseObj = getSelection().getObjectsOfType(TechDraw::DrawViewPart::getClassTypeId()); - if (baseObj.empty()) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select at least 1 DrawViewPart object as Base.")); - return; - } - TechDraw::DrawViewPart* dvp = static_cast(*baseObj.begin()); - Gui::Control().showDialog(new TaskDlgSectionView(dvp)); - - updateActive(); //ok here since dialog doesn't call doc.recompute() - commitCommand(); + execSimpleSection(this); } bool CmdTechDrawSectionView::isActive() @@ -519,6 +607,26 @@ bool CmdTechDrawSectionView::isActive() return (havePage && haveView && !taskInProgress); } +void execSimpleSection(Gui::Command* cmd) +{ + TechDraw::DrawPage* page = DrawGuiUtil::findPage(cmd); + if (!page) { + return; + } + + std::vector baseObj = cmd->getSelection().getObjectsOfType(TechDraw::DrawViewPart::getClassTypeId()); + if (baseObj.empty()) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Select at least 1 DrawViewPart object as Base.")); + return; + } + TechDraw::DrawViewPart* dvp = static_cast(*baseObj.begin()); + Gui::Control().showDialog(new TaskDlgSectionView(dvp)); + + cmd->updateActive(); //ok here since dialog doesn't call doc.recompute() + cmd->commitCommand(); +} + //=========================================================================== // TechDraw_ComplexSection //=========================================================================== @@ -540,7 +648,26 @@ CmdTechDrawComplexSection::CmdTechDrawComplexSection() void CmdTechDrawComplexSection::activated(int iMsg) { Q_UNUSED(iMsg); - TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); + return; + } + + execComplexSection(this); +} + +bool CmdTechDrawComplexSection::isActive() +{ + return DrawGuiUtil::needPage(this); +} + +//Complex Sections can be created without a baseView, so the gathering of input +//for the dialog is more involved that simple section +void execComplexSection(Gui::Command* cmd) +{ + TechDraw::DrawPage* page = DrawGuiUtil::findPage(cmd); if (!page) { return; } @@ -553,7 +680,7 @@ void CmdTechDrawComplexSection::activated(int iMsg) std::vector profileSubs; Gui::ResolveMode resolve = Gui::ResolveMode::OldStyleElement; //mystery bool single = false; //mystery - auto selection = getSelection().getSelectionEx(nullptr, + auto selection = cmd->getSelection().getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), resolve, single); @@ -581,11 +708,11 @@ void CmdTechDrawComplexSection::activated(int iMsg) } // 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()) { + if (obj->getDocument() != cmd->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()) { + if (parent->getDocument() != cmd->getDocument()) { continue; } // 2nd, do we really have a link to obj? @@ -628,11 +755,6 @@ void CmdTechDrawComplexSection::activated(int iMsg) profileObject, profileSubs)); } -bool CmdTechDrawComplexSection::isActive() -{ - return DrawGuiUtil::needPage(this); -} - //=========================================================================== // TechDraw_DetailView //=========================================================================== @@ -835,63 +957,6 @@ bool CmdTechDrawProjectionGroup::isActive() return (havePage && !taskInProgress); } -//=========================================================================== -// TechDraw_NewMulti **deprecated** -//=========================================================================== - -//DEF_STD_CMD_A(CmdTechDrawNewMulti); - -//CmdTechDrawNewMulti::CmdTechDrawNewMulti() -// : Command("TechDraw_NewMulti") -//{ -// sAppModule = "TechDraw"; -// sGroup = QT_TR_NOOP("TechDraw"); -// sMenuText = QT_TR_NOOP("Insert multi-part view in drawing"); -// sToolTipText = QT_TR_NOOP("Insert a new View of a multiple Parts in the active drawing"); -// sWhatsThis = "TechDraw_NewMulti"; -// sStatusTip = sToolTipText; -// sPixmap = "actions/TechDraw_Multiview"; -//} - -//void CmdTechDrawNewMulti::activated(int iMsg) -//{ -// Q_UNUSED(iMsg); -// TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); -// if (!page) { -// return; -// } - -// std::vector shapes = getSelection().getObjectsOfType(App::DocumentObject::getClassTypeId()); -// if (shapes.empty()) { -// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), -// QObject::tr("Can not MultiView from this selection.")); -// return; -// } - -// std::string PageName = page->getNameInDocument(); - -// Gui::WaitCursor wc; - -// openCommand(QT_TRANSLATE_NOOP("Command", "Create view")); -// std::string FeatName = getUniqueObjectName("MultiView"); -// doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewMulti', '%s')", FeatName.c_str()); -// App::DocumentObject *docObj = getDocument()->getObject(FeatName.c_str()); -// auto multiView( static_cast(docObj) ); -// multiView->Sources.setValues(shapes); -// doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); -// updateActive(); -// commitCommand(); -//} - -//bool CmdTechDrawNewMulti::isActive(void) -//{ -// return DrawGuiUtil::needPage(this); -//} - -//=========================================================================== -// TechDraw_Balloon -//=========================================================================== - //! common checks of Selection for Dimension commands //non-empty selection, no more than maxObjs selected and at least 1 DrawingPage exists bool _checkSelectionBalloon(Gui::Command* cmd, unsigned maxObjs) { @@ -1617,7 +1682,9 @@ void CreateTechDrawCommands() rcCmdMgr.addCommand(new CmdTechDrawPrintAll()); rcCmdMgr.addCommand(new CmdTechDrawView()); rcCmdMgr.addCommand(new CmdTechDrawActiveView()); + rcCmdMgr.addCommand(new CmdTechDrawSectionGroup()); rcCmdMgr.addCommand(new CmdTechDrawSectionView()); + rcCmdMgr.addCommand(new CmdTechDrawComplexSection()); rcCmdMgr.addCommand(new CmdTechDrawDetailView()); rcCmdMgr.addCommand(new CmdTechDrawProjectionGroup()); rcCmdMgr.addCommand(new CmdTechDrawClipGroup()); @@ -1631,5 +1698,4 @@ 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/QGIViewPart.cpp b/src/Mod/TechDraw/Gui/QGIViewPart.cpp index 5c900a432a..e4e7f41dca 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewPart.cpp @@ -912,15 +912,15 @@ void QGIViewPart::drawComplexSectionLine(TechDraw::DrawViewSection* viewSection, } 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 + if (dcs->ProjectionStrategy.isValue("Offset")) { + Base::Vector3d arrowDirOffset = viewSection->SectionNormal.getValue(); + arrowDirOffset = - viewPart->projectPoint(arrowDirOffset); //arrows are opposite section normal + sectionLine->setDirection(arrowDirOffset.x, -arrowDirOffset.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); + std::pair dirsAligned = dcs->sectionArrowDirs(); + dirsAligned.first = DrawUtil::invertY(dirsAligned.first); + dirsAligned.second = DrawUtil::invertY(dirsAligned.second); + sectionLine->setArrowDirections(dirsAligned.first, dirsAligned.second); } //set the general parameters diff --git a/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg index ab7fde1d26..d0e0e2fd7f 100644 --- a/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg +++ b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_ComplexSection.svg @@ -6,602 +6,623 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - id="svg249" - height="64" + version="1.1" width="64" - version="1.1"> + height="64" + id="svg249"> + style="overflow:visible;"> + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) rotate(180) translate(0,0)" /> + style="overflow:visible;"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) rotate(180) translate(10,0)" /> + style="overflow:visible;"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) rotate(180) translate(10,0)" /> + style="overflow:visible;"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) rotate(180) translate(10,0)" /> + style="overflow:visible;"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) rotate(180) translate(10,0)" /> + style="overflow:visible;"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) rotate(180) translate(10,0)" /> + style="overflow:visible"> + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) translate(0,0)" /> + style="overflow:visible"> + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) translate(0,0)" /> + style="overflow:visible"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.2) translate(6,0)" /> + style="overflow:visible"> + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(1.1) translate(1,0)" /> + style="overflow:visible"> + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) translate(0,0)" /> + refX="0.0" + id="Arrow1Lstart" + style="overflow:visible"> + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.8) translate(12.5,0)" /> + id="stop4001" /> + id="stop4003" /> + r="117.14286" + fx="605.71429" + fy="486.64789" + id="radialGradient5031" + xlink:href="#linearGradient5060" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" /> + offset="0" /> + offset="1" /> + r="117.14286" + fx="605.71429" + fy="486.64789" + id="radialGradient5029" + xlink:href="#linearGradient5060" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" /> + offset="0" /> + offset="0.5" /> + offset="1" /> + x2="302.85715" + y2="609.50507" + id="linearGradient5027" + xlink:href="#linearGradient5048" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" /> + offset="0" /> + offset="1" /> + r="15.821514" + fx="24.306795" + fy="42.07798" + id="radialGradient4548" + xlink:href="#linearGradient4542" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.284916,0,30.08928)" /> + offset="0" /> + offset="1" /> + r="5.257" + fx="20.892099" + fy="64.567902" + id="aigrd3" + gradientUnits="userSpaceOnUse"> + offset="0" /> + offset="1" /> + r="5.256" + fx="20.892099" + fy="114.5684" + id="aigrd2" + gradientUnits="userSpaceOnUse"> + offset="0" /> + offset="1" /> + offset="0" /> + offset="1" /> + offset="0" /> + offset="1" /> + offset="0" /> + offset="0.5" /> + offset="1" /> - + - + - + - - + + + x2="44.524982" + y2="41.792759" + id="linearGradient4343" + xlink:href="#linearGradient3377-76" + gradientUnits="userSpaceOnUse" /> + offset="0" /> + offset="0.5" /> + offset="1" /> + x2="175.6825" + y2="108.75008" + id="linearGradient4349" + xlink:href="#linearGradient3377-76" + gradientUnits="userSpaceOnUse" /> + offset="0" /> + offset="0.5" /> + offset="1" /> + r="19.467436" + fx="135.38333" + fy="97.369568" + id="radialGradient4351" + xlink:href="#linearGradient3377" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.97435,0.2250379,-0.4623105,2.0016728,48.487554,-127.99883)" /> + offset="0" /> + offset="1" /> + r="19.467436" + fx="45.883327" + fy="28.869568" + id="radialGradient4353" + xlink:href="#linearGradient3377" + gradientUnits="userSpaceOnUse" /> + offset="0" /> + offset="1" /> - + r="5.256" /> + + offset="0" /> + offset="1.0000000" /> - + r="5.257" /> + + offset="0" /> + offset="1.0000000" /> + fx="8.1435566" + cy="7.2678967" + cx="8.1435566" + gradientTransform="matrix(1.2992848,0,0,1.4315068,3.2140525,-64.437909)" + gradientUnits="userSpaceOnUse" + id="radialGradient15668-2" + xlink:href="#linearGradient15662-7" /> + id="stop15664-6" /> + id="stop15666-1" /> + fx="33.966679" + cy="35.736916" + cx="33.966679" + gradientTransform="matrix(1.3214205,0,0,1.4752426,-2.0839021,-66.146883)" + gradientUnits="userSpaceOnUse" + id="radialGradient15658-4" + xlink:href="#linearGradient259-5" /> + id="stop260-5" /> + id="stop261-1" /> + fx="3.3431637" + cy="37.388847" + cx="3.3431637" + gradientTransform="matrix(1.3321242,0,0,1.4633899,2.5298271,-65.230893)" + gradientUnits="userSpaceOnUse" + id="radialGradient15656-7" + xlink:href="#linearGradient269-1" /> + id="stop270-1" /> + id="stop271-5" /> + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" + x1="302.85715" + y1="366.64789" + x2="302.85715" + y2="609.50507" /> + id="stop5050-4" /> + + + + + + offset="0" + id="stop5062-8" /> + id="stop5064-2" /> - - - - - + xlink:href="#linearGradient5060-8" /> + xlink:href="#linearGradient5060-8" /> + cx="123.11044" + cy="7.0833611" + fx="123.11044" + fy="7.0833611" + r="11.846154" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35714269,0.7142857,-1.3537553,0.6768774,100.50163,-81.878026)" /> + + + + + + + + + + + + + + + + + + + + @@ -710,58 +790,84 @@ - + id="g2062"> + id="g2033"> - - - - + transform="translate(-45.955657,1.2945347)" + id="g3615"> + style="display:inline;fill:url(#linearGradient3387);fill-opacity:1;stroke:#042a2a;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" + d="m 50.193891,47.557745 -6.03e-4,-29.979514 10.000603,1.979513 -6.03e-4,30.020491 z" + id="path3185" /> + + + + - + transform="translate(1.7061099,0.5884249)" + id="g3630"> + id="g3622" + transform="translate(-38.129929,0.94147973)"> + id="path3185-3" + d="m 78.186788,47.576855 -6.03e-4,-29.979512 10.000603,1.979512 -6e-4,30.02049 z" + style="display:inline;fill:url(#linearGradient3484);fill-opacity:1;stroke:#2e3436;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + id="path3973-2" + d="m 78.186185,17.597343 8.000003,-6 10,2 -7.9994,5.979512 z" + style="display:inline;fill:#d3d7cf;stroke:#2e3436;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + id="path3975-2" + d="m 88.186788,19.576855 -6e-4,30.02049 8,-6 V 13.597343 Z" + style="display:inline;fill:url(#linearGradient3389-1);fill-opacity:1;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_SectionView.svg b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_SectionView.svg index 457315a5d2..3283742566 100644 --- a/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_SectionView.svg +++ b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_SectionView.svg @@ -1,169 +1,670 @@ - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + - - - - + + + + - - - - - - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + - - - + + + + - - - - - - - - - - - - - - + + + + + - - - - + + + - - - - - + + + - - - - + + + - + + + + + + + + - - - - + - + image/svg+xml - - + + [agryson] Alexander Gryson http://agryson.net - + TechDraw_SectionView 2016-01-14 http://www.freecadweb.org/wiki/index.php?title=Artwork @@ -184,33 +685,112 @@ - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp index bd0afe5296..685bf0d77b 100644 --- a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ #endif // #ifndef _PreComp_ +#include #include #include @@ -55,9 +56,12 @@ #include #include #include + #include #include "DrawGuiUtil.h" +#include "Widgets/CompassWidget.h" +#include "Widgets/VectorEditWidget.h" #include "TaskComplexSection.h" @@ -65,6 +69,7 @@ using namespace Gui; using namespace TechDraw; using namespace TechDrawGui; +//ctor for creation TaskComplexSection::TaskComplexSection(TechDraw::DrawPage* page, TechDraw::DrawViewPart* baseView, std::vector shapes, @@ -79,46 +84,200 @@ TaskComplexSection::TaskComplexSection(TechDraw::DrawPage* page, m_xShapes(xShapes), m_profileObject(profileObject), m_profileSubs(profileSubs), - m_sectionName(std::string()) + m_dirName("Aligned"), + m_createMode(true), + m_applyDeferred(0), + m_angle(0.0) { + m_sectionName = std::string(); + m_doc = m_baseView->getDocument(); + + m_saveBaseName = m_baseView->getNameInDocument(); + m_savePageName = m_baseView->findParentPage()->getNameInDocument(); + m_localUnit = Base::Vector3d(0.0, 1.0, 0.0); + ui->setupUi(this); + + saveSectionState(); setUiPrimary(); - connect(ui->pbSectionObjects, SIGNAL(clicked()), this, SLOT(onSectionObjectsUseSelectionClicked())); - connect(ui->pbProfileObject, SIGNAL(clicked()), this, SLOT(onProfileObjectsUseSelectionClicked())); + m_applyDeferred = 0; //setting the direction widgets causes an increment of the deferred count, + //so we reset the counter and the message. + ui->lPendingUpdates->setText(QString()); } -void TaskComplexSection::changeEvent(QEvent* event) +//ctor for edit +TaskComplexSection::TaskComplexSection(TechDraw::DrawComplexSection* complexSection) : + ui(new Ui_TaskComplexSection), + m_page(nullptr), + m_baseView(nullptr), + m_section(complexSection), + m_profileObject(nullptr), + m_dirName("Aligned"), + m_createMode(false), + m_applyDeferred(0), + m_angle(0.0) { - if (event->type() == QEvent::LanguageChange) { - ui->retranslateUi(this); + m_sectionName = m_section->getNameInDocument(); + m_doc = m_section->getDocument(); + m_baseView = dynamic_cast (m_section->BaseView.getValue()); + if (m_baseView) { + m_saveBaseName = m_baseView->getNameInDocument(); + m_savePageName = m_baseView->findParentPage()->getNameInDocument(); } + m_shapes = m_section->Source.getValues(); + m_xShapes = m_section->XSource.getValues(); + m_profileObject = m_section->CuttingToolWireObject.getValue(); + m_localUnit = Base::Vector3d(0.0, 1.0, 0.0); + + ui->setupUi(this); + + saveSectionState(); + setUiEdit(); + + m_applyDeferred = 0; //setting the direction widgets causes an increment of the deferred count, + //so we reset the counter and the message. + ui->lPendingUpdates->setText(QString()); } void TaskComplexSection::setUiPrimary() { setWindowTitle(QObject::tr("New Complex Section")); + if (m_baseView) { + ui->sbScale->setValue(m_baseView->getScale()); + ui->cmbScaleType->setCurrentIndex(m_baseView->ScaleType.getValue()); + } else { + ui->sbScale->setValue(Preferences::scale()); + ui->cmbScaleType->setCurrentIndex(Preferences::scaleType()); + } + ui->cmbStrategy->setCurrentIndex(0); - 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; + setUiCommon(); + if (m_baseView) { + ui->leBaseView->setText(Base::Tools::fromStdString(m_baseView->getNameInDocument())); + //if there is a baseView, we don't know the sectionNormal yet and have to wait until + //one is picked in the dialog + Base::Vector3d defaultNormal(-1.0, 0.0, 0.0); + m_saveNormal = defaultNormal; + m_localUnit = defaultNormal; + m_saveXDir = Base::Vector3d(0.0, 1.0, 0.0); + ui->leBaseView->setText(Base::Tools::fromStdString(m_baseView->getNameInDocument())); + m_viewDirectionWidget->setValue(defaultNormal * -1.0); + } else { + //if there is no baseView, we use the 3d view to determine the SectionNormal + //and XDirection. + std::pair dirs = DrawGuiUtil::get3DDirAndRot(); + m_saveNormal = dirs.first; + m_localUnit = dirs.first; + m_saveXDir = dirs.second; + m_viewDirectionWidget->setValue(m_saveNormal * -1.0); //this will propogate to m_compass + } +} + +void TaskComplexSection::setUiEdit() +{ + setWindowTitle(QObject::tr("Edit Complex Section")); + + if (m_baseView) { + ui->leBaseView->setText(Base::Tools::fromStdString(m_baseView->getNameInDocument())); + } + ui->cmbStrategy->setCurrentIndex(m_section->ProjectionStrategy.getValue()); + ui->leSymbol->setText(Base::Tools::fromStdString(m_section->SectionSymbol.getValue())); + ui->sbScale->setValue(m_section->Scale.getValue()); + ui->cmbScaleType->setCurrentIndex(m_section->ScaleType.getValue()); + + setUiCommon(); + + Base::Vector3d sectionNormalVec = m_section->SectionNormal.getValue(); + if (m_baseView) { + ui->leBaseView->setText(Base::Tools::fromStdString(m_baseView->getNameInDocument())); + Base::Vector3d projectedViewDirection = m_baseView->projectPoint(sectionNormalVec, false); + double viewAngle = atan2(-projectedViewDirection.y, + -projectedViewDirection.x); + m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + m_viewDirectionWidget->setValue(projectedViewDirection * -1.0); + } else { + //no local angle makes sense if there is no baseView? + m_viewDirectionWidget->setValue(sectionNormalVec * -1.0); + } +} + +void TaskComplexSection::setUiCommon() +{ 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()); + + m_compass = new CompassWidget(this); + auto layout = ui->compassLayout; + layout->addWidget(m_compass); + + m_viewDirectionWidget = new VectorEditWidget(this); + m_viewDirectionWidget->setLabel(QObject::tr("Current View Direction")); + auto editLayout = ui->viewDirectionLayout; + editLayout->addWidget(m_viewDirectionWidget); + + connect(ui->pbRight, SIGNAL(clicked(bool)), m_compass, SLOT(setToEast())); + connect(ui->pbLeft, SIGNAL(clicked(bool)), m_compass, SLOT(setToWest())); + connect(ui->pbUp, SIGNAL(clicked(bool)), m_compass, SLOT(setToNorth())); + connect(ui->pbDown, SIGNAL(clicked(bool)), m_compass, SLOT(setToSouth())); + connect(m_compass, SIGNAL(angleChanged(double)), this, SLOT(slotChangeAngle(double))); + + connect(ui->pbUp, SIGNAL(clicked(bool)), this, SLOT(onUpClicked())); + connect(ui->pbDown, SIGNAL(clicked(bool)), this, SLOT(onDownClicked())); + connect(ui->pbRight, SIGNAL(clicked(bool)), this, SLOT(onRightClicked())); + connect(ui->pbLeft, SIGNAL(clicked(bool)), this, SLOT(onLeftClicked())); + + connect(ui->pbUpdateNow, SIGNAL(clicked(bool)), this, SLOT(updateNowClicked())); + connect(ui->cbLiveUpdate, SIGNAL(clicked(bool)), this, SLOT(liveUpdateClicked())); + + connect(ui->pbSectionObjects, SIGNAL(clicked()), this, SLOT(onSectionObjectsUseSelectionClicked())); + connect(ui->pbProfileObject, SIGNAL(clicked()), this, SLOT(onProfileObjectsUseSelectionClicked())); + + connect(m_compass, SIGNAL(angleChanged(double)), this, SLOT(slotChangeAngle(double))); + connect(m_viewDirectionWidget, SIGNAL(valueChanged(Base::Vector3d)), + this, SLOT(slotViewDirectionChanged(Base::Vector3d))); +} + +//save the start conditions +void TaskComplexSection::saveSectionState() +{ +// Base::Console().Message("TCS::saveSectionState()\n"); + if (m_section) { + m_saveSymbol = m_section->SectionSymbol.getValue(); + m_saveScale = m_section->getScale(); + m_saveScaleType = m_section->ScaleType.getValue(); + m_saveNormal = m_section->SectionNormal.getValue(); + m_saveDirection = m_section->Direction.getValue(); + m_localUnit = m_saveNormal; + m_saveXDir = m_section->XDirection.getValue(); + m_saveOrigin = m_section->SectionOrigin.getValue(); + m_saveDirName = m_section->SectionDirection.getValueAsString(); + m_saved = true; } + if (m_baseView) { + m_shapes = m_baseView->Source.getValues(); + m_xShapes = m_baseView->XSource.getValues(); + } +} + +//restore the start conditions +void TaskComplexSection::restoreSectionState() +{ +// Base::Console().Message("TCS::restoreSectionState()\n"); + if (!m_section) + return; + + m_section->SectionSymbol.setValue(m_saveSymbol); + m_section->Scale.setValue(m_saveScale); + m_section->ScaleType.setValue(m_saveScaleType); + m_section->SectionNormal.setValue(m_saveNormal); + m_section->Direction.setValue(m_saveDirection); + m_section->XDirection.setValue(m_saveXDir); + m_section->SectionOrigin.setValue(m_saveOrigin); + m_section->SectionDirection.setValue(m_saveDirName.c_str()); } void TaskComplexSection::onSectionObjectsUseSelectionClicked() @@ -140,6 +299,78 @@ void TaskComplexSection::onSectionObjectsUseSelectionClicked() ui->leSectionObjects->setText(sourcesToString()); } +//the VectorEditWidget reports a change in direction +void TaskComplexSection::slotViewDirectionChanged(Base::Vector3d newDirection) +{ +// Base::Console().Message("TCS::slotViewDirectionChanged(%s)\n", +// DrawUtil::formatVector(newDirection).c_str()); + Base::Vector3d projectedViewDirection = m_baseView->projectPoint(newDirection, false); + projectedViewDirection.Normalize(); + double viewAngle = atan2(projectedViewDirection.y, + projectedViewDirection.x); + m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + checkAll(false); + applyAligned(projectedViewDirection); +} + +//the CompassWidget reports the view direction. This is the reverse of the +//SectionNormal +void TaskComplexSection::slotChangeAngle(double newAngle) +{ +// Base::Console().Message("TCS::slotAngleChanged(%.3f)\n", newAngle); + double angleRadians = newAngle * M_PI / 180.0; + if (m_baseView) { + double unitX = cos(angleRadians); + double unitY = sin(angleRadians); + Base::Vector3d localUnit(unitX, unitY, 0.0); + m_viewDirectionWidget->setValue(localUnit); + checkAll(false); + applyAligned(localUnit); + } else { + //save the angle for later use + m_angle = angleRadians; + } +} +void TaskComplexSection::onUpClicked() +{ +// Base::Console().Message("TCS::onUpClicked()\n"); + checkAll(false); + applyAligned(Base::Vector3d(0.0, 1.0, 0.0)); +} + +void TaskComplexSection::onDownClicked() +{ +// Base::Console().Message("TCS::onDownClicked()\n"); + checkAll(false); + applyAligned(Base::Vector3d(0.0, -1.0, 0.0)); +} + +void TaskComplexSection::onLeftClicked() +{ +// Base::Console().Message("TCS::onLeftClicked()\n"); + checkAll(false); + applyAligned(Base::Vector3d(-1.0, 0.0, 0.0)); +} + +void TaskComplexSection::onRightClicked() +{ +// Base::Console().Message("TCS::onRightClicked()\n"); + checkAll(false); + applyAligned(Base::Vector3d(1.0, 0.0, 0.0)); +} + +void TaskComplexSection::onIdentifierChanged() +{ + checkAll(false); + apply(); +} + +void TaskComplexSection::onScaleChanged() +{ + checkAll(false); + apply(); +} + void TaskComplexSection::onProfileObjectsUseSelectionClicked() { std::vector selection = Gui::Selection().getSelectionEx(); @@ -151,44 +382,155 @@ void TaskComplexSection::onProfileObjectsUseSelectionClicked() Base::Tools::fromStdString(m_profileObject->Label.getValue())); } } +void TaskComplexSection::scaleTypeChanged(int index) +{ + if (index == 0) { + // Page Scale Type + ui->sbScale->setEnabled(false); + if (m_baseView->findParentPage()) { + ui->sbScale->setValue(m_baseView->findParentPage()->Scale.getValue()); + ui->sbScale->setEnabled(false); + } + } else if (index == 1) { + // Automatic Scale Type + ui->sbScale->setEnabled(false); + if (m_section) { + ui->sbScale->setValue(m_section->autoScale()); + } + } else if (index == 2) { + // Custom Scale Type + ui->sbScale->setEnabled(true); + if (m_section) { + ui->sbScale->setValue(m_section->Scale.getValue()); + ui->sbScale->setEnabled(true); + } + } else { + Base::Console().Log("Error - TaskComplexSection::scaleTypeChanged - unknown scale type: %d\n", index); + return; + } +} + +void TaskComplexSection::checkAll(bool check) +{ + ui->pbUp->setChecked(check); + ui->pbDown->setChecked(check); + ui->pbRight->setChecked(check); + ui->pbLeft->setChecked(check); +} + +void TaskComplexSection::enableAll(bool enable) +{ + ui->leSymbol->setEnabled(enable); + ui->sbScale->setEnabled(enable); + ui->cmbScaleType->setEnabled(enable); + QString qScaleType = ui->cmbScaleType->currentText(); + //Allow or prevent scale changing initially + if (qScaleType == QString::fromUtf8("Custom")) { + ui->sbScale->setEnabled(true); + } + else { + ui->sbScale->setEnabled(false); + } +} + +void TaskComplexSection::liveUpdateClicked() { + apply(true); +} + +void TaskComplexSection::updateNowClicked() { + apply(true); +} QString TaskComplexSection::sourcesToString() { QString result; + QString separator(QString::fromUtf8(", ")); + QString currentSeparator; if (m_baseView) { for (auto& obj : m_baseView->Source.getValues()) { - result += Base::Tools::fromStdString(obj->getNameInDocument()) + + result += currentSeparator + + Base::Tools::fromStdString(obj->getNameInDocument()) + QString::fromUtf8(" / ") + - Base::Tools::fromStdString(obj->Label.getValue()) + - QString::fromUtf8(", "); + Base::Tools::fromStdString(obj->Label.getValue()); + currentSeparator = separator; } + currentSeparator = QString(); for (auto& obj : m_baseView->XSource.getValues()) { - result += Base::Tools::fromStdString(obj->getNameInDocument()) + + result += currentSeparator + + Base::Tools::fromStdString(obj->getNameInDocument()) + QString::fromUtf8(" / ") + - Base::Tools::fromStdString(obj->Label.getValue()) + - QString::fromUtf8(", "); + Base::Tools::fromStdString(obj->Label.getValue()); } } else { for (auto& obj : m_shapes) { - result += Base::Tools::fromStdString(obj->getNameInDocument()) + + result += currentSeparator + + Base::Tools::fromStdString(obj->getNameInDocument()) + QString::fromUtf8(" / ") + - Base::Tools::fromStdString(obj->Label.getValue()) + - QString::fromUtf8(", "); + Base::Tools::fromStdString(obj->Label.getValue()); } + currentSeparator = QString(); for (auto& obj : m_xShapes) { - result += Base::Tools::fromStdString(obj->getNameInDocument()) + + result += currentSeparator + + Base::Tools::fromStdString(obj->getNameInDocument()) + QString::fromUtf8(" / ") + - Base::Tools::fromStdString(obj->Label.getValue()) + - QString::fromUtf8(", "); + Base::Tools::fromStdString(obj->Label.getValue()); } } return result; } -void TaskComplexSection::updateUi() +//****************************************************************************** +bool TaskComplexSection::apply(bool forceUpdate) { +// Base::Console().Message("TCS::apply() - liveUpdate: %d forece: %d\n", +// ui->cbLiveUpdate->isChecked(), forceUpdate); + if(!ui->cbLiveUpdate->isChecked() && + !forceUpdate) { + //nothing to do + m_applyDeferred++; + QString msgLiteral = QString::fromUtf8(QT_TRANSLATE_NOOP("TaskPojGroup", " updates pending")); + QString msgNumber = QString::number(m_applyDeferred); + ui->lPendingUpdates->setText(msgNumber + msgLiteral); + return false; + } + + Gui::WaitCursor wc; + if (!m_section) { + createComplexSection(); + } + + if (isSectionValid()) { + updateComplexSection(); + } else { + failNoObject(); + } + + m_section->recomputeFeature(); + if (isBaseValid()) { + m_baseView->requestPaint(); + } + + enableAll(true); + checkAll(false); + + wc.restoreCursor(); + m_applyDeferred = 0; + ui->lPendingUpdates->setText(QString()); + return true; } +void TaskComplexSection::applyAligned(Base::Vector3d localUnit) +{ +// Base::Console().Message("TCS::applyAligned(%s)\n", +// DrawUtil::formatVector(localUnit).c_str()); + m_dirName = "Aligned"; + m_localUnit = localUnit; + enableAll(true); + apply(); +} + +//******************************************************************* + //pointer to created view is not returned, but stored in m_section void TaskComplexSection::createComplexSection() { @@ -219,7 +561,7 @@ void TaskComplexSection::createComplexSection() Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.6f", m_sectionName.c_str(), - ui->dsbScale->value()); + ui->sbScale->value()); int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", m_sectionName.c_str(), scaleType); @@ -227,14 +569,6 @@ void TaskComplexSection::createComplexSection() 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'", @@ -246,62 +580,181 @@ void TaskComplexSection::createComplexSection() 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()); - + if (m_localUnit.Length() != 0.0) { + m_section->setCSFromBase(m_localUnit * -1.0); + } + m_section->Source.setValues(m_baseView->Source.getValues()); + m_section->XSource.setValues(m_baseView->XSource.getValues()); } else { + //if we have not changed the direction, we should use the 3d directions saved in the + //constructor + if (m_localUnit.IsEqual(m_saveNormal, EWTOLERANCE)) { + m_section->SectionNormal.setValue(m_saveNormal); + m_section->XDirection.setValue(m_saveXDir); + } else { + //if we have changed the direction, use the local unit to create a CS + m_section->setCSFromLocalUnit(m_localUnit * -1.0); + m_localUnit = m_saveNormal; //don't repeat this work next time through + } m_section->Source.setValues(m_shapes); m_section->XSource.setValues(m_xShapes); } m_section->CuttingToolWireObject.setValue(m_profileObject); + m_section->SectionDirection.setValue("Aligned"); + m_section->Source.setValues(m_shapes); + m_section->XSource.setValues(m_xShapes); } Gui::Command::commitCommand(); - if (m_section) { - m_section->recomputeFeature(); +} + +void TaskComplexSection::updateComplexSection() +{ +// Base::Console().Message("TCS:;updateComplexSection()\n"); + if (!isSectionValid()) { + failNoObject(); + return; } - return; + + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit SectionView")); + if (m_section) { + 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->sbScale->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.SectionDirection = 'Aligned'", + m_sectionName.c_str()); + m_section->CuttingToolWireObject.setValue(m_profileObject); + m_section->SectionDirection.setValue("Aligned"); + if (m_baseView) { + if (!m_localUnit.IsEqual(m_saveNormal, EWTOLERANCE) && + m_localUnit.Length() != 0.0) { + //if we have changed the view direction (and by extension the + //section normal, update the feature with the new value. + //m_localUnit should always be valid, but will cause an exception + //if it is null. + m_section->setCSFromBase(m_localUnit * -1.0); + } + m_section->Source.setValues(m_baseView->Source.getValues()); + m_section->XSource.setValues(m_baseView->XSource.getValues()); + } else { + //without a baseView, our choice of SectionNormal and XDirection may well be wrong + //if we have not changed the direction, we leave things as they are + if (!m_localUnit.IsEqual(m_saveNormal, EWTOLERANCE)) { + //just do our best. + m_section->setCSFromLocalUnit(m_localUnit * -1.0); + m_localUnit = m_saveNormal; //don't repeat this work next time through + } + + m_section->Source.setValues(m_shapes); + m_section->XSource.setValues(m_xShapes); + } + } + Gui::Command::commitCommand(); } -void TaskComplexSection::saveButtons(QPushButton* btnOK, - QPushButton* btnCancel) +void TaskComplexSection::failNoObject(void) { - m_btnOK = btnOK; - m_btnCancel = btnCancel; + QString qsectionName = Base::Tools::fromStdString(m_sectionName); + QString qbaseName = Base::Tools::fromStdString(m_saveBaseName); + QString msg = tr("Can not continue. Object * %1 or %2 not found.").arg(qsectionName, qbaseName); + QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Operation Failed"), msg); + Gui::Control().closeDialog(); } -void TaskComplexSection::enableTaskButtons(bool button) +bool TaskComplexSection::isBaseValid() { - m_btnOK->setEnabled(button); - m_btnCancel->setEnabled(button); + if (!m_baseView) + return false; + + App::DocumentObject* baseObj = m_doc->getObject(m_saveBaseName.c_str()); + if (!baseObj) + return false; + + return true; } +bool TaskComplexSection::isSectionValid() +{ + if (!m_section) + return false; + + App::DocumentObject* sectionObj = m_doc->getObject(m_sectionName.c_str()); + if (!sectionObj) + return false; + + return true; +} //****************************************************************************** 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()"); - +// Base::Console().Message("TCS::accept()\n"); + apply(true); + 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) + if (!m_section) { //no section created, nothing to undo + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); return false; + } - //make sure any dangling objects are cleaned up - Gui::Command::doCommand(Gui::Command::Gui, "App.activeDocument().recompute()"); + if (!isSectionValid()) { //section !exist. nothing to undo + if (isBaseValid()) { + m_baseView->requestPaint(); + } + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + return false; + } + + if (m_createMode) { + std::string SectionName = m_section->getNameInDocument(); + Gui::Command::doCommand(Gui::Command::Gui, + "App.ActiveDocument.%s.removeView(App.ActiveDocument.%s)", + m_savePageName.c_str(), SectionName.c_str()); + Gui::Command::doCommand(Gui::Command::Gui, + "App.ActiveDocument.removeObject('%s')", + SectionName.c_str()); + } else { + restoreSectionState(); + m_section->recomputeFeature(); + m_section->requestPaint(); + } + + if (isBaseValid()) { + m_baseView->requestPaint(); + } + Gui::Command::updateActive(); Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); - return false; + return false;} + +void TaskComplexSection::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -320,6 +773,16 @@ TaskDlgComplexSection::TaskDlgComplexSection(TechDraw::DrawPage* page, Content.push_back(taskbox); } +TaskDlgComplexSection::TaskDlgComplexSection(TechDraw::DrawComplexSection* complexSection) + : TaskDialog() +{ + widget = new TaskComplexSection(complexSection); + 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() { } @@ -329,22 +792,11 @@ 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(); diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.h b/src/Mod/TechDraw/Gui/TaskComplexSection.h index 744936ea2b..cc8f3ed792 100644 --- a/src/Mod/TechDraw/Gui/TaskComplexSection.h +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.h @@ -46,6 +46,9 @@ class DrawComplexSection; namespace TechDrawGui { +class CompassWidget; +class VectorEditWidget; + class Ui_TaskComplexSection; class TaskComplexSection : public QWidget @@ -59,40 +62,85 @@ public: std::vector xShapes, App::DocumentObject* profileObject, std::vector profileSubs); + TaskComplexSection(TechDraw::DrawComplexSection* complexSection); ~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 saveSectionState(); + void restoreSectionState(); + + bool apply(bool forceUpdate = false); + void applyQuick(std::string dir); + void applyAligned(Base::Vector3d localUnit); void setUiPrimary(); + void setUiEdit(); + void setUiCommon(); + + void checkAll(bool check); + void enableAll(bool enable); + + void failNoObject(); + bool isBaseValid(); + bool isSectionValid(); + void updateUi(); +protected Q_SLOTS: + void onSectionObjectsUseSelectionClicked(); + void onProfileObjectsUseSelectionClicked(); + void onUpClicked(); + void onDownClicked(); + void onLeftClicked(); + void onRightClicked(); + void onIdentifierChanged(); + void onScaleChanged(); + void scaleTypeChanged(int index); + void liveUpdateClicked(); + void updateNowClicked(); + void slotChangeAngle(double newAngle); + void slotViewDirectionChanged(Base::Vector3d newDirection); + private: void createComplexSection(); + void updateComplexSection(); + QString sourcesToString(); std::unique_ptr ui; TechDraw::DrawPage* m_page; + App::Document* m_doc; 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_dirName; std::string m_sectionName; + Base::Vector3d m_saveNormal; Base::Vector3d m_saveXDir; + std::string m_saveBaseName; + std::string m_savePageName; + std::string m_saveSymbol; + std::string m_saveDirName; + Base::Vector3d m_saveDirection; + Base::Vector3d m_saveOrigin; + double m_saveScale; + int m_saveScaleType; + bool m_saved; + bool m_createMode; + Base::Vector3d m_normal; - QPushButton* m_btnOK; - QPushButton* m_btnCancel; + int m_applyDeferred; + Base::Vector3d m_localUnit; + CompassWidget* m_compass; + double m_angle; + VectorEditWidget* m_viewDirectionWidget; }; @@ -107,13 +155,13 @@ public: std::vector xShapes, App::DocumentObject* profileObject, std::vector profileSubs); + TaskDlgComplexSection(TechDraw::DrawComplexSection* page); ~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) @@ -123,8 +171,6 @@ public: { return false; } void update(); - void modifyStandardButtons(QDialogButtonBox* box) override; - protected: private: diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.ui b/src/Mod/TechDraw/Gui/TaskComplexSection.ui index 85ab665226..68957ecb2e 100644 --- a/src/Mod/TechDraw/Gui/TaskComplexSection.ui +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.ui @@ -7,7 +7,7 @@ 0 0 370 - 478 + 612 @@ -85,6 +85,13 @@ + + + + Qt::Horizontal + + + @@ -93,14 +100,7 @@ - - - - Identifier - - - - + @@ -128,8 +128,22 @@ - - + + + + Scale + + + + + + + Scale Type + + + + + 0 @@ -141,41 +155,14 @@ - - - - Scale Type - - - - - - - Scale - - - - - - - - 0 - 26 - - - - Identifier for this section - - - - + Projection Strategy - + @@ -196,19 +183,19 @@ - Single + Offset 0 - Single + Offset - Piecewise + Aligned @@ -218,67 +205,194 @@ + + + + Identifier + + + + + + + + 0 + 26 + + + + Identifier for this section + + + + + + + BaseView + + + + + + + false + + + - + + + Qt::Horizontal + + + + + - Section Normal + Set View Direction + + + + + + + + + Preset view direction looking up. + + + + + + + :/icons/actions/section-up.svg + + + + + 48 + 48 + + + + + + + + Preset view direction looking down. + + + + + + + :/icons/actions/section-down.svg + + + + + 48 + 48 + + + + + + + + Preset view direction looking left. + + + + + + + :/icons/actions/section-left.svg + + + + + 48 + 48 + + + + + + + + Preset view direction looking right. + + + + + + + :/icons/actions/section-right.svg + + + + + 48 + 48 + + + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + Preview + + - - - - Z - - - - - - X + + + Check to update display after every property change. - - - - - Y + Live Update - - - -2147483647.000000000000000 + + + Rebuild display now. May be slow for complex models. - - 2147483647.000000000000000 + + Update Now - - - - -2147483647.000000000000000 - - - 2147483647.000000000000000 - - - - - - - -2147483647.000000000000000 - - - 2147483647.000000000000000 + + + + diff --git a/src/Mod/TechDraw/Gui/TaskSectionView.cpp b/src/Mod/TechDraw/Gui/TaskSectionView.cpp index 552f6dd0a9..19439d0ca4 100644 --- a/src/Mod/TechDraw/Gui/TaskSectionView.cpp +++ b/src/Mod/TechDraw/Gui/TaskSectionView.cpp @@ -30,7 +30,6 @@ #include #endif // #ifndef _PreComp_ - #include #include #include @@ -58,7 +57,8 @@ //#include "ViewProviderViewPart.h" #include - +#include "Widgets/CompassWidget.h" +#include "Widgets/VectorEditWidget.h" #include "TaskSectionView.h" using namespace Gui; @@ -71,10 +71,11 @@ TaskSectionView::TaskSectionView(TechDraw::DrawViewPart* base) : m_base(base), m_section(nullptr), m_saveScale(1.0), + m_dirName(""), m_doc(nullptr), m_createMode(true), m_saved(false), - m_abort(false) + m_applyDeferred(0) { //existence of base is guaranteed by CmdTechDrawSectionView (Command.cpp) @@ -85,15 +86,12 @@ TaskSectionView::TaskSectionView(TechDraw::DrawViewPart* base) : m_savePageName = m_base->findParentPage()->getNameInDocument(); ui->setupUi(this); - - connect(ui->pbUp, SIGNAL(clicked(bool)), this, SLOT(onUpClicked())); - connect(ui->pbDown, SIGNAL(clicked(bool)), this, SLOT(onDownClicked())); - connect(ui->pbRight, SIGNAL(clicked(bool)), this, SLOT(onRightClicked())); - connect(ui->pbLeft, SIGNAL(clicked(bool)), this, SLOT(onLeftClicked())); - setUiPrimary(); -} + m_applyDeferred = 0; //setting the direction widgets causes an increment of the deferred count, + //so we reset the counter and the message. + ui->lPendingUpdates->setText(QString()); +} //ctor for edit TaskSectionView::TaskSectionView(TechDraw::DrawViewSection* section) : @@ -104,7 +102,7 @@ TaskSectionView::TaskSectionView(TechDraw::DrawViewSection* section) : m_doc(nullptr), m_createMode(false), m_saved(false), - m_abort(false) + m_applyDeferred(0) { //existence of section is guaranteed by ViewProviderViewSection.setEdit @@ -112,76 +110,82 @@ TaskSectionView::TaskSectionView(TechDraw::DrawViewSection* section) : m_sectionName = m_section->getNameInDocument(); App::DocumentObject* newObj = m_section->BaseView.getValue(); m_base = dynamic_cast(newObj); - if (!newObj || !m_base) + if (!newObj || !m_base) { throw Base::RuntimeError("TaskSectionView - BaseView not found"); + } m_saveBaseName = m_base->getNameInDocument(); m_savePageName = m_base->findParentPage()->getNameInDocument(); ui->setupUi(this); - connect(ui->pbUp, SIGNAL(clicked(bool)), this, SLOT(onUpClicked())); - connect(ui->pbDown, SIGNAL(clicked(bool)), this, SLOT(onDownClicked())); - connect(ui->pbRight, SIGNAL(clicked(bool)), this, SLOT(onRightClicked())); - connect(ui->pbLeft, SIGNAL(clicked(bool)), this, SLOT(onLeftClicked())); - m_dirName = m_section->SectionDirection.getValueAsString(); saveSectionState(); setUiEdit(); + + m_applyDeferred = 0; //setting the direction widgets causes an increment of the deferred count, + //so we reset the counter and the message. + ui->lPendingUpdates->setText(QString()); } void TaskSectionView::setUiPrimary() { // Base::Console().Message("TSV::setUiPrimary()\n"); setWindowTitle(QObject::tr("Create Section View")); - std::string temp = m_base->getNameInDocument(); - QString qTemp = Base::Tools::fromStdString(temp); - ui->leBaseView->setText(qTemp); - ui->sbScale->setValue(m_base->getScale()); ui->cmbScaleType->setCurrentIndex(m_base->ScaleType.getValue()); + + //Allow or prevent scale changing initially + if (m_base->ScaleType.isValue("Custom")) { + ui->sbScale->setEnabled(true); + } + else { + ui->sbScale->setEnabled(false); + } + Base::Vector3d origin = m_base->getOriginalCentroid(); - ui->sbOrgX->setUnit(Base::Unit::Length); - ui->sbOrgX->setValue(origin.x); - ui->sbOrgY->setUnit(Base::Unit::Length); - ui->sbOrgY->setValue(origin.y); - ui->sbOrgZ->setUnit(Base::Unit::Length); - ui->sbOrgZ->setValue(origin.z); + setUiCommon(origin); - // before the user did not select an orientation, - // the section properties cannot be changed - this->setToolTip(QObject::tr("Select at first an orientation")); - enableAll(false); - - connect(ui->leSymbol, SIGNAL(editingFinished()), this, SLOT(onIdentifierChanged())); - - // the UI file uses keyboardTracking = false so that a recomputation - // will only be triggered when the arrow keys of the spinboxes are used - connect(ui->sbScale, SIGNAL(valueChanged(double)), this, SLOT(onScaleChanged())); - connect(ui->sbOrgX, SIGNAL(valueChanged(double)), this, SLOT(onXChanged())); - connect(ui->sbOrgY, SIGNAL(valueChanged(double)), this, SLOT(onYChanged())); - connect(ui->sbOrgZ, SIGNAL(valueChanged(double)), this, SLOT(onZChanged())); - - connect(ui->cmbScaleType, SIGNAL(currentIndexChanged(int)), this, SLOT(scaleTypeChanged(int))); + m_viewDirectionWidget->setValue(Base::Vector3d(1.0, 0.0, 0.0)); } void TaskSectionView::setUiEdit() { // Base::Console().Message("TSV::setUiEdit()\n"); setWindowTitle(QObject::tr("Edit Section View")); + std::string temp = m_section->SectionSymbol.getValue(); + QString qTemp = Base::Tools::fromStdString(temp); + ui->leSymbol->setText(qTemp); + ui->sbScale->setValue(m_section->getScale()); + ui->cmbScaleType->setCurrentIndex(m_section->ScaleType.getValue()); + //Allow or prevent scale changing initially + if (m_section->ScaleType.isValue("Custom")) { + ui->sbScale->setEnabled(true); + } + else { + ui->sbScale->setEnabled(false); + } + + Base::Vector3d origin = m_section->SectionOrigin.getValue(); + setUiCommon(origin); + + // convert section normal to view angle + Base::Vector3d sectionNormalVec = m_section->SectionNormal.getValue(); + Base::Vector3d projectedViewDirection = m_base->projectPoint(sectionNormalVec, false); + double viewAngle = atan2(-projectedViewDirection.y, + -projectedViewDirection.x); + m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + m_viewDirectionWidget->setValue(sectionNormalVec * -1.0); +} + +void TaskSectionView::setUiCommon(Base::Vector3d origin) +{ std::string temp = m_base->getNameInDocument(); QString qTemp = Base::Tools::fromStdString(temp); ui->leBaseView->setText(qTemp); - temp = m_section->SectionSymbol.getValue(); - qTemp = Base::Tools::fromStdString(temp); - ui->leSymbol->setText(qTemp); - ui->sbScale->setValue(m_section->getScale()); - ui->cmbScaleType->setCurrentIndex(m_section->ScaleType.getValue()); - - Base::Vector3d origin = m_section->SectionOrigin.getValue(); ui->sbOrgX->setUnit(Base::Unit::Length); ui->sbOrgX->setValue(origin.x); ui->sbOrgY->setUnit(Base::Unit::Length); @@ -189,33 +193,58 @@ void TaskSectionView::setUiEdit() ui->sbOrgZ->setUnit(Base::Unit::Length); ui->sbOrgZ->setValue(origin.z); + enableAll(false); + connect(ui->leSymbol, SIGNAL(editingFinished()), this, SLOT(onIdentifierChanged())); + //TODO: use event filter instead of keyboard tracking to capture enter/return keys // the UI file uses keyboardTracking = false so that a recomputation // will only be triggered when the arrow keys of the spinboxes are used + //if this is not done, recomputes are triggered on each key press giving + //unaccceptable UX connect(ui->sbScale, SIGNAL(valueChanged(double)), this, SLOT(onScaleChanged())); connect(ui->sbOrgX, SIGNAL(valueChanged(double)), this, SLOT(onXChanged())); connect(ui->sbOrgY, SIGNAL(valueChanged(double)), this, SLOT(onYChanged())); connect(ui->sbOrgZ, SIGNAL(valueChanged(double)), this, SLOT(onZChanged())); connect(ui->cmbScaleType, SIGNAL(currentIndexChanged(int)), this, SLOT(scaleTypeChanged(int))); + + connect(ui->pbUp, SIGNAL(clicked(bool)), this, SLOT(onUpClicked())); + connect(ui->pbDown, SIGNAL(clicked(bool)), this, SLOT(onDownClicked())); + connect(ui->pbRight, SIGNAL(clicked(bool)), this, SLOT(onRightClicked())); + connect(ui->pbLeft, SIGNAL(clicked(bool)), this, SLOT(onLeftClicked())); + + connect(ui->pbUpdateNow, SIGNAL(clicked(bool)), this, SLOT(updateNowClicked())); + connect(ui->cbLiveUpdate, SIGNAL(clicked(bool)), this, SLOT(liveUpdateClicked())); + + m_compass = new CompassWidget(this); + auto layout = ui->compassLayout; + layout->addWidget(m_compass); + connect(m_compass, SIGNAL(angleChanged(double)), this, SLOT(slotChangeAngle(double))); + + m_viewDirectionWidget = new VectorEditWidget(this); + m_viewDirectionWidget->setLabel(QObject::tr("Current View Direction")); + auto editLayout = ui->viewDirectionLayout; + editLayout->addWidget(m_viewDirectionWidget); + connect(m_viewDirectionWidget, SIGNAL(valueChanged(Base::Vector3d)), + this, SLOT(slotViewDirectionChanged(Base::Vector3d))); } //save the start conditions void TaskSectionView::saveSectionState() { // Base::Console().Message("TSV::saveSectionState()\n"); - if (!m_section) - return; - - m_saveSymbol = m_section->SectionSymbol.getValue(); - m_saveScale = m_section->getScale(); - m_saveScaleType = m_section->ScaleType.getValue(); - m_saveNormal = m_section->SectionNormal.getValue(); - m_saveDirection = m_section->Direction.getValue(); - m_saveOrigin = m_section->SectionOrigin.getValue(); - m_saveDirName = m_section->SectionDirection.getValueAsString(); - m_saved = true; + if (m_section) { + m_saveSymbol = m_section->SectionSymbol.getValue(); + m_saveScale = m_section->getScale(); + m_saveScaleType = m_section->ScaleType.getValue(); + m_saveNormal = m_section->SectionNormal.getValue(); + m_normal = m_saveNormal; + m_saveDirection = m_section->Direction.getValue(); + m_saveOrigin = m_section->SectionOrigin.getValue(); + m_saveDirName = m_section->SectionDirection.getValueAsString(); + m_saved = true; + } } //restore the start conditions @@ -234,36 +263,72 @@ void TaskSectionView::restoreSectionState() m_section->SectionDirection.setValue(m_saveDirName.c_str()); } +//the VectorEditWidget reports a change in direction +void TaskSectionView::slotViewDirectionChanged(Base::Vector3d newDirection) +{ +// Base::Console().Message("TSV::slotViewDirectionChanged(%s)\n", +// DrawUtil::formatVector(newDirection).c_str()); + Base::Vector3d projectedViewDirection = m_base->projectPoint(newDirection, false); + projectedViewDirection.Normalize(); + double viewAngle = atan2(projectedViewDirection.y, + projectedViewDirection.x); + m_compass->setDialAngle(viewAngle * 180.0 / M_PI); + checkAll(false); + applyAligned(projectedViewDirection); +} + +//the CompassWidget reports that the view direction angle has changed +void TaskSectionView::slotChangeAngle(double newAngle) +{ +// Base::Console().Message("TSV::slotChangeAngle(%.3f)\n", newAngle); + double angleRadians = newAngle * M_PI / 180.0; + double unitX = cos(angleRadians); + double unitY = sin(angleRadians); + Base::Vector3d localUnit(unitX, unitY, 0.0); + m_viewDirectionWidget->setValueNoNotify(localUnit); + checkAll(false); + applyAligned(localUnit); +} + +//preset view directions void TaskSectionView::onUpClicked() { // Base::Console().Message("TSV::onUpClicked()\n"); + m_compass->setToNorth(); + Base::Vector3d localUnit(0.0, 1.0, 0.0); + m_viewDirectionWidget->setValue(localUnit); checkAll(false); - ui->pbUp->setChecked(true); - applyQuick("Up"); + applyAligned(localUnit); } void TaskSectionView::onDownClicked() { // Base::Console().Message("TSV::onDownClicked()\n"); + m_compass->setToSouth(); + Base::Vector3d localUnit(0.0, -1.0, 0.0); + m_viewDirectionWidget->setValue(localUnit); checkAll(false); - ui->pbDown->setChecked(true); - applyQuick("Down"); + applyAligned(localUnit); } void TaskSectionView::onLeftClicked() { // Base::Console().Message("TSV::onLeftClicked()\n"); + m_compass->setToWest(); + Base::Vector3d localUnit(-1.0, 0.0, 0.0); + m_viewDirectionWidget->setValue(localUnit); checkAll(false); - ui->pbLeft->setChecked(true); - applyQuick("Left"); + applyAligned(localUnit); } void TaskSectionView::onRightClicked() { // Base::Console().Message("TSV::onRightClicked()\n"); + m_compass->setToEast(); + Base::Vector3d localUnit(1.0, 0.0, 0.0); + m_viewDirectionWidget->setValue(localUnit); checkAll(false); - ui->pbRight->setChecked(true); - applyQuick("Right"); + applyAligned(localUnit); } void TaskSectionView::onIdentifierChanged() @@ -278,18 +343,17 @@ void TaskSectionView::onScaleChanged() apply(); } +//SectionOrigin changed void TaskSectionView::onXChanged() { checkAll(false); apply(); } - void TaskSectionView::onYChanged() { checkAll(false); apply(); } - void TaskSectionView::onZChanged() { checkAll(false); @@ -303,6 +367,7 @@ void TaskSectionView::scaleTypeChanged(int index) ui->sbScale->setEnabled(false); if (m_base->findParentPage()) { ui->sbScale->setValue(m_base->findParentPage()->Scale.getValue()); + ui->sbScale->setEnabled(false); } } else if (index == 1) { // Automatic Scale Type @@ -315,6 +380,7 @@ void TaskSectionView::scaleTypeChanged(int index) ui->sbScale->setEnabled(true); if (m_section) { ui->sbScale->setValue(m_section->Scale.getValue()); + ui->sbScale->setEnabled(true); } } else { Base::Console().Log("Error - TaskSectionView::scaleTypeChanged - unknown scale type: %d\n", index); @@ -338,25 +404,68 @@ void TaskSectionView::enableAll(bool enable) ui->sbOrgY->setEnabled(enable); ui->sbOrgZ->setEnabled(enable); ui->cmbScaleType->setEnabled(enable); + QString qScaleType = ui->cmbScaleType->currentText(); + //Allow or prevent scale changing initially + if (qScaleType == QString::fromUtf8("Custom")) { + ui->sbScale->setEnabled(true); + } + else { + ui->sbScale->setEnabled(false); + } +} + +void TaskSectionView::liveUpdateClicked() { + apply(true); +} + +void TaskSectionView::updateNowClicked() { + apply(true); } //****************************************************************************** -bool TaskSectionView::apply() +bool TaskSectionView::apply(bool forceUpdate) { -// Base::Console().Message("TSV::apply() - m_dirName: %s\n", m_dirName.c_str()); +// Base::Console().Message("TSV::apply() - liveUpdate: %d force: %d deferred: %d\n", +// ui->cbLiveUpdate->isChecked(), forceUpdate, m_applyDeferred); + if(!ui->cbLiveUpdate->isChecked() && + !forceUpdate) { + //nothing to do + m_applyDeferred++; + QString msgLiteral = QString::fromUtf8(QT_TRANSLATE_NOOP("TaskPojGroup", " updates pending")); + QString msgNumber = QString::number(m_applyDeferred); + ui->lPendingUpdates->setText(msgNumber + msgLiteral); + return false; + } + + Gui::WaitCursor wc; if (m_dirName.empty()) { - std::string msg = + //this should never happen + std::string msg = Base::Tools::toStdString(tr("Nothing to apply. No section direction picked yet")); Base::Console().Error((msg + "\n").c_str()); return false; } - if (!m_section) //didn't create the feature yet - //this can't happen as applyQuick has to be called by the direction - //setting process - return false; + if (!m_section) { + m_section = createSectionView(); + } + if (isSectionValid()) { + updateSectionView(); + } else { + failNoObject(); + } + + m_section->recomputeFeature(); + if (isBaseValid()) { + m_base->requestPaint(); + } + + enableAll(true); checkAll(false); - applyQuick(m_dirName); + + wc.restoreCursor(); + m_applyDeferred = 0; + ui->lPendingUpdates->setText(QString()); return true; } @@ -364,39 +473,27 @@ void TaskSectionView::applyQuick(std::string dir) { // Base::Console().Message("TSV::applyQuick(%s)\n", dir.c_str()); m_dirName = dir; - if (!m_section) - createSectionView(); - - if (!isSectionValid()) { - failNoObject(m_sectionName); - return; - } - - updateSectionView(); enableAll(true); - - m_section->recomputeFeature(); - if (isBaseValid()) - m_base->requestPaint(); + apply(); } -void TaskSectionView::applyAligned() +void TaskSectionView::applyAligned(Base::Vector3d localUnit) { - Base::Console().Message("TSV::applyAligned() - not implemented yet\n"); -// Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Apply Aligned")); +// Base::Console().Message("TSV::applyAligned()\n"); m_dirName = "Aligned"; - //fiddle with directions here + m_localUnit = localUnit; + enableAll(true); + apply(); } //********************************************************************* -//pointer to created view is not returned, but stored in m_section -void TaskSectionView::createSectionView() +TechDraw::DrawViewSection* TaskSectionView::createSectionView(void) { // Base::Console().Message("TSV::createSectionView()\n"); if (!isBaseValid()) { - failNoObject(m_baseName); - return; + failNoObject(); + return nullptr; } std::string sectionName; @@ -416,7 +513,7 @@ void TaskSectionView::createSectionView() Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Source = App.ActiveDocument.%s.Source", m_sectionName.c_str(), baseName.c_str()); Command::doCommand(Command::Doc, - "App.ActiveDocument.%s.SectionOrigin = FreeCAD.Vector(%.3f, %.3f, %.3f)", + "App.ActiveDocument.%s.SectionOrigin = FreeCAD.Vector(%.6f, %.6f, %.6f)", m_sectionName.c_str(), ui->sbOrgX->value().getValue(), ui->sbOrgY->value().getValue(), @@ -427,22 +524,32 @@ void TaskSectionView::createSectionView() int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", m_sectionName.c_str(), scaleType); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.SectionDirection = '%s'", + m_sectionName.c_str(), m_dirName.c_str()); App::DocumentObject* newObj = m_base->getDocument()->getObject(m_sectionName.c_str()); m_section = dynamic_cast(newObj); - if (!newObj || !m_section) + if (!newObj || !m_section) { throw Base::RuntimeError("TaskSectionView - new section object not found"); + } + if (m_dirName == "Aligned") { + //m_localUnit is a view direction so we need to reverse it to make a + //section normal + m_section->setCSFromBase(m_localUnit * -1.0); + } else { + //Note: DirectionName is to be deprecated in the future + m_section->setCSFromBase(m_dirName.c_str()); + } } Gui::Command::commitCommand(); - - return; + return m_section; } void TaskSectionView::updateSectionView() { // Base::Console().Message("TSV::updateSectionView() - m_sectionName: %s\n", m_sectionName.c_str()); if (!isSectionValid()) { - failNoObject(m_sectionName); + failNoObject(); return; } @@ -474,18 +581,28 @@ void TaskSectionView::updateSectionView() int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", m_sectionName.c_str(), scaleType); - m_section->setCSFromBase(m_dirName.c_str()); + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.SectionDirection = '%s'", + m_sectionName.c_str(), m_dirName.c_str()); + if (m_dirName == "Aligned") { + //m_localUnit is a view direction so we need to reverse it to make a + //section normal + m_section->setCSFromBase(m_localUnit * -1.0); + } else { + //Note: DirectionName is to be deprecated in the future + m_section->setCSFromBase(m_dirName.c_str()); + } + } Gui::Command::commitCommand(); } -void TaskSectionView::failNoObject(std::string objectName) +void TaskSectionView::failNoObject(void) { - QString qObjectName = Base::Tools::fromStdString(objectName); - QString msg = tr("Can not continue. Object * %1 * not found.").arg(qObjectName); + QString qsectionName = Base::Tools::fromStdString(m_sectionName); + QString qbaseName = Base::Tools::fromStdString(m_baseName); + QString msg = tr("Can not continue. Object * %1 or %2 not found.").arg(qsectionName, qbaseName); QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Operation Failed"), msg); Gui::Control().closeDialog(); - m_abort = true; } bool TaskSectionView::isBaseValid() @@ -517,12 +634,8 @@ bool TaskSectionView::isSectionValid() bool TaskSectionView::accept() { // Base::Console().Message("TSV::accept()\n"); - if (m_abort) { - return true; - } - apply(); - Gui::Command::updateActive(); - Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + apply(true); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.ActiveDocument.resetEdit()"); return true; } @@ -589,6 +702,7 @@ TaskDlgSectionView::TaskDlgSectionView(TechDraw::DrawViewSection* section) : widget = new TaskSectionView(section); taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("actions/TechDraw_SectionView"), widget->windowTitle(), true, nullptr); + taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); } diff --git a/src/Mod/TechDraw/Gui/TaskSectionView.h b/src/Mod/TechDraw/Gui/TaskSectionView.h index 4a676a5446..a6a0353a31 100644 --- a/src/Mod/TechDraw/Gui/TaskSectionView.h +++ b/src/Mod/TechDraw/Gui/TaskSectionView.h @@ -39,6 +39,9 @@ namespace TechDraw { namespace TechDrawGui { +class CompassWidget; +class VectorEditWidget; + class TaskSectionView : public QWidget { Q_OBJECT @@ -56,20 +59,21 @@ protected: void saveSectionState(); void restoreSectionState(); - bool apply(); + bool apply(bool forceUpdate = false); void applyQuick(std::string dir); - void applyAligned(); + void applyAligned(Base::Vector3d localUnit); - void createSectionView(); + TechDraw::DrawViewSection* createSectionView(); void updateSectionView(); void setUiPrimary(); void setUiEdit(); + void setUiCommon(Base::Vector3d origin); void checkAll(bool check); void enableAll(bool enable); - void failNoObject(std::string objectName); + void failNoObject(); bool isBaseValid(); bool isSectionValid(); @@ -84,6 +88,10 @@ protected Q_SLOTS: void onYChanged(); void onZChanged(); void scaleTypeChanged(int index); + void liveUpdateClicked(); + void updateNowClicked(); + void slotChangeAngle(double newAngle); + void slotViewDirectionChanged(Base::Vector3d newDirection); private: std::unique_ptr ui; @@ -113,8 +121,10 @@ private: std::string m_saveBaseName; std::string m_savePageName; - bool m_abort; - + int m_applyDeferred; + Base::Vector3d m_localUnit; + CompassWidget* m_compass; + VectorEditWidget* m_viewDirectionWidget; }; class TaskDlgSectionView : public Gui::TaskView::TaskDialog diff --git a/src/Mod/TechDraw/Gui/TaskSectionView.ui b/src/Mod/TechDraw/Gui/TaskSectionView.ui index 2b4a8962eb..d92b8204cd 100644 --- a/src/Mod/TechDraw/Gui/TaskSectionView.ui +++ b/src/Mod/TechDraw/Gui/TaskSectionView.ui @@ -7,7 +7,7 @@ 0 0 370 - 368 + 508 @@ -82,7 +82,7 @@ - + @@ -140,6 +140,9 @@ Scale factor for the section view + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + false @@ -157,7 +160,7 @@ - + @@ -167,11 +170,14 @@ - + - Section Orientation + Set View Direction + + + @@ -183,7 +189,7 @@ - Looking up + Preset view direction looking up. @@ -219,7 +225,7 @@ - Looking down + Preset view direction looking down. @@ -249,7 +255,7 @@ - Looking left + Preset view direction looking left. @@ -279,7 +285,7 @@ - Looking right + Preset view direction looking right. @@ -302,6 +308,9 @@ + + + @@ -313,14 +322,14 @@ - + Position from the 3D origin of the object in the view Section Plane Location - + @@ -369,6 +378,9 @@ 22 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + false @@ -410,6 +422,9 @@ 22 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + false @@ -451,6 +466,9 @@ 22 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + false @@ -464,6 +482,56 @@ + + + + Qt::Horizontal + + + + + + + Preview + + + + + + + + <html><head/><body><p>Rebuild display now. May be slow for complex models.</p></body></html> + + + Update Now + + + + + + + Check to update display after every property change. + + + Live Update + + + + + + + false + + + + + + + + + + + @@ -474,7 +542,7 @@ - + diff --git a/src/Mod/TechDraw/Gui/ViewProviderViewSection.cpp b/src/Mod/TechDraw/Gui/ViewProviderViewSection.cpp index 9303fae91c..0f9d3b0fed 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderViewSection.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderViewSection.cpp @@ -36,10 +36,12 @@ #include #include +#include #include #include #include "TaskSectionView.h" +#include "TaskComplexSection.h" #include "ViewProviderViewSection.h" #include "QGIView.h" @@ -123,6 +125,12 @@ bool ViewProviderViewSection::setEdit(int ModNum) } // clear the selection (convenience) Gui::Selection().clearSelection(); + + auto dcs = dynamic_cast(getViewObject()); + if (dcs) { + Gui::Control().showDialog(new TaskDlgComplexSection(dcs)); + return true; + } Gui::Control().showDialog(new TaskDlgSectionView(getViewObject())); return true; } @@ -133,7 +141,6 @@ bool ViewProviderViewSection::doubleClicked() return true; } - void ViewProviderViewSection::getParameters() { Base::Reference hGrp = App::GetApplication().GetUserParameter() diff --git a/src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.cpp b/src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.cpp new file mode 100644 index 0000000000..031f6e3d9d --- /dev/null +++ b/src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** + * 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 * + * * + ***************************************************************************/ + +//largely based on a python widget from: +//https://github.com/tcalmant/demo-ipopo-qt/blob/master/pc/details/compass.py + +#include + +#include + +#include +#include + +#include "CompassDialWidget.h" + +using namespace TechDrawGui; +using CardinalMap = std::map; + +CompassDialWidget::CompassDialWidget(QWidget* parent) : QWidget(parent), + m_markInterval(15), + m_defaultSize(75), + m_defaultMargin(10), + m_designRadius(64) +{ + setObjectName(QString::fromUtf8("Compass")); + m_rect = QRect(0, 0, m_defaultSize, m_defaultSize); + m_angle = 0.0; + m_margin = m_defaultMargin; + m_designDiameter = 2 * m_designRadius; + + QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + sizePolicy.setHorizontalStretch(1); + sizePolicy.setVerticalStretch(1); + setSizePolicy(sizePolicy); + + repaint(); +} + +QSize CompassDialWidget::sizeHint() const +{ + return m_rect.size(); +} + +QSize CompassDialWidget::minimumSizeHint() const +{ + return QRect(0, 0, m_defaultSize, m_defaultSize).size(); +} + +void CompassDialWidget::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + QPainter painter(this); + drawWidget(painter); + QWidget::paintEvent(event); +} + +void CompassDialWidget::drawWidget(QPainter& painter) +{ + painter.setRenderHint(QPainter::Antialiasing, true); + + // Draw the background + drawBackground(painter); + + //Draw the cardinal points + drawMarkings(painter); + + //Draw the needle + drawNeedle(painter); +} + +void CompassDialWidget::drawBackground(QPainter& painter) +{ + painter.save(); + painter.setPen(QPen(Qt::NoPen)); + // Clear the background + painter.fillRect(m_rect, palette().brush((QPalette::Window))); + painter.restore(); +} + +//Draws the cardinal points on painter. This widget was designed such that +//the dial gradations extend to 50 units from center and the compass point text +//begins at 52 units from the center. With the font size set to 12 pixels, this +//gives a design area of a circle with a radius of approximately 64 units. All of +//the constants reflect this design size. +void CompassDialWidget::drawMarkings(QPainter& painter) +{ + CardinalMap CompassPointText( { {0, "Y"}, {45, "XY"}, {90, "X"}, {135, "X-Y"}, {180, "-Y"}, + {225, "-X-Y"}, {270, "-X"}, {315, "-XY"} } ); + painter.save(); + int markInterval(15); + + //Move to the center of the compass + painter.translate(width() / 2, height() / 2); + double scale = std::min((float) width() / (float) (m_designDiameter + 2.0 * m_margin), + (float) height() / (float) (m_designDiameter + 2.0 * m_margin)); + painter.scale(scale, scale); + + // Setup the fonts and the painter + QFont widgetFont = font(); + widgetFont.setPixelSize(12); + QFontMetrics metrics(widgetFont); + + //outer circle + int circleWidth = 2.0 * (m_designRadius + m_margin); + int circleHeight = 2.0 * (m_designRadius + m_margin); + QRect circleRect(-circleWidth / 2, -circleHeight / 2, circleWidth, circleHeight); + painter.drawEllipse(circleRect); + + painter.setFont(widgetFont); + painter.setPen(QPen(palette().color(QPalette::WindowText))); + + int iDegree = 0; + while ( iDegree < 360 ) { + if (iDegree % 45 == 0) { + //Named direction (every 45°) + painter.drawLine(0, -40, 0, -50); //this has to depend on m_rect or size? + QString qPointText = Base::Tools::fromStdString(CompassPointText.at(iDegree)); + painter.drawText(-metrics.boundingRect(qPointText).width() / 2.0, -52, qPointText); + // what is -52? line end point y = -50 + 2 for margin? + } else { + //Small line + painter.drawLine(0, -45, 0, -50); + } + + //Next line (+15°) + painter.rotate(markInterval); + iDegree += markInterval; + } + + painter.restore(); +} + +//Draws a needle on painter +void CompassDialWidget::drawNeedle(QPainter& painter) +{ + painter.save(); + + //Move to the center of the compass + painter.translate(width() / 2, height() / 2); + + //Rotate to the correct angle + painter.rotate(m_angle); + double scale = std::min((float) width() / (float) (m_designDiameter + 2.0 * m_margin), + (float) height() / (float) (m_designDiameter + 2.0 * m_margin)); + painter.scale(scale, scale); + + //Setup the painter + QPen needlePen(palette().color(QPalette::WindowText)); + needlePen.setWidth(2); + needlePen.setStyle(Qt::DashDotLine); + painter.setPen(needlePen); + painter.setBrush(palette().color(QPalette::WindowText)); + + //Draw the section line + int sectionLineExtent(25); + painter.drawLine(0, sectionLineExtent, 0, -sectionLineExtent); + needlePen.setStyle(Qt::SolidLine); + painter.setPen(needlePen); + int viewDirectionExtent(15); + painter.drawLine(-viewDirectionExtent, sectionLineExtent, 0, sectionLineExtent); + painter.drawLine(-viewDirectionExtent, -sectionLineExtent, 0, -sectionLineExtent); + + //Draw the arrowheads of the needle section line + needlePen.setWidth(1); + needlePen.setStyle(Qt::SolidLine); + painter.setPen(needlePen); + int arrowLength(5); + int arrowWidth(3); //actual 1/2 width + painter.drawPolygon( + QPolygon( { QPoint(0, sectionLineExtent), + QPoint(-arrowLength, sectionLineExtent + arrowWidth), + QPoint(-arrowLength, sectionLineExtent - arrowWidth), + QPoint(0, sectionLineExtent) } ) ); + painter.drawPolygon( + QPolygon( { QPoint(0, -sectionLineExtent), + QPoint(-arrowLength, -(sectionLineExtent + arrowWidth)), + QPoint(-arrowLength, -(sectionLineExtent - arrowWidth)), + QPoint(0, -sectionLineExtent) } ) ); + + //draw the actual needle + needlePen.setStyle(Qt::SolidLine); + painter.setPen(needlePen); + painter.setBrush(palette().color(QPalette::BrightText)); + int needleExtent(40); + painter.drawPolygon( + QPolygon( { QPoint(needleExtent, 0), + QPoint(0, 5), + QPoint(-15, 2), + QPoint(-15, -2), + QPoint(0, -5), + QPoint(needleExtent, 0) } ) ); + + //draw needle pivot + painter.setBrush(palette().color(QPalette::WindowText)); + int pivotSize(4); + QRect pivotRect(-pivotSize / 2, -pivotSize / 2, pivotSize, pivotSize); + painter.drawEllipse(pivotRect); + + //draw needle point + painter.setBrush(QBrush(Qt::red)); + int pointLength(5); + int pointWidth(3); + painter.drawPolygon( + QPolygon( { QPoint(needleExtent, 0), + QPoint(needleExtent - pointLength, pointWidth), + QPoint(needleExtent - pointLength, -pointWidth), + QPoint(needleExtent, 0) } ) ); + + painter.restore(); +} + +//convert a conventional angle to a Qt angle and set the dial accordingly +void CompassDialWidget::setAngle(double newAngle) +{ + m_angle = fmod(360.0 - newAngle, 360.0); + repaint(); +} + +void CompassDialWidget::setSize(int newSize) +{ + m_rect = QRect(0, 0, newSize, newSize); + repaint(); +} diff --git a/src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.h b/src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.h new file mode 100644 index 0000000000..88c553f180 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Widgets/CompassDialWidget.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * 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 * + * * + ***************************************************************************/ + +//based on a python widget from: +//https://github.com/tcalmant/demo-ipopo-qt/blob/master/pc/details/compass.py + +#ifndef COMPASSDIALWIDGET_H +#define COMPASSDIALWIDGET_H + +#include +#include + +namespace TechDrawGui { + +class CompassDialWidget : public QWidget +{ + Q_OBJECT + +public: + CompassDialWidget(QWidget* parent = 0); + ~CompassDialWidget() = default; + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + double angle() const { return m_angle; } + void setAngle(double newAngle); + void setSize(int newSize); + +public Q_SLOTS: + void slotChangeAngle(double angle) { setAngle(angle); } + void resetAngle() { setAngle(0.0); } + +protected: + void paintEvent(QPaintEvent* event) override; + void drawWidget(QPainter& painter); + void drawNeedle(QPainter& painter); + void drawMarkings(QPainter& painter); + void drawBackground(QPainter& painter); + +private: + QRect m_rect; + double m_angle; + double m_margin; + double m_markInterval; + int m_defaultSize; + int m_defaultMargin; + int m_designRadius; + int m_designDiameter; +}; + +} //namespace TechDrawGui +#endif // COMPASSDIALWIDGET_H diff --git a/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp b/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp new file mode 100644 index 0000000000..78f15a38a9 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Widgets/CompassWidget.cpp @@ -0,0 +1,254 @@ +/*************************************************************************** + * 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 * + * * + ***************************************************************************/ + +// The CompassWidget has several children widgets - a CompassDialWidget, a fine +// adjustment QDoubleSpinBox and a QPushButton that acts as an enter key +// for the spin box. +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +#include +#include + +#include "CompassDialWidget.h" +#include "CompassWidget.h" + +using namespace TechDrawGui; + +CompassWidget::CompassWidget(QWidget *parent) + : QWidget(parent), m_minimumWidth(200), m_minimumHeight(200), m_defaultMargin(10), m_angle(0.0), + m_advanceIncrement(10.0) +{ + setObjectName(QString::fromUtf8("Compass")); + m_rect = QRect(0, 0, m_minimumWidth, m_minimumHeight); + buildWidget(); + compassDial->setSize(m_minimumHeight - 2 * m_defaultMargin); + + connect(pbUseCompassSetting, &QPushButton::pressed, this, &CompassWidget::slotUseSpinboxValue); + dsbAngle->installEventFilter(this); + + connect(pbCWAdvance, &QPushButton::pressed, this, &CompassWidget::slotCWAdvance); + connect(pbCCWAdvance, &QPushButton::pressed, this, &CompassWidget::slotCCWAdvance); +} + +//trap Enter press in dsbAngle so as not to invoke task accept processing +bool CompassWidget::eventFilter(QObject *target, QEvent *event) +{ + if (target == dsbAngle) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + dsbAngle->interpretText(); + slotSpinBoxEnter(dsbAngle->value()); + return true; + } + } + } + return QWidget::eventFilter(target, event); +} + +void CompassWidget::buildWidget() +{ + resize(m_minimumWidth, m_minimumHeight); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(sizePolicy.hasHeightForWidth()); + setSizePolicy(sizePolicy); + setMinimumSize(QSize(m_minimumWidth, m_minimumHeight)); + compassLayout = new QVBoxLayout(this); + compassLayout->setObjectName(QString::fromUtf8("CompassLayout")); + + compassDialLayout = new QHBoxLayout(); + compassDialLayout->setObjectName(QString::fromUtf8("compassDialLayout")); + + pbCWAdvance = new QPushButton(this); + pbCWAdvance->setObjectName(QString::fromUtf8("pbCWAdvance")); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/icons/arrow-cw.svg"), QSize(), QIcon::Normal, QIcon::On); + pbCWAdvance->setIcon(icon1); + compassDialLayout->addWidget(pbCWAdvance); + + compassDial = new CompassDialWidget(this); + compassDial->setObjectName(QString::fromUtf8("CompassDial")); + compassDialLayout->addWidget(compassDial); + + pbCCWAdvance = new QPushButton(this); + pbCCWAdvance->setObjectName(QString::fromUtf8("pbCCWAdvance")); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/arrow-ccw.svg"), QSize(), QIcon::Normal, QIcon::On); + pbCCWAdvance->setIcon(icon2); + compassDialLayout->addWidget(pbCCWAdvance); + + compassDialLayout->setStretch(1, 2); + compassLayout->addLayout(compassDialLayout); + + compassControlLayout = new QHBoxLayout(); + compassControlLayout->setObjectName(QString::fromUtf8("compassControlLayout")); + compassControlLabel = new QLabel(this); + compassControlLabel->setObjectName(QString::fromUtf8("compassControlLabel")); + QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::Minimum); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(compassControlLabel->sizePolicy().hasHeightForWidth()); + compassControlLabel->setSizePolicy(sizePolicy2); + + compassControlLayout->addWidget(compassControlLabel); + + dsbAngle = new QDoubleSpinBox(this); + dsbAngle->setObjectName(QString::fromUtf8("dsbAngle")); + sizePolicy2.setHeightForWidth(dsbAngle->sizePolicy().hasHeightForWidth()); + dsbAngle->setSizePolicy(sizePolicy2); + dsbAngle->setMinimumSize(QSize(75, 26)); + dsbAngle->setMouseTracking(true); + dsbAngle->setFocusPolicy(Qt::ClickFocus); + dsbAngle->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); + dsbAngle->setKeyboardTracking(false); + dsbAngle->setSuffix(QString::fromUtf8("\302\260")); + dsbAngle->setMaximum(360.000000000000000); + dsbAngle->setMinimum(-360.000000000000000); + + compassControlLayout->addWidget(dsbAngle); + + pbUseCompassSetting = new QPushButton(this); + pbUseCompassSetting->setObjectName(QString::fromUtf8("pbUseCompassSetting")); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/edit_OK.svg"), QSize(), QIcon::Normal, QIcon::On); + pbUseCompassSetting->setIcon(icon); + pbUseCompassSetting->setText(QString()); + + compassControlLayout->addWidget(pbUseCompassSetting); + + compassControlLayout->setStretch(0, 3); + compassControlLayout->setStretch(1, 2); + + compassLayout->addLayout(compassControlLayout); + + retranslateUi(); +} + +void CompassWidget::retranslateUi() +{ + compassControlLabel->setText( + QApplication::translate("CompassWidget", "View Direction as Angle", nullptr)); +#ifndef QT_NO_TOOLTIP + dsbAngle->setToolTip(QApplication::translate( + "CompassWidget", "The view direction angle relative to +X in the BaseView.", nullptr)); + pbUseCompassSetting->setToolTip(QApplication::translate( + "CompassWidget", "Use the current view direction to set the Section Normal.", nullptr)); + pbCWAdvance->setToolTip(QApplication::translate( + "CompassWidget", "Advance the view direction in clockwise direction.", nullptr)); + pbCCWAdvance->setToolTip(QApplication::translate( + "CompassWidget", "Advance the view direction in anti-clockwise direction.", nullptr)); +#endif// QT_NO_TOOLTIP +} + +QSize CompassWidget::sizeHint() const { return m_rect.size(); } + +QSize CompassWidget::minimumSizeHint() const +{ + return QRect(0, 0, m_minimumWidth, m_minimumHeight).size(); +} + +void CompassWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QWidget::paintEvent(event); +} + +//general purpose angle update from external source. +void CompassWidget::setDialAngle(double newAngle) +{ + // Base::Console().Message("CW::setDialAngle(%.3f)\n", newAngle); + m_angle = newAngle; + if (compassDial) { + compassDial->setAngle(m_angle); + } + if (dsbAngle) { + dsbAngle->setValue(m_angle); + } +} + +//slot for updates from spinbox arrows or typing. +void CompassWidget::slotSpinBoxUpdate(double newAngle) +{ + // Base::Console().Message("CW::slotSpinBoxUpdate(%.3f)\n", newAngle); + m_angle = newAngle; + Q_EMIT angleChanged(m_angle); + if (compassDial) { + compassDial->setAngle(m_angle); + } +} + +//slot for updates from spinbox on Enter/Return press. +void CompassWidget::slotSpinBoxEnter(double newAngle) +{ + // Base::Console().Message("CW::slotSpinBoxEnter(%.3f)\n", newAngle); + if (dsbAngle) { + m_angle = newAngle; + Q_EMIT angleChanged(m_angle); + if (compassDial) { + compassDial->setAngle(m_angle); + } + } +} + +//slot for OK button press +void CompassWidget::slotUseSpinboxValue() +{ + // Base::Console().Message("CW::slotUseSpinboxValue()\n"); + if (dsbAngle) { + dsbAngle->interpretText(); + m_angle = dsbAngle->value(); + Q_EMIT angleChanged(m_angle); + } + if (compassDial) { + compassDial->setAngle(m_angle); + } +} + +void CompassWidget::slotCWAdvance() +{ + double angle = m_angle - m_advanceIncrement; + if (angle < -360.0) { + angle = angle + 360.0; + } + setDialAngle(angle); +} + +void CompassWidget::slotCCWAdvance() +{ + double angle = m_angle + m_advanceIncrement; + setDialAngle(fmod(angle, 360.0)); +} + +void CompassWidget::setAdvanceIncrement(double newIncrement) { m_advanceIncrement = newIncrement; } diff --git a/src/Mod/TechDraw/Gui/Widgets/CompassWidget.h b/src/Mod/TechDraw/Gui/Widgets/CompassWidget.h new file mode 100644 index 0000000000..191235f16e --- /dev/null +++ b/src/Mod/TechDraw/Gui/Widgets/CompassWidget.h @@ -0,0 +1,105 @@ +/*************************************************************************** + * 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 COMPASSWIDGET_H +#define COMPASSWIDGET_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QLabel; +class QHBoxLayout; +class QPushButton; +class QVBoxLayout; +QT_END_NAMESPACE + +namespace TechDrawGui { + +class CompassDialWidget; + +class CompassWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY( double angle READ dialAngle WRITE setDialAngle NOTIFY angleChanged) + +public: + CompassWidget(QWidget* parent = 0); + ~CompassWidget() = default; + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + bool eventFilter(QObject *target, QEvent *event) override; + void retranslateUi(); + double dialAngle() const { return m_angle; } + void setDialAngle(double newAngle); + void setAdvanceIncrement(double newIncrement); + double advanceIncrement() const { return m_advanceIncrement; } + +Q_SIGNALS: + void angleChanged(double angle); + void angleSet(double angle); + +public Q_SLOTS: + void slotChangeAngle(double angle) { setDialAngle(angle); } + void slotSpinBoxUpdate(double newAngle); + void slotSpinBoxEnter(double newAngle); + void slotUseSpinboxValue(); + void resetAngle() { setDialAngle(0.0); } //conventional angles + void setToEast() { setDialAngle(0.0); } + void setToNorth() { setDialAngle(90.0); } + void setToWest() { setDialAngle(180.0); } + void setToSouth() { setDialAngle(270.0); } + void slotCWAdvance(); + void slotCCWAdvance(); + +protected: + void paintEvent(QPaintEvent* event) override; + void buildWidget(); + double changeAngleConvention(double CWY) const; + +private: + QRect m_rect; + int m_minimumWidth; + int m_minimumHeight; + int m_defaultMargin; + double m_angle; + double m_advanceIncrement; + + QVBoxLayout* compassLayout; + QHBoxLayout* compassDialLayout; + QHBoxLayout* compassControlLayout; + + CompassDialWidget* compassDial; +// DoubleSpinBoxNoEnter* dsbAngle; + QDoubleSpinBox* dsbAngle; + QLabel* compassControlLabel; + QPushButton* pbUseCompassSetting; + QPushButton* pbCWAdvance; + QPushButton* pbCCWAdvance; +}; + +} //namespace TechDrawGui +#endif // COMPASSWIDGET_H + diff --git a/src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.cpp b/src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.cpp new file mode 100644 index 0000000000..15b68ad713 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.cpp @@ -0,0 +1,265 @@ +/*************************************************************************** + * 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 * + * * + ***************************************************************************/ + +// A widget for editing Vector3d without taking up too much space in the UI. + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include "VectorEditWidget.h" + +using namespace TechDrawGui; +using namespace TechDraw; + +VectorEditWidget::VectorEditWidget(QWidget* parent) : QWidget(parent), + m_minimumWidth(200), + m_minimumHeight(30), + m_expandedHeight(155), + m_blockNotify(false) +{ + m_size = QSize(m_minimumWidth, m_minimumHeight); + setObjectName(QString::fromUtf8("VectorEdit")); + buildWidget(); + + connect(tbExpand, SIGNAL(toggled(bool)), this, SLOT(slotExpandButtonToggled(bool))); + connect(dsbX, SIGNAL(valueChanged(double)), this, SLOT(xValueChanged(double))); + connect(dsbY, SIGNAL(valueChanged(double)), this, SLOT(yValueChanged(double))); + connect(dsbZ, SIGNAL(valueChanged(double)), this, SLOT(zValueChanged(double))); + + dsbX->installEventFilter(this); + dsbY->installEventFilter(this); + dsbZ->installEventFilter(this); +} + +//trap Enter press in dsb? so as not to invoke task accept processing +bool VectorEditWidget::eventFilter(QObject *target, QEvent *event) +{ + if (target == dsbX || + target == dsbY || + target == dsbZ) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return || + keyEvent->key() == Qt::Key_Enter) { + QDoubleSpinBox* dsb = static_cast(target); + dsb->interpretText(); + Q_EMIT dsb->valueChanged(dsb->value()); + return true; + } + } + } + return QWidget::eventFilter(target, event); +} +void VectorEditWidget::setLabel(std::string newLabel) +{ + QString qNewLabelString = Base::Tools::fromStdString(newLabel); + lvectorName->setText(qNewLabelString); +} + +void VectorEditWidget::setLabel(QString newLabel) +{ + lvectorName->setText(newLabel); +} + +void VectorEditWidget::setValue(Base::Vector3d newValue) +{ +// Base::Console().Message("VEW::setValue(%s)\n", DrawUtil::formatVector(newValue).c_str()); + m_value = newValue; + dsbX->setValue(m_value.x); + dsbY->setValue(m_value.y); + dsbZ->setValue(m_value.z); + updateDisplay(); +} + +void VectorEditWidget::setValueNoNotify(Base::Vector3d newValue) +{ +// Base::Console().Message("VEW::setValue(%s)\n", DrawUtil::formatVector(newValue).c_str()); + m_value = newValue; + m_blockNotify = true; + dsbX->setValue(m_value.x); + dsbY->setValue(m_value.y); + dsbZ->setValue(m_value.z); + m_blockNotify = false; + updateDisplay(); +} + +void VectorEditWidget::slotExpandButtonToggled(bool checked) +{ +// Base::Console().Message("VEW::slotExpand - checked: %d\n", checked); + if (checked) { + vectorEditLayout->addLayout(VectorEditItemLayout); + vectorEditLayout->addItem(verticalSpacer); + m_size = QSize(m_minimumWidth, m_expandedHeight); + + } else { + vectorEditLayout->removeItem(VectorEditItemLayout); + vectorEditLayout->removeItem(verticalSpacer); + m_size = QSize(m_minimumWidth, m_minimumHeight); + } +} + +void VectorEditWidget::xValueChanged(double newValue) +{ +// Base::Console().Message("VEW::xValueChanged(%.3f)\n", newValue); + m_value.x = newValue; + updateDisplay(); + if (!m_blockNotify) { + Q_EMIT valueChanged(m_value); + } +} +void VectorEditWidget::yValueChanged(double newValue) +{ +// Base::Console().Message("VEW::yValueChanged(%.3f)\n", newValue); + m_value.y = newValue; + updateDisplay(); + if (!m_blockNotify) { + Q_EMIT valueChanged(m_value); + } +} +void VectorEditWidget::zValueChanged(double newValue) +{ +// Base::Console().Message("VEW::zValueChanged(%.3f)\n", newValue); + m_value.z = newValue; + updateDisplay(); + if (!m_blockNotify) { + Q_EMIT valueChanged(m_value); + } +} + +void VectorEditWidget::updateDisplay() +{ +// Base::Console().Message("VEW::updateDisplay() - m_value: %s\n", DrawUtil::formatVector(m_value).c_str()); + QString qNewDisplayString = Base::Tools::fromStdString(DrawUtil::formatVector(m_value)); + leVectorDisplay->setText(qNewDisplayString); +} + +QSize VectorEditWidget::minimumSizeHint() const +{ + return m_size; +} + +void VectorEditWidget::buildWidget() +{ + if (objectName().isEmpty()) + setObjectName(QString::fromUtf8("VectorEdit")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + setSizePolicy(sizePolicy); + + vectorEditLayout = new QVBoxLayout(this); + vectorEditLayout->setObjectName(QString::fromUtf8("vectorEditLayout")); + vectorEditLayout->setContentsMargins(0, 0, 0, 0); + VectorEditButtonLayout = new QHBoxLayout(); + VectorEditButtonLayout->setSpacing(0); + VectorEditButtonLayout->setObjectName(QString::fromUtf8("VectorEditButtonLayout")); + + lvectorName = new QLabel(this); + lvectorName->setObjectName(QString::fromUtf8("lvectorName")); + VectorEditButtonLayout->addWidget(lvectorName); + + leVectorDisplay = new QLineEdit(this); + leVectorDisplay->setObjectName(QString::fromUtf8("leVectorDisplay")); + VectorEditButtonLayout->addWidget(leVectorDisplay); + + tbExpand = new QToolButton(this); + tbExpand->setObjectName(QString::fromUtf8("tbExpand")); + tbExpand->setText(QString::fromUtf8("...")); + tbExpand->setCheckable(true); + VectorEditButtonLayout->addWidget(tbExpand); + + VectorEditButtonLayout->setStretch(0, 1); + VectorEditButtonLayout->setStretch(1, 1); + vectorEditLayout->addLayout(VectorEditButtonLayout); + + VectorEditItemLayout = new QGridLayout(); + VectorEditItemLayout->setObjectName(QString::fromUtf8("VectorEditItemLayout")); + + lX = new QLabel(); + lX->setObjectName(QString::fromUtf8("lX")); + lX->setText(QString::fromUtf8("X:")); + VectorEditItemLayout->addWidget(lX, 0, 0, 1, 1); + + dsbX = new Gui::DoubleSpinBox(); + dsbX->setObjectName(QString::fromUtf8("dsbX")); + dsbX->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + dsbX->setKeyboardTracking(false); + dsbX->setMaximum(std::numeric_limits::max()); + dsbX->setMinimum(std::numeric_limits::lowest()); + dsbX->setDecimals(Base::UnitsApi::getDecimals()); + VectorEditItemLayout->addWidget(dsbX, 0, 1, 1, 1); + + lY = new QLabel(); + lY->setObjectName(QString::fromUtf8("lY")); + lY->setText(QString::fromUtf8("Y:")); + VectorEditItemLayout->addWidget(lY, 1, 0, 1, 1); + + dsbY = new Gui::DoubleSpinBox(); + dsbY->setObjectName(QString::fromUtf8("dsbY")); + dsbY->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + dsbY->setKeyboardTracking(false); + dsbY->setMaximum(std::numeric_limits::max()); + dsbY->setMinimum(std::numeric_limits::lowest()); + dsbY->setDecimals(Base::UnitsApi::getDecimals()); + VectorEditItemLayout->addWidget(dsbY, 1, 1, 1, 1); + + lZ = new QLabel(); + lZ->setObjectName(QString::fromUtf8("lZ")); + lZ->setText(QString::fromUtf8("Z:")); + VectorEditItemLayout->addWidget(lZ, 2, 0, 1, 1); + + dsbZ = new Gui::DoubleSpinBox(); + dsbZ->setObjectName(QString::fromUtf8("dsbZ")); + dsbZ->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + dsbZ->setKeyboardTracking(false); + dsbZ->setMaximum(std::numeric_limits::max()); + dsbZ->setMinimum(std::numeric_limits::lowest()); + dsbZ->setDecimals(Base::UnitsApi::getDecimals()); + VectorEditItemLayout->addWidget(dsbZ, 2, 1, 1, 1); + + verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); +} + + diff --git a/src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.h b/src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.h new file mode 100644 index 0000000000..f6c7e38b36 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Widgets/VectorEditWidget.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * 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 VECTOREDITWIDGET_H +#define VECTOREDITWIDGET_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QLabel; +class QLineEdit; +class QToolButton; +class QSpacerItem; +QT_END_NAMESPACE + +namespace Gui { +class DoubleSpinBox; +} + +#include + +namespace TechDrawGui { + +class VectorEditWidget : public QWidget +{ + Q_OBJECT + +public: + VectorEditWidget(QWidget* parent = 0); + ~VectorEditWidget() = default; + + QSize minimumSizeHint() const override; + bool eventFilter(QObject *target, QEvent *event) override; + + void setLabel(std::string newLabel); + void setLabel(QString newLabel); + Base::Vector3d value() const { return m_value; } + +Q_SIGNALS: + void valueChanged(Base::Vector3d newValue); + +public Q_SLOTS: + void setValue(Base::Vector3d newValue); + void setValueNoNotify(Base::Vector3d newValue); + +protected: + void buildWidget(); + +protected Q_SLOTS: + void slotExpandButtonToggled(bool checked); + void xValueChanged(double newValue); + void yValueChanged(double newValue); + void zValueChanged(double newValue); + + void updateDisplay(); + +private: + int m_minimumWidth; + int m_minimumHeight; + int m_expandedHeight; + bool m_blockNotify; + + QSize m_size; + + Base::Vector3d m_value; + + QVBoxLayout *vectorEditLayout; + QHBoxLayout *VectorEditButtonLayout; + QLabel *lvectorName; + QLineEdit *leVectorDisplay; + QToolButton *tbExpand; + QGridLayout *VectorEditItemLayout; + Gui::DoubleSpinBox *dsbX; + Gui::DoubleSpinBox *dsbY; + Gui::DoubleSpinBox *dsbZ; + QLabel *lX; + QLabel *lY; + QLabel *lZ; + QSpacerItem *verticalSpacer; +}; + +} //namespace TechDrawGui +#endif // VECTOREDITWIDGET_H diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index ec7e6628aa..ed0cb7533d 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -246,8 +246,9 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *views << "TechDraw_View"; *views << "TechDraw_ActiveView"; *views << "TechDraw_ProjectionGroup"; - *views << "TechDraw_SectionView"; - *views << "TechDraw_ComplexSection"; + *views << "TechDraw_SectionGroup"; +// *views << "TechDraw_SectionView"; +// *views << "TechDraw_ComplexSection"; *views << "TechDraw_DetailView"; *views << "TechDraw_DraftView"; *views << "TechDraw_ArchView"; @@ -391,10 +392,10 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const views->setCommand("Views"); *views << "TechDraw_View"; *views << "TechDraw_ActiveView"; -// *views << "TechDraw_NewMulti"; //deprecated *views << "TechDraw_ProjectionGroup"; - *views << "TechDraw_SectionView"; - *views << "TechDraw_ComplexSection"; + *views << "TechDraw_SectionGroup"; +// *views << "TechDraw_SectionView"; +// *views << "TechDraw_ComplexSection"; *views << "TechDraw_DetailView"; *views << "TechDraw_DraftView"; *views << "TechDraw_SpreadsheetView";