Download src/Mod/TechDraw/App/DrawBrokenView.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 48.4 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/DrawBrokenView.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/TechDraw/App/DrawBrokenView.cpp
-
curl -L -o DrawBrokenView.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/DrawBrokenView.cpp
48.4 kB
| // SPDX-License-Identifier: LGPL-2-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2024 WandererFan <wandererfan@gmail.com> * | |
| * * | |
| * 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 * | |
| * * | |
| ***************************************************************************/ | |
| //! DrawBrokenView produces a view of the Source shapes after a portion of the shapes | |
| //! has been removed. | |
| //! DrawBrokenView processing is essentially the same as DrawViewPart, except that the | |
| //! Source shapes are cut and the cut pieces moved before the projection/hlr steps. | |
| //! | |
| //! Break points are defined by | |
| //! - a horizontal or vertical edge whose endpoints represent where the source | |
| //! shape should be cut, or, | |
| //! - a sketch containing 2 horizontal or vertical edges whose midpoints represent | |
| //! where the source shape should be cut. | |
| //! Terminology: | |
| //! - "break direction" is the direction pieces will need to be moved along to | |
| //! close a break. for edges, the break direction is parallel to the edge. | |
| //! for sketch based breaks, the break direction is perpendicular to the edges | |
| //! in the sketch. | |
| // ??? is option 1 actually working? Not used in practice? | |
| using namespace TechDraw; | |
| using DU = DrawUtil; | |
| using SU = ShapeUtils; | |
| //=========================================================================== | |
| // DrawBrokenView | |
| //=========================================================================== | |
| // NOLINTNEXTLINE | |
| const char *DrawBrokenView::BreakTypeEnums[] = { | |
| QT_TRANSLATE_NOOP("DrawBrokenView", "None"), | |
| QT_TRANSLATE_NOOP("DrawBrokenView", "ZigZag"), | |
| QT_TRANSLATE_NOOP("DrawBrokenView", "Simple"), | |
| nullptr}; | |
| PROPERTY_SOURCE(TechDraw::DrawBrokenView, TechDraw::DrawViewPart) | |
| DrawBrokenView::DrawBrokenView() | |
| { | |
| static const char* sgroup = "Broken view"; | |
| constexpr double DefaultGapSizeMm{10.0}; | |
| ADD_PROPERTY_TYPE(Breaks, (nullptr), sgroup, App::Prop_None, | |
| "Objects in the 3D view that define the start/end points and direction of breaks in this view."); | |
| Breaks.setScope(App::LinkScope::Global); | |
| Breaks.setAllowExternal(true); | |
| ADD_PROPERTY_TYPE(Gap, | |
| (DefaultGapSizeMm), | |
| sgroup, | |
| App::Prop_None, | |
| "The separation distance for breaks in this view (unscaled 3D length)."); | |
| } | |
| short DrawBrokenView::mustExecute() const | |
| { | |
| if (isRestoring()) { | |
| return TechDraw::DrawViewPart::mustExecute(); | |
| } | |
| if (Breaks.isTouched() || | |
| Gap.isTouched() ) { | |
| return 1; | |
| } | |
| return TechDraw::DrawViewPart::mustExecute(); | |
| } | |
| App::DocumentObjectExecReturn* DrawBrokenView::execute() | |
| { | |
| if (!keepUpdated()) { | |
| return App::DocumentObject::StdReturn; | |
| } | |
| if (waitingForResult()) { | |
| // don't start something new until the in-progress events complete | |
| return DrawView::execute(); // NOLINT | |
| } | |
| TopoDS_Shape shape = getSourceShape(); | |
| if (shape.IsNull()) { | |
| Base::Console().message("DBV::execute - %s - Source shape is Null.\n", getNameInDocument()); | |
| return DrawView::execute(); // NOLINT | |
| } | |
| BRepBuilderAPI_Copy BuilderCopy(shape); | |
| TopoDS_Shape safeShape = BuilderCopy.Shape(); | |
| m_unbrokenCenter = SU::findCentroidVec(safeShape, getProjectionCS()); | |
| TopoDS_Shape brokenShape = breakShape(safeShape); | |
| m_compressedShape = compressShape(brokenShape); | |
| partExec(m_compressedShape); | |
| return DrawView::execute(); // NOLINT | |
| } | |
| //! applies the breaks to the input shape. returns a compound of broken | |
| //! pieces moved so they are separated by a distance of Gap. | |
| TopoDS_Shape DrawBrokenView::breakShape(const TopoDS_Shape& shapeToBreak) const | |
| { | |
| auto breaksAll = Breaks.getValues(); | |
| TopoDS_Shape updatedShape = shapeToBreak; | |
| for (auto& item : breaksAll) { | |
| updatedShape = apply1Break(*item, updatedShape); | |
| } | |
| return updatedShape; | |
| } | |
| //! applies a single break to the input shape. returns a compound of the | |
| //! broken pieces. | |
| TopoDS_Shape DrawBrokenView::apply1Break(const App::DocumentObject& breakObj, const TopoDS_Shape& inShape) const | |
| { | |
| auto breakPoints = breakPointsFromObj(breakObj); | |
| if (breakPoints.first.IsEqual(breakPoints.second, EWTOLERANCE)) { | |
| Base::Console().message("DBV::apply1Break - break points are equal\n"); | |
| return inShape; | |
| } | |
| auto breakDirection = DU::closestBasisOriented(directionFromObj(breakObj)); | |
| breakDirection.Normalize(); | |
| // make a halfspace that is positioned at the first breakpoint and extends | |
| // in the direction of the second point | |
| Base::Vector3d moveDir0 = breakPoints.second - breakPoints.first; | |
| moveDir0.Normalize(); | |
| moveDir0 = DU::closestBasisOriented(moveDir0); | |
| auto halfSpace0 = makeHalfSpace(breakPoints.first, moveDir0, breakPoints.second); | |
| FCBRepAlgoAPI_Cut mkCut0(inShape, halfSpace0); | |
| if (!mkCut0.IsDone()) { | |
| Base::Console().message("DBV::apply1Break - cut0 failed\n"); | |
| } | |
| TopoDS_Shape cut0 = mkCut0.Shape(); | |
| // make a halfspace that is positioned at the second breakpoint and extends | |
| // in the direction of the first point | |
| Base::Vector3d moveDir1 = breakPoints.first - breakPoints.second; | |
| moveDir1.Normalize(); | |
| moveDir1 = DU::closestBasisOriented(moveDir1); | |
| auto halfSpace1 = makeHalfSpace(breakPoints.second, moveDir1, breakPoints.first); | |
| FCBRepAlgoAPI_Cut mkCut1(inShape, halfSpace1); | |
| if (!mkCut1.IsDone()) { | |
| Base::Console().message("DBV::apply1Break - cut1 failed\n"); | |
| } | |
| TopoDS_Shape cut1 = mkCut1.Shape(); | |
| BRep_Builder builder; | |
| TopoDS_Compound result; | |
| builder.MakeCompound(result); | |
| builder.Add(result, cut0); | |
| builder.Add(result, cut1); | |
| return result; | |
| } | |
| //! compress the broken shape at the breaks | |
| TopoDS_Shape DrawBrokenView::compressShape(const TopoDS_Shape& shapeToCompress) const | |
| { | |
| TopoDS_Shape compressed = compressHorizontal(shapeToCompress); | |
| return compressVertical(compressed); | |
| } | |
| //! move the broken pieces in the input shape "right" to close up the removed areas. | |
| //! note: breaks and pieces should not intersect by this point | |
| //! a break: BbbbbbbB | |
| //! a piece: PpppP move right by removed(B) | |
| //! a piece: PppppP no need to move | |
| TopoDS_Shape DrawBrokenView::compressHorizontal(const TopoDS_Shape& shapeToCompress)const | |
| { | |
| std::vector<TopoDS_Shape> pieces = getPieces(shapeToCompress); | |
| std::vector<App::DocumentObject*> breaksAll = Breaks.getValues(); | |
| Base::Vector3d moveDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().XDirection())); | |
| bool descend = false; | |
| BreakList sortedBreaks = makeSortedBreakList(breaksAll, moveDirection, descend); | |
| PieceLimitList limits = getPieceLimits(pieces, moveDirection); | |
| // for each break, move all the pieces left of the break to the right by the removed amount | |
| // for the break | |
| for (auto& breakItem : sortedBreaks) { | |
| // check each break against all the pieces | |
| Base::Vector3d netBreakDisplace = | |
| moveDirection * (removedLengthFromObj(*breakItem.breakObj) - Gap.getValue()); | |
| size_t iPiece{0}; | |
| for (auto& pieceLimit : limits) { | |
| // check each piece against the current break | |
| if (moveThisPiece(pieceLimit, breakItem, moveDirection)) { | |
| // piece is to left of break, so needs to move right | |
| TopoDS_Shape temp = ShapeUtils::moveShape(pieces.at(iPiece), netBreakDisplace); | |
| pieces.at(iPiece) = temp; | |
| } | |
| iPiece++; | |
| } | |
| } | |
| // turn updated pieces into a compound | |
| BRep_Builder builder; | |
| TopoDS_Compound result; | |
| builder.MakeCompound(result); | |
| for (auto& pieceShape : pieces) { | |
| builder.Add(result, pieceShape); | |
| } | |
| return result; | |
| } | |
| //! move the broken pieces in the input shape "Up" to close up the removed areas. | |
| //! note: breaks and pieces should not intersect by this point | |
| TopoDS_Shape DrawBrokenView::compressVertical(const TopoDS_Shape& shapeToCompress)const | |
| { | |
| auto pieces = getPieces(shapeToCompress); | |
| auto breaksAll = Breaks.getValues(); | |
| // not sure about using closestBasis here. may prevent oblique breaks later. | |
| auto moveDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().YDirection())); | |
| bool descend = false; | |
| auto sortedBreaks = makeSortedBreakList(breaksAll, moveDirection, descend); | |
| auto limits = getPieceLimits(pieces, moveDirection); | |
| // for each break, move all the pieces below the break up by the removed amount | |
| // for the break | |
| for (auto& breakItem : sortedBreaks) { | |
| // check each break against all the pieces | |
| Base::Vector3d netBreakDisplace = | |
| moveDirection * (removedLengthFromObj(*breakItem.breakObj) - Gap.getValue()); | |
| size_t iPiece{0}; | |
| for (auto& pieceLimit : limits) { | |
| // check each piece against the current break using a fuzzy equality | |
| if (moveThisPiece(pieceLimit, breakItem, moveDirection)) { | |
| // piece is below the break, move it up | |
| TopoDS_Shape temp = ShapeUtils::moveShape(pieces.at(iPiece), netBreakDisplace); | |
| pieces.at(iPiece) = temp; | |
| } | |
| iPiece++; | |
| } | |
| } | |
| // turn updated pieces into a compound | |
| BRep_Builder builder; | |
| TopoDS_Compound result; | |
| builder.MakeCompound(result); | |
| for (auto& pieceShape : pieces) { | |
| builder.Add(result, pieceShape); | |
| } | |
| return result; | |
| } | |
| //! returns a half space. The half space is defined by a plane created by (planePoint, | |
| //! plane normal) and a point inside the half space (pointInSpace). | |
| TopoDS_Shape DrawBrokenView::makeHalfSpace(const Base::Vector3d& planePoint, | |
| const Base::Vector3d& planeNormal, | |
| const Base::Vector3d& pointInSpace) const | |
| { | |
| auto origin = Base::convertTo<gp_Pnt>(planePoint); | |
| auto axis = Base::convertTo<gp_Dir>(planeNormal); | |
| gp_Pln plane(origin, axis); | |
| BRepBuilderAPI_MakeFace mkFace(plane); | |
| BRepPrimAPI_MakeHalfSpace mkHalf(mkFace.Face(), Base::convertTo<gp_Pnt>(pointInSpace)); | |
| return mkHalf.Solid(); | |
| } | |
| //! extract the break points from the break object. | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::breakPointsFromObj(const App::DocumentObject& breakObj) const | |
| { | |
| if (ShapeExtractor::isSketchObject(&breakObj)) { | |
| return breakPointsFromSketch(breakObj); | |
| } | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (!locShape.IsNull() && locShape.ShapeType() == TopAbs_EDGE) { | |
| return breakPointsFromEdge(breakObj); | |
| } | |
| return {Base::Vector3d(), Base::Vector3d()}; | |
| } | |
| //! extract the breakDirection from the break object. The break direction is | |
| //! perpendicular to the break lines. | |
| Base::Vector3d DrawBrokenView::directionFromObj(const App::DocumentObject& breakObj) const | |
| { | |
| std::pair<Base::Vector3d, Base::Vector3d> ends = breakPointsFromObj(breakObj); | |
| Base::Vector3d direction = ends.second - ends.first; | |
| direction.Normalize(); | |
| // does this need to be oriented? | |
| return DU::closestBasis(direction); | |
| } | |
| //! extract the breakDirection from the break object in a form the gui will understand. | |
| Base::Vector3d DrawBrokenView::guiDirectionFromObj(const App::DocumentObject& breakObj) const | |
| { | |
| return projectPoint(directionFromObj(breakObj)); | |
| } | |
| //! calculate the length to be removed as specified by break object. | |
| double DrawBrokenView::removedLengthFromObj(const App::DocumentObject& breakObj) const | |
| { | |
| std::pair<Base::Vector3d, Base::Vector3d> ends = breakPointsFromObj(breakObj); | |
| Base::Vector3d direction = ends.second - ends.first; | |
| return direction.Length(); | |
| } | |
| //! determine if a given object can be used as a break object | |
| bool DrawBrokenView::isBreakObject(const App::DocumentObject& breakObj) | |
| { | |
| if (ShapeExtractor::isSketchObject(&breakObj)) { | |
| return isBreakObjectSketch(breakObj); | |
| } | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| return (!locShape.IsNull() && locShape.ShapeType() == TopAbs_EDGE); | |
| } | |
| //! determine if a sketch object can be used as a break object | |
| //! to be a break object the sketch must contain 2 edges, both of which are | |
| //! horizontal or vertical | |
| bool DrawBrokenView::isBreakObjectSketch(const App::DocumentObject& breakObj) | |
| { | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (locShape.IsNull()) { | |
| return false; | |
| } | |
| // get the edges from the shape. | |
| std::vector<TopoDS_Edge> sketchEdges; | |
| TopExp_Explorer expl(locShape, TopAbs_EDGE); | |
| for (; expl.More(); expl.Next()) { | |
| sketchEdges.push_back(TopoDS::Edge(expl.Current())); | |
| } | |
| // there should be 2 | |
| if (sketchEdges.size() != 2) { | |
| Base::Console().message("DBV::isBreakObjectSketch - wrong number of edges\n"); | |
| return false; | |
| } | |
| // they should both have the same orientation | |
| TopoDS_Edge first = sketchEdges.front(); | |
| TopoDS_Edge last = sketchEdges.back(); | |
| return SU::edgesAreParallel(first, last); | |
| } | |
| //! extract the break points from a sketch. The sketch is expected to contain | |
| //! 2 vertical or horizontal edges only. | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::breakPointsFromSketch(const App::DocumentObject& breakObj) const | |
| { | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (locShape.IsNull()) { | |
| return {Base::Vector3d(), Base::Vector3d()};; | |
| } | |
| // get the edges from the shape. | |
| // vertical or horizontal | |
| std::vector<TopoDS_Edge> sketchEdges; | |
| TopExp_Explorer expl(locShape, TopAbs_EDGE); | |
| for (; expl.More(); expl.Next()) { | |
| sketchEdges.push_back(TopoDS::Edge(expl.Current())); | |
| } | |
| // there should be 2 | |
| if (sketchEdges.size() != 2) { | |
| return {Base::Vector3d(), Base::Vector3d()}; | |
| } | |
| // they should both have the same orientation | |
| TopoDS_Edge first = sketchEdges.front(); | |
| TopoDS_Edge last = sketchEdges.back(); | |
| if ((isVertical(first) && isVertical(last)) || | |
| (isHorizontal(first) && isHorizontal(last))) { | |
| // get points on each edge that are in line with the center of the sketch | |
| // along the break direction | |
| Bnd_Box edgeBox; | |
| edgeBox.SetGap(0.0); | |
| BRepBndLib::AddOptimal(first, edgeBox); | |
| BRepBndLib::AddOptimal(last, edgeBox); | |
| double xMin = 0, xMax = 0, yMin = 0, yMax = 0, zMin = 0, zMax = 0; // NOLINT | |
| edgeBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); | |
| Base::Vector3d center( (xMin + xMax) / 2, | |
| (yMin + yMax) / 2, | |
| (zMin + zMax) / 2 ); | |
| auto ends0 = SU::getEdgeEnds(first); | |
| auto dir0 = ends0.second - ends0.first; | |
| dir0.Normalize(); | |
| // get a point on edge first on a perpendicular line through center | |
| auto break0 = center.Perpendicular(ends0.first, dir0); | |
| auto ends1 = SU::getEdgeEnds(last); | |
| auto dir1 = ends1.second - ends1.first; | |
| dir1.Normalize(); | |
| auto break1 = center.Perpendicular(ends1.first, dir1); | |
| return { break0, break1 }; | |
| } | |
| return {Base::Vector3d(), Base::Vector3d()}; | |
| } | |
| //! extract the break points from an edge. The edge should be vertical or horizontal, perpendicular to the desired | |
| //! break lines. | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::breakPointsFromEdge(const App::DocumentObject& breakObj) const | |
| { | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (locShape.IsNull() || locShape.ShapeType() != TopAbs_EDGE) { | |
| return {Base::Vector3d(), Base::Vector3d()}; | |
| } | |
| TopoDS_Edge edge = TopoDS::Edge(locShape); | |
| gp_Pnt start = BRep_Tool::Pnt(TopExp::FirstVertex(edge)); | |
| gp_Pnt end = BRep_Tool::Pnt(TopExp::LastVertex(edge)); | |
| return {Base::convertTo<Base::Vector3d>(start), Base::convertTo<Base::Vector3d>(end)}; | |
| } | |
| //! determine the rectangle to be occupied by the break lines. used by gui. | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::breakBoundsFromObj(const App::DocumentObject& breakObj) const | |
| { | |
| if (ShapeExtractor::isSketchObject(&breakObj)) { | |
| auto unscaled = breakBoundsFromSketch(breakObj); | |
| return scalePair(unscaled); | |
| } | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (!locShape.IsNull() && locShape.ShapeType() == TopAbs_EDGE) { | |
| auto unscaled = breakBoundsFromEdge(breakObj); | |
| return scalePair(unscaled); | |
| } | |
| return {Base::Vector3d(), Base::Vector3d()}; | |
| } | |
| //! extract the boundary of the break lines from a sketch (3d coords) and map to this | |
| //! broken view. used in making break lines. | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::breakBoundsFromSketch(const App::DocumentObject& breakObj) const | |
| { | |
| std::pair<Base::Vector3d, Base::Vector3d> breakPoints = breakPointsFromObj(breakObj); | |
| Base::Vector3d center = (breakPoints.first + breakPoints.second) / 2; | |
| Base::Vector3d breakDir = directionFromObj(breakObj); | |
| breakDir.Normalize(); | |
| Base::Vector3d perpDir = makePerpendicular(breakDir); | |
| perpDir.Normalize(); | |
| // get the midpoint of the zigzags | |
| Base::Vector3d ptOnLine0 = center + breakDir * removedLengthFromObj(breakObj) / 2; | |
| Base::Vector3d ptOnLine1 = center - breakDir * removedLengthFromObj(breakObj) / 2; | |
| double lineLength = breaklineLength(breakObj); | |
| Base::Vector3d corner0 = ptOnLine0 - perpDir * lineLength / 2; | |
| Base::Vector3d corner1 = ptOnLine1 + perpDir * lineLength / 2; | |
| corner0 = mapPoint3dToView(corner0); | |
| corner1 = mapPoint3dToView(corner1); | |
| // these are unscaled, unrotated points | |
| return{corner0, corner1}; | |
| } | |
| //! extract the boundary of the break lines from an edge | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::breakBoundsFromEdge(const App::DocumentObject& breakObj) const | |
| { | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (locShape.IsNull() || locShape.ShapeType() != TopAbs_EDGE) { | |
| return {Base::Vector3d(), Base::Vector3d()}; | |
| } | |
| auto edge = projectEdge(TopoDS::Edge(locShape)); | |
| auto start = edge->getStartPoint(); | |
| auto end = edge->getEndPoint(); | |
| Base::Vector3d direction = end - start; | |
| double length = direction.Length(); | |
| direction.Normalize(); | |
| Base::Vector3d stdX{1.0, 0.0, 0.0}; | |
| Base::Vector3d stdY{0.0, 1.0, 0.0}; | |
| if (DU::fpCompare(fabs(direction.Dot(stdX)), 1.0, EWTOLERANCE) ) { | |
| double left = std::min(start.x, end.x); | |
| double right = std::max(start.x, end.x); | |
| // not wild about this for top/bottom | |
| double top = start.y + length; | |
| double bottom = start.y - length; | |
| Base::Vector3d topLeft{left, top, 0.0}; | |
| Base::Vector3d bottomRight{right, bottom, 0.0}; | |
| return{topLeft, bottomRight}; | |
| } | |
| if (!DU::fpCompare(fabs(direction.Dot(stdY)), 1.0, EWTOLERANCE) ) { | |
| Base::Console().message("DBV::breakBoundsFromEdge - direction is neither X nor Y\n"); | |
| // TODO: throw? return nonsense? | |
| } | |
| double left = start.x - length; | |
| double right = start.x + length; | |
| double bottom = std::min(start.y, end.y); | |
| double top = std::max(start.y, end.y); | |
| Base::Vector3d topLeft{left, top, 0.0}; | |
| Base::Vector3d bottomRight{right, bottom, 0.0}; | |
| return{topLeft, bottomRight}; | |
| } | |
| //! calculate the unscaled length of the breakline | |
| double DrawBrokenView::breaklineLength(const App::DocumentObject& breakObj) const | |
| { | |
| if (ShapeExtractor::isSketchObject(&breakObj)) { | |
| return breaklineLengthFromSketch(breakObj); | |
| } | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (!locShape.IsNull() && locShape.ShapeType() == TopAbs_EDGE) { | |
| return breaklineLengthFromEdge(breakObj); | |
| } | |
| return 0.0; | |
| } | |
| //! calculate the length of the breakline for a sketch based break | |
| double DrawBrokenView::breaklineLengthFromSketch(const App::DocumentObject& breakObj) const | |
| { | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (locShape.IsNull()) { | |
| return 0; | |
| } | |
| // get the edges from the sketch | |
| std::vector<TopoDS_Edge> sketchEdges; | |
| TopExp_Explorer expl(locShape, TopAbs_EDGE); | |
| for (; expl.More(); expl.Next()) { | |
| sketchEdges.push_back(TopoDS::Edge(expl.Current())); | |
| } | |
| if (sketchEdges.size() < 2) { | |
| // need 2 edges | |
| Base::Console().message("DBV::breaklineLengthFromSketch - not enough edges\n"); | |
| } | |
| std::pair<Base::Vector3d, Base::Vector3d> ends0 = SU::getEdgeEnds(sketchEdges.front()); | |
| ends0.first = projectPoint(ends0.first, false); | |
| ends0.second = projectPoint(ends0.second, false); | |
| std::pair<Base::Vector3d, Base::Vector3d> ends1 = SU::getEdgeEnds(sketchEdges.back()); | |
| ends1.first = projectPoint(ends1.first, false); | |
| ends1.second = projectPoint(ends1.second, false); | |
| if (isVertical(ends0, true)) { | |
| // sketch line is vertical, so breakline is also vertical | |
| double yLow = std::min({ends0.first.y, ends0.second.y, ends1.first.y, ends1.second.y}); | |
| double yHigh = std::max({ends0.first.y, ends0.second.y, ends1.first.y, ends1.second.y}); | |
| return yHigh - yLow; | |
| } | |
| // sketch line is horizontal, so breakline is also horizontal | |
| double xLow = std::min({ends0.first.x, ends0.second.x, ends1.first.x, ends1.second.x}); | |
| double xHigh = std::max({ends0.first.x, ends0.second.x, ends1.first.x, ends1.second.x}); | |
| return xHigh - xLow; | |
| } | |
| //! calculate the length of the breakline for an edge based break | |
| double DrawBrokenView::breaklineLengthFromEdge(const App::DocumentObject& breakObj) const | |
| { | |
| TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); | |
| if (!locShape.IsNull() && locShape.ShapeType() != TopAbs_EDGE) { | |
| return 0.0; | |
| } | |
| // the breakline could be very long. do we need a max breakline length? | |
| auto edge = projectEdge(TopoDS::Edge(locShape)); | |
| auto start = edge->getStartPoint(); | |
| auto end = edge->getEndPoint(); | |
| return (end - start).Length(); | |
| } | |
| //! return true if the edge is vertical. | |
| bool DrawBrokenView::isVertical(const TopoDS_Edge& edge, const bool projected) const | |
| { | |
| Base::Vector3d stdY{0.0, 1.0, 0.0}; | |
| auto ends = SU::getEdgeEnds(edge); | |
| auto edgeDir = ends.second - ends.first; | |
| edgeDir.Normalize(); | |
| auto upDir = Base::convertTo<Base::Vector3d>(getProjectionCS().YDirection()); | |
| if (projected) { | |
| upDir = stdY; | |
| } | |
| upDir.Normalize(); // probably superfluous | |
| return (DU::fpCompare(std::fabs(upDir.Dot(edgeDir)), 1.0, EWTOLERANCE)); | |
| } | |
| //! return true if the input points are vertical | |
| bool DrawBrokenView::isVertical(std::pair<Base::Vector3d, Base::Vector3d> inPoints, bool projected) const | |
| { | |
| Base::Vector3d stdY{0.0, 1.0, 0.0}; | |
| auto pointDir = inPoints.second - inPoints.first; | |
| pointDir.Normalize(); | |
| auto upDir = Base::convertTo<Base::Vector3d>(getProjectionCS().YDirection()); | |
| if (projected) { | |
| upDir = stdY; | |
| } | |
| upDir.Normalize(); // probably superfluous | |
| return (DU::fpCompare(std::fabs(upDir.Dot(pointDir)), 1.0, EWTOLERANCE)); | |
| } | |
| //! return true if the edge is horizontal | |
| bool DrawBrokenView::isHorizontal(const TopoDS_Edge& edge, bool projected) const | |
| { | |
| Base::Vector3d stdX{1.0, 0.0, 0.0}; | |
| auto ends = SU::getEdgeEnds(edge); | |
| auto edgeDir = ends.second - ends.first; | |
| edgeDir.Normalize(); | |
| auto sideDir = Base::convertTo<Base::Vector3d>(getProjectionCS().XDirection()); | |
| if (projected) { | |
| sideDir = stdX; | |
| } | |
| sideDir.Normalize(); // probably superfluous | |
| return (DU::fpCompare(std::fabs(sideDir.Dot(edgeDir)), 1.0, EWTOLERANCE)); | |
| } | |
| //! removes break objects from a list of document objects and returns the rest of the objects. | |
| //! used by TechDrawGui::Command | |
| std::vector<App::DocumentObject*> DrawBrokenView::removeBreakObjects(std::vector<App::DocumentObject*> breaks, std::vector<App::DocumentObject*> shapes) | |
| { | |
| std::vector<App::DocumentObject*> result; | |
| for (auto& shapeObj : shapes) { | |
| bool found = false; | |
| for (auto& breakObj : breaks) { | |
| if (breakObj == shapeObj) { | |
| found = true; | |
| break; | |
| } | |
| } | |
| if (!found) { | |
| result.push_back(shapeObj); | |
| } | |
| } | |
| return result; | |
| } | |
| std::vector<TopoDS_Edge> DrawBrokenView::edgesFromCompound(const TopoDS_Shape& compound) | |
| { | |
| std::vector<TopoDS_Edge> edgesOut; | |
| TopExp_Explorer expl(compound, TopAbs_EDGE); | |
| for (; expl.More(); expl.Next()) { | |
| edgesOut.push_back(TopoDS::Edge(expl.Current())); | |
| } | |
| return edgesOut; | |
| } | |
| //! find the limits of each piece's bounding box in cardinal direction (if we support oblique projection directions, then the | |
| //! piece will have to be transformed to align with OXYZ cardinal axes as in DrawViewPart::getSizeAlongVector) | |
| PieceLimitList DrawBrokenView::getPieceLimits(const std::vector<TopoDS_Shape>& pieces, Base::Vector3d direction) | |
| { | |
| Base::Vector3d stdX{1.0, 0.0, 0.0}; | |
| Base::Vector3d stdY{0.0, 1.0, 0.0}; | |
| Base::Vector3d stdZ{0.0, 0.0, 1.0}; | |
| PieceLimitList limits; | |
| limits.reserve(pieces.size()); | |
| for (auto& item : pieces) { | |
| Bnd_Box pieceBox; | |
| pieceBox.SetGap(0.0); | |
| BRepBndLib::AddOptimal(item, pieceBox); | |
| double xMin = 0, xMax = 0, yMin = 0, yMax = 0, zMin = 0, zMax = 0; // NOLINT | |
| pieceBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); | |
| if (DU::fpCompare(std::fabs(direction.Dot(stdX)), 1.0, EWTOLERANCE)) { | |
| PieceLimitEntry newEntry; // NOLINT | |
| newEntry.highLimit = xMax; | |
| newEntry.lowLimit = xMin; | |
| limits.push_back(newEntry); | |
| } else if (DU::fpCompare(std::fabs(direction.Dot(stdY)), 1.0, EWTOLERANCE)) { | |
| PieceLimitEntry newEntry; // NOLINT | |
| newEntry.highLimit = yMax; | |
| newEntry.lowLimit = yMin; | |
| limits.push_back(newEntry); | |
| } else { | |
| PieceLimitEntry newEntry; // NOLINT | |
| newEntry.highLimit = zMax; | |
| newEntry.lowLimit = zMin; | |
| limits.push_back(newEntry); | |
| } | |
| } | |
| return limits; | |
| } | |
| //! get the pieces of the broken shape. | |
| std::vector<TopoDS_Shape> DrawBrokenView::getPieces(const TopoDS_Shape& brokenShape) | |
| { | |
| std::vector<TopoDS_Shape> result = getPiecesByType(brokenShape, TopAbs_SOLID); | |
| std::vector<TopoDS_Shape> temp = getPiecesByType(brokenShape, TopAbs_SHELL, TopAbs_SOLID); | |
| result.insert(result.end(), temp.begin(), temp.end()); | |
| temp = getPiecesByType(brokenShape, TopAbs_FACE, TopAbs_SHELL); | |
| result.insert(result.end(), temp.begin(), temp.end()); | |
| temp = getPiecesByType(brokenShape, TopAbs_WIRE, TopAbs_FACE); | |
| result.insert(result.end(), temp.begin(), temp.end()); | |
| temp = getPiecesByType(brokenShape, TopAbs_EDGE, TopAbs_WIRE); | |
| result.insert(result.end(), temp.begin(), temp.end()); | |
| return result; | |
| } | |
| //! retrieve the subelements of a shape that are of type desiredShapeType, but that do not | |
| //! belong to a shape of type avoidShapeType. | |
| std::vector<TopoDS_Shape> DrawBrokenView::getPiecesByType(const TopoDS_Shape& shapeToSearch, | |
| TopAbs_ShapeEnum desiredShapeType, | |
| TopAbs_ShapeEnum avoidShapeType) | |
| { | |
| std::vector<TopoDS_Shape> result; | |
| TopExp_Explorer expl(shapeToSearch, desiredShapeType, avoidShapeType); | |
| for (; expl.More(); expl.Next()) { | |
| result.push_back(expl.Current()); | |
| } | |
| return result; | |
| } | |
| //! sort the breaks that match direction by their minimum limit | |
| BreakList DrawBrokenView::makeSortedBreakList(const std::vector<App::DocumentObject*>& breaks, | |
| const Base::Vector3d& direction, | |
| const bool descend) const | |
| { | |
| Base::Vector3d stdX{1.0, 0.0, 0.0}; | |
| Base::Vector3d stdY{0.0, 1.0, 0.0}; | |
| Base::Vector3d stdZ{0.0, 0.0, 1.0}; | |
| BreakList unsorted; | |
| for (auto& breakObj : breaks) { | |
| auto breakDirection = directionFromObj(*breakObj); | |
| if (DU::fpCompare(std::fabs(direction.Dot(breakDirection)), 1.0, EWTOLERANCE)) { | |
| // this break interests us | |
| BreakListEntry newEntry; // NOLINT | |
| newEntry.breakObj = breakObj; | |
| auto breakPoints = breakPointsFromObj(*breakObj); | |
| if (DU::fpCompare(std::fabs(direction.Dot(stdX)), 1.0, EWTOLERANCE )) { | |
| newEntry.lowLimit = std::min(breakPoints.first.x, breakPoints.second.x); | |
| newEntry.highLimit = std::max(breakPoints.first.x, breakPoints.second.x); | |
| } else if (DU::fpCompare(std::fabs(direction.Dot(stdY)), 1.0, EWTOLERANCE )) { | |
| newEntry.lowLimit = std::min(breakPoints.first.y, breakPoints.second.y); | |
| newEntry.highLimit = std::max(breakPoints.first.y, breakPoints.second.y); | |
| } else { | |
| // must be Z! | |
| newEntry.lowLimit = std::min(breakPoints.first.z, breakPoints.second.z); | |
| newEntry.highLimit = std::max(breakPoints.first.z, breakPoints.second.z); | |
| } | |
| newEntry.netRemoved = removedLengthFromObj(*breakObj) - Gap.getValue(); | |
| unsorted.push_back(newEntry); | |
| } | |
| } | |
| BreakList sorted = sortBreaks(unsorted, descend); | |
| return sorted; | |
| } | |
| //! find the compressed location of the breaks, and sort the result by lower limit | |
| BreakList DrawBrokenView::makeSortedBreakListCompressed(const std::vector<App::DocumentObject*>& breaks, | |
| const Base::Vector3d& moveDirection, | |
| const bool descend) const | |
| { | |
| // sortedBreaks is in lowLimit order | |
| auto sortedBreaks = makeSortedBreakList(breaks, moveDirection, descend); | |
| BreakList result; | |
| size_t iBreak{0}; | |
| for (auto& breakObj : sortedBreaks) { | |
| BreakListEntry newEntry; // NOLINT | |
| double breakSum{0}; | |
| if (isDirectionReversed(moveDirection)) { | |
| // reversed X+ high low X- | |
| // our list is sorted ascending by low limit - each is further left than the previous | |
| // so we must reverse our list to get the correct shift values. | |
| std::reverse(sortedBreaks.begin(), sortedBreaks.end()); | |
| for (size_t iSum = iBreak + 1; iSum < sortedBreaks.size(); iSum++) { | |
| // shift right by the removed amount of all the breaks to the right of this break | |
| breakSum += sortedBreaks.at(iSum).netRemoved; | |
| } | |
| newEntry.breakObj = breakObj.breakObj; | |
| newEntry.lowLimit = breakObj.lowLimit - breakSum; // move to right | |
| newEntry.highLimit = newEntry.lowLimit + Gap.getValue(); | |
| newEntry.netRemoved = breakObj.netRemoved; | |
| result.push_back(newEntry); | |
| } else { | |
| // forward X- low high X+ | |
| for (size_t iSum = iBreak + 1; iSum < sortedBreaks.size(); iSum++) { | |
| // shift right by the removed amount of all the breaks to the right of this break | |
| breakSum += sortedBreaks.at(iSum).netRemoved; | |
| } | |
| newEntry.breakObj = breakObj.breakObj; | |
| newEntry.lowLimit = breakObj.lowLimit + breakObj.netRemoved + breakSum; | |
| newEntry.highLimit = newEntry.lowLimit + Gap.getValue(); | |
| newEntry.netRemoved = breakObj.netRemoved; | |
| result.push_back(newEntry); | |
| } | |
| iBreak++; | |
| } | |
| if (isDirectionReversed(moveDirection)) { | |
| std::reverse(sortedBreaks.begin(), sortedBreaks.end()); | |
| } | |
| return result; | |
| } | |
| BreakList DrawBrokenView::sortBreaks(BreakList& inList, bool descend) | |
| { | |
| BreakList sorted = inList; | |
| std::sort(sorted.begin(), sorted.end(), DrawBrokenView::breakLess); | |
| if (descend) { | |
| std::reverse(sorted.begin(), sorted.end()); | |
| } | |
| return sorted; | |
| } | |
| //! return true if entry0 "is less than" entry | |
| /*static*/bool DrawBrokenView::breakLess(const BreakListEntry& entry0, const BreakListEntry& entry1) | |
| { | |
| return (entry0.lowLimit < entry1.lowLimit); | |
| } | |
| //! transform a 3d point into its position within the broken view. used in creating | |
| //! dimensions. | |
| Base::Vector3d DrawBrokenView::mapPoint3dToView(Base::Vector3d point3d) const | |
| { | |
| Base::Vector3d result{point3d}; | |
| auto breaksAll = Breaks.getValues(); | |
| bool descend = false; | |
| auto moveXDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().XDirection())); | |
| // get the breaks that move us in X | |
| auto sortedXBreaks = makeSortedBreakList(breaksAll, moveXDirection, descend); | |
| double xLimit = DU::coordinateForDirection(point3d, moveXDirection); | |
| double xShift = shiftAmountShrink(xLimit, moveXDirection, sortedXBreaks); | |
| Base::Vector3d xMove = moveXDirection * xShift; // move to the right (+X) | |
| auto moveYDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().YDirection())); | |
| descend = false; | |
| // get the breaks that move us in Y | |
| auto sortedYBreaks = makeSortedBreakList(breaksAll, moveYDirection, descend); | |
| double yLimit = DU::coordinateForDirection(point3d, moveYDirection); | |
| double yShift = shiftAmountShrink(yLimit, moveYDirection, sortedYBreaks); | |
| Base::Vector3d yMove = moveYDirection * yShift; // move up (+Y) | |
| auto moved3d = point3d + xMove + yMove; | |
| result = moved3d - getCompressedCentroid(); | |
| result = projectPoint(result, false); | |
| return result; | |
| } | |
| //! transform a 2d point in the broken view into the equivalent point on the XY | |
| //! paper plane. used in creating dimensions from points on the broken view. | |
| Base::Vector3d DrawBrokenView::mapPoint2dFromView(Base::Vector3d point2d) const | |
| { | |
| Base::Vector3d stdX(1.0, 0.0, 0.0); | |
| Base::Vector3d stdY(0.0, 1.0, 0.0); | |
| // make pseudo 3d point from point2d | |
| gp_Ax3 OXYZ; | |
| gp_Ax3 projCS3(getProjectionCS(getCompressedCentroid())); | |
| gp_Trsf xTo3d; | |
| xTo3d.SetTransformation(projCS3, OXYZ); | |
| auto pseudo3d = Base::convertTo<Base::Vector3d>(Base::convertTo<gp_Pnt>(point2d).Transformed(xTo3d)); | |
| // now shift down and left | |
| auto breaksAll = Breaks.getValues(); | |
| auto moveXDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().XDirection())); | |
| // we are expanding, so the direction should be to the "left"/"down" which is the opposite of | |
| // our XDirection | |
| auto moveXReverser = isDirectionReversed(moveXDirection) ? 1.0 : -1.0; | |
| bool descend = false; // should be false so we move from lowest break to highest? | |
| auto sortedXBreaks = makeSortedBreakList(breaksAll, moveXDirection, descend); | |
| double xLimit = DU::coordinateForDirection(pseudo3d, moveXDirection); | |
| std::vector<size_t> fullGaps; | |
| int partialGapIndex{-1}; | |
| auto compressedXBreaks = makeSortedBreakListCompressed(breaksAll, moveXDirection, descend); | |
| double partialGapPenetration = getExpandGaps(xLimit, compressedXBreaks, moveXDirection, fullGaps, partialGapIndex); | |
| double breakSum{0}; | |
| for (auto& index : fullGaps) { | |
| double breakSize = sortedXBreaks.at(index).netRemoved; | |
| breakSum += breakSize; | |
| } | |
| if (partialGapIndex >= 0) { | |
| double breakSize = sortedXBreaks.at(partialGapIndex).netRemoved; | |
| breakSum += breakSize * partialGapPenetration; | |
| } | |
| double xCoord2 = xLimit + breakSum * moveXReverser; | |
| auto moveYDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().YDirection())); | |
| auto moveYReverser = isDirectionReversed(moveYDirection) ? 1.0 : -1.0; | |
| descend = false; | |
| auto sortedYBreaks = makeSortedBreakList(breaksAll, moveYDirection, descend); | |
| double yLimit = DU::coordinateForDirection(pseudo3d, moveYDirection); | |
| fullGaps.clear(); | |
| partialGapIndex = -1; | |
| auto compressedYBreaks = makeSortedBreakListCompressed(breaksAll, moveYDirection, descend); | |
| partialGapPenetration = getExpandGaps(yLimit, compressedYBreaks, moveYDirection, fullGaps, partialGapIndex); | |
| breakSum = 0; | |
| for (auto& index : fullGaps) { | |
| double breakSize = sortedYBreaks.at(index).netRemoved; | |
| breakSum += breakSize; | |
| } | |
| if (partialGapIndex >= 0) { | |
| double breakSize = sortedYBreaks.at(partialGapIndex).netRemoved; | |
| breakSum += breakSize * partialGapPenetration; | |
| } | |
| double yCoord2 = yLimit + breakSum * moveYReverser; | |
| Base::Vector3d movedResult{xCoord2, yCoord2, 0.0}; | |
| return movedResult; | |
| } | |
| //! returns the amount a coordinate needs to move to reflect the effect of the breaks to the right/above | |
| //! the input. used in mapping points to the broken view. | |
| double DrawBrokenView::shiftAmountShrink(double pointCoord, Base::Vector3d direction, const BreakList& sortedBreaks) const | |
| { | |
| double shift{0}; | |
| for (auto& breakItem : sortedBreaks) { | |
| if (isDirectionReversed(direction)) { | |
| if (pointCoord <= breakItem.lowLimit) { | |
| // h--------l -ve | |
| // p | |
| // point is right/above break | |
| // leave alone, this break doesn't affect us | |
| continue; | |
| } | |
| if (pointCoord > breakItem.highLimit || | |
| DU::fpCompare(pointCoord, breakItem.highLimit, EWTOLERANCE) ) { | |
| // h--------l -ve | |
| // p | |
| // point is left/below break | |
| shift += breakItem.netRemoved; | |
| continue; | |
| } | |
| // h--------l -ve | |
| // p | |
| // g-g | |
| // p' | |
| // break.start < value < break.end - point is in the break area | |
| // we move our point by a fraction of the Gap length | |
| double penetration = fabs(pointCoord - breakItem.highLimit); // (p - h) start(high) to point distance | |
| double removed = removedLengthFromObj(*breakItem.breakObj); // (h-l) full break size | |
| double factor = 1 - (penetration / removed); // fraction of break to right | |
| double toShift = pointCoord - (breakItem.lowLimit - factor * Gap.getValue()); | |
| shift += fabs(toShift); | |
| } else { | |
| if (pointCoord >= breakItem.highLimit) { | |
| // l--------h +ve | |
| // p | |
| // leave alone, this break doesn't affect us | |
| continue; | |
| } | |
| if (pointCoord < breakItem.lowLimit || | |
| DU::fpCompare(pointCoord, breakItem.lowLimit, EWTOLERANCE) ) { | |
| // l--------h +ve | |
| // p | |
| // move right/up by the removed area less the gap | |
| shift += removedLengthFromObj(*breakItem.breakObj) - Gap.getValue(); | |
| continue; | |
| } | |
| // l--------h +ve | |
| // p | |
| // g--g | |
| // p' | |
| // break.start < value < break.end - point is in the break area | |
| // we move our point by a fraction of the Gap length | |
| double penetration = fabs(pointCoord - breakItem.lowLimit); | |
| double removed = removedLengthFromObj(*breakItem.breakObj); | |
| double factor = 1 - (penetration / removed); | |
| double netRemoved = pointCoord - breakItem.highLimit - factor * Gap.getValue(); | |
| shift += fabs(netRemoved); | |
| } | |
| } | |
| return shift; | |
| } | |
| //! determine which gaps require pointCoord to move by a full gap and if there is a partial gap that must | |
| //! be included in the move operation. If there is a partial gap, the penetration factor is returned. | |
| //! penetration is measure right to left in the view. | |
| double DrawBrokenView::getExpandGaps (double pointCoord, | |
| const BreakList& compressedBreakList, | |
| Base::Vector3d moveDirection, | |
| std::vector<size_t>& fullGaps, | |
| int& partialGapIndex) const | |
| { | |
| double partialPenetrationFactor{0}; | |
| // check pointCoord against compressed gaps | |
| size_t iBreak{0}; | |
| for (auto& gap : compressedBreakList) { | |
| if (isDirectionReversed(moveDirection)) { | |
| // reversed X+ is to the left | |
| if (pointCoord < gap.lowLimit) { | |
| // not interested | |
| iBreak++; | |
| continue; | |
| } | |
| if (pointCoord > gap.highLimit || | |
| DU::fpCompare(pointCoord, gap.highLimit, EWTOLERANCE) ) { | |
| // need to move by full length of associated break | |
| fullGaps.push_back(iBreak); | |
| iBreak++; | |
| continue; | |
| } | |
| // pointCoord is in gap | |
| // X+ high > pointCoord > low X- | |
| partialGapIndex = static_cast<int>(iBreak); | |
| partialPenetrationFactor = (pointCoord - gap.lowLimit) / Gap.getValue(); | |
| iBreak++; | |
| } else { | |
| // forward +X is to the right | |
| if (pointCoord > gap.highLimit) { | |
| // not interested | |
| iBreak++; | |
| continue; | |
| } | |
| if (pointCoord < gap.lowLimit || | |
| DU::fpCompare(pointCoord, gap.lowLimit, EWTOLERANCE) ) { | |
| // need to move by full length of associated break | |
| fullGaps.push_back(iBreak); | |
| iBreak++; | |
| continue; | |
| } | |
| // pointCoord is in gap | |
| // low < pointCoord < highLimit X+ | |
| partialGapIndex = static_cast<int>(iBreak); | |
| partialPenetrationFactor = (gap.highLimit - pointCoord) / Gap.getValue(); | |
| iBreak++; | |
| } | |
| } | |
| return partialPenetrationFactor; | |
| } | |
| Base::Vector3d DrawBrokenView::getCompressedCentroid() const | |
| { | |
| if (m_compressedShape.IsNull()) { | |
| return Base::Vector3d(0.0, 0.0, 0.0); | |
| } | |
| gp_Ax2 cs = getProjectionCS(); | |
| gp_Pnt gCenter = ShapeUtils::findCentroid(m_compressedShape, cs); | |
| return Base::convertTo<Base::Vector3d>(gCenter); | |
| } | |
| //! construct a perpendicular direction in the projection CS | |
| Base::Vector3d DrawBrokenView::makePerpendicular(Base::Vector3d inDir) const | |
| { | |
| auto gDir = Base::convertTo<gp_Dir>(inDir); | |
| gp_Pnt origin(0.0, 0.0, 0.0); | |
| auto dir = getProjectionCS().Direction(); | |
| gp_Ax1 axis(origin, dir); | |
| auto gRotated = gDir.Rotated(axis, std::numbers::pi/2); | |
| return Base::convertTo<Base::Vector3d>(gRotated); | |
| } | |
| //! true if this piece should be moved | |
| bool DrawBrokenView::moveThisPiece(PieceLimitEntry piece, | |
| BreakListEntry breakItem, | |
| Base::Vector3d moveDirection) const | |
| { | |
| if (isDirectionReversed(moveDirection)) { | |
| // -ve direction | |
| if (piece.lowLimit > breakItem.highLimit || | |
| DU::fpCompare(piece.lowLimit, breakItem.highLimit, EWTOLERANCE) ) { | |
| return true; | |
| } | |
| } else { | |
| // +ve direction | |
| if (piece.highLimit < breakItem.lowLimit || | |
| DU::fpCompare(piece.highLimit, breakItem.lowLimit, EWTOLERANCE) ) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| //! true if direction is the reversed of a cardinal direction. (1.0, 0.0, 0.0) | |
| //! returns false, (-1.0, 0.0, 0.0) returns true; | |
| bool DrawBrokenView::isDirectionReversed(Base::Vector3d direction) const | |
| { | |
| Base::Vector3d stdX{1.0, 0.0, 0.0}; | |
| Base::Vector3d stdY{0.0, 1.0, 0.0}; | |
| Base::Vector3d stdZ{0.0, 0.0, 1.0}; | |
| if (DU::fpCompare(std::fabs(direction.Dot(stdX)), 1.0, EWTOLERANCE)) { | |
| return DU::fpCompare(direction.Dot(stdX), -1.0, EWTOLERANCE); | |
| } | |
| if (DU::fpCompare(std::fabs(direction.Dot(stdY)), 1.0, EWTOLERANCE)) { | |
| return DU::fpCompare(direction.Dot(stdY), -1.0, EWTOLERANCE); | |
| } | |
| if (DU::fpCompare(std::fabs(direction.Dot(stdZ)), 1.0, EWTOLERANCE)) { | |
| return DU::fpCompare(direction.Dot(stdZ), -1.0, EWTOLERANCE); | |
| } | |
| return false; | |
| } | |
| void DrawBrokenView::printBreakList(const std::string& text, const BreakList& inBreaks) const | |
| { | |
| Base::Console().message("DBV - %s\n", text.c_str()); | |
| for (auto& entry : inBreaks) { | |
| Base::Console().message(" > label: %s > low: %.3f > high: %.3f > net: %.3f\n", entry.breakObj->Label.getValue(), | |
| entry.lowLimit, entry.highLimit, entry.netRemoved); | |
| } | |
| } | |
| std::pair<Base::Vector3d, Base::Vector3d> DrawBrokenView::scalePair(std::pair<Base::Vector3d, Base::Vector3d> inPair) const | |
| { | |
| std::pair<Base::Vector3d, Base::Vector3d> result; | |
| result.first = inPair.first * getScale(); | |
| result.second = inPair.second * getScale(); | |
| return result; | |
| } | |
| PyObject *DrawBrokenView::getPyObject() | |
| { | |
| if (PythonObject.is(Py::_None())) { | |
| // ref counter is set to 1 | |
| PythonObject = Py::Object(new DrawBrokenViewPy(this),true); | |
| } | |
| return Py::new_reference_to(PythonObject); | |
| } | |
| namespace App | |
| { | |
| /// @cond DOXERR | |
| PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawBrokenViewPython, TechDraw::DrawBrokenView) | |
| template<> | |
| const char* TechDraw::DrawBrokenViewPython::getViewProviderName() const | |
| { | |
| return "TechDrawGui::ViewProviderViewPart"; | |
| } | |
| /// @endcond | |
| // explicit template instantiation | |
| template class TechDrawExport FeaturePythonT<TechDraw::DrawBrokenView>; | |
| }// namespace App | |