| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| |
|
| | #include <BRepAdaptor_Curve.hxx> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Cut.h> |
| | #include <BRepBndLib.hxx> |
| | #include <BRepBuilderAPI_Copy.hxx> |
| | #include <BRepBuilderAPI_MakeFace.hxx> |
| | #include <BRepBuilderAPI_Transform.hxx> |
| | #include <BRepPrimAPI_MakePrism.hxx> |
| | #include <BRepTools.hxx> |
| | #include <BRep_Builder.hxx> |
| | #include <Bnd_Box.hxx> |
| | #include <GCPnts_AbscissaPoint.hxx> |
| | #include <QtConcurrentRun> |
| | #include <ShapeAnalysis.hxx> |
| | #include <ShapeFix_Shape.hxx> |
| | #include <TopExp.hxx> |
| | #include <TopExp_Explorer.hxx> |
| | #include <TopoDS.hxx> |
| | #include <TopoDS_Compound.hxx> |
| | #include <TopoDS_Edge.hxx> |
| | #include <TopoDS_Face.hxx> |
| | #include <TopoDS_Shape.hxx> |
| | #include <gp_Ax2.hxx> |
| | #include <gp_Ax3.hxx> |
| | #include <gp_Dir.hxx> |
| | #include <gp_Pln.hxx> |
| | #include <gp_Pnt.hxx> |
| |
|
| | #include <App/Document.h> |
| | #include <Base/BoundBox.h> |
| | #include <Base/Console.h> |
| | #include <Base/Converter.h> |
| | #include <Base/FileInfo.h> |
| | #include <Base/Parameter.h> |
| |
|
| | #include <Mod/Part/App/PartFeature.h> |
| |
|
| | #include "DrawGeomHatch.h" |
| | #include "DrawHatch.h" |
| | #include "DrawUtil.h" |
| | #include "DrawViewDetail.h" |
| | #include "GeometryObject.h" |
| | #include "ShapeExtractor.h" |
| | #include "ShapeUtils.h" |
| |
|
| | #include "DrawBrokenView.h" |
| | #include "DrawBrokenViewPy.h" |
| |
|
| | using namespace TechDraw; |
| | using DU = DrawUtil; |
| | using SU = ShapeUtils; |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | 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()) { |
| | |
| | return DrawView::execute(); |
| | } |
| |
|
| | TopoDS_Shape shape = getSourceShape(); |
| | if (shape.IsNull()) { |
| | Base::Console().message("DBV::execute - %s - Source shape is Null.\n", getNameInDocument()); |
| | return DrawView::execute(); |
| | } |
| |
|
| | 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(); |
| | } |
| |
|
| |
|
| | |
| | |
| | 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; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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(); |
| |
|
| | |
| | |
| | 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(); |
| | |
| | |
| | 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; |
| | } |
| |
|
| | |
| | TopoDS_Shape DrawBrokenView::compressShape(const TopoDS_Shape& shapeToCompress) const |
| | { |
| | TopoDS_Shape compressed = compressHorizontal(shapeToCompress); |
| | return compressVertical(compressed); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | 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 (auto& breakItem : sortedBreaks) { |
| | |
| | Base::Vector3d netBreakDisplace = |
| | moveDirection * (removedLengthFromObj(*breakItem.breakObj) - Gap.getValue()); |
| | size_t iPiece{0}; |
| | for (auto& pieceLimit : limits) { |
| | |
| | if (moveThisPiece(pieceLimit, breakItem, moveDirection)) { |
| | |
| | TopoDS_Shape temp = ShapeUtils::moveShape(pieces.at(iPiece), netBreakDisplace); |
| | pieces.at(iPiece) = temp; |
| | } |
| |
|
| | iPiece++; |
| | } |
| | } |
| | |
| | BRep_Builder builder; |
| | TopoDS_Compound result; |
| | builder.MakeCompound(result); |
| | for (auto& pieceShape : pieces) { |
| | builder.Add(result, pieceShape); |
| | } |
| |
|
| | return result; |
| | } |
| |
|
| | |
| | |
| | TopoDS_Shape DrawBrokenView::compressVertical(const TopoDS_Shape& shapeToCompress)const |
| | { |
| | auto pieces = getPieces(shapeToCompress); |
| |
|
| | auto breaksAll = Breaks.getValues(); |
| | |
| | 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 (auto& breakItem : sortedBreaks) { |
| | |
| | Base::Vector3d netBreakDisplace = |
| | moveDirection * (removedLengthFromObj(*breakItem.breakObj) - Gap.getValue()); |
| | size_t iPiece{0}; |
| | for (auto& pieceLimit : limits) { |
| | |
| | if (moveThisPiece(pieceLimit, breakItem, moveDirection)) { |
| | |
| | TopoDS_Shape temp = ShapeUtils::moveShape(pieces.at(iPiece), netBreakDisplace); |
| | pieces.at(iPiece) = temp; |
| | } |
| | iPiece++; |
| | } |
| | } |
| | |
| | BRep_Builder builder; |
| | TopoDS_Compound result; |
| | builder.MakeCompound(result); |
| | for (auto& pieceShape : pieces) { |
| | builder.Add(result, pieceShape); |
| | } |
| |
|
| | return result; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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(); |
| | } |
| |
|
| |
|
| | |
| | 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()}; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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(); |
| | |
| | return DU::closestBasis(direction); |
| | } |
| |
|
| |
|
| | |
| | Base::Vector3d DrawBrokenView::guiDirectionFromObj(const App::DocumentObject& breakObj) const |
| | { |
| | return projectPoint(directionFromObj(breakObj)); |
| | } |
| |
|
| | |
| | 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(); |
| | } |
| |
|
| | |
| | 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); |
| | } |
| |
|
| | |
| | |
| | |
| | bool DrawBrokenView::isBreakObjectSketch(const App::DocumentObject& breakObj) |
| | { |
| | TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); |
| | if (locShape.IsNull()) { |
| | return false; |
| | } |
| |
|
| | |
| | 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) { |
| | Base::Console().message("DBV::isBreakObjectSketch - wrong number of edges\n"); |
| | return false; |
| | } |
| | |
| | TopoDS_Edge first = sketchEdges.front(); |
| | TopoDS_Edge last = sketchEdges.back(); |
| | return SU::edgesAreParallel(first, last); |
| | } |
| |
|
| | |
| | |
| | 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()};; |
| | } |
| |
|
| | |
| | |
| | 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) { |
| | return {Base::Vector3d(), Base::Vector3d()}; |
| | } |
| |
|
| | |
| | TopoDS_Edge first = sketchEdges.front(); |
| | TopoDS_Edge last = sketchEdges.back(); |
| | if ((isVertical(first) && isVertical(last)) || |
| | (isHorizontal(first) && isHorizontal(last))) { |
| | |
| | |
| | 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; |
| | 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(); |
| | |
| | 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()}; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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)}; |
| | } |
| |
|
| |
|
| | |
| | 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()}; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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(); |
| |
|
| | |
| | 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); |
| |
|
| | |
| | return{corner0, corner1}; |
| | } |
| |
|
| |
|
| | |
| | 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); |
| | |
| | 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"); |
| | |
| | } |
| |
|
| | 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}; |
| | } |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | double DrawBrokenView::breaklineLengthFromSketch(const App::DocumentObject& breakObj) const |
| | { |
| | TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); |
| | if (locShape.IsNull()) { |
| | return 0; |
| | } |
| | |
| | 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) { |
| | |
| | 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)) { |
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | double DrawBrokenView::breaklineLengthFromEdge(const App::DocumentObject& breakObj) const |
| | { |
| | TopoDS_Shape locShape = ShapeExtractor::getLocatedShape(&breakObj); |
| | if (!locShape.IsNull() && locShape.ShapeType() != TopAbs_EDGE) { |
| | return 0.0; |
| | } |
| | |
| | auto edge = projectEdge(TopoDS::Edge(locShape)); |
| | auto start = edge->getStartPoint(); |
| | auto end = edge->getEndPoint(); |
| | return (end - start).Length(); |
| | } |
| |
|
| | |
| | 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(); |
| |
|
| | return (DU::fpCompare(std::fabs(upDir.Dot(edgeDir)), 1.0, EWTOLERANCE)); |
| | } |
| |
|
| | |
| | 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(); |
| | return (DU::fpCompare(std::fabs(upDir.Dot(pointDir)), 1.0, EWTOLERANCE)); |
| | } |
| |
|
| | |
| | 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(); |
| |
|
| | return (DU::fpCompare(std::fabs(sideDir.Dot(edgeDir)), 1.0, EWTOLERANCE)); |
| | } |
| |
|
| | |
| | |
| | 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; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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; |
| | pieceBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); |
| | if (DU::fpCompare(std::fabs(direction.Dot(stdX)), 1.0, EWTOLERANCE)) { |
| | PieceLimitEntry newEntry; |
| | newEntry.highLimit = xMax; |
| | newEntry.lowLimit = xMin; |
| | limits.push_back(newEntry); |
| | } else if (DU::fpCompare(std::fabs(direction.Dot(stdY)), 1.0, EWTOLERANCE)) { |
| | PieceLimitEntry newEntry; |
| | newEntry.highLimit = yMax; |
| | newEntry.lowLimit = yMin; |
| | limits.push_back(newEntry); |
| | } else { |
| | PieceLimitEntry newEntry; |
| | newEntry.highLimit = zMax; |
| | newEntry.lowLimit = zMin; |
| | limits.push_back(newEntry); |
| | } |
| | } |
| |
|
| | return limits; |
| | } |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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)) { |
| | |
| | BreakListEntry newEntry; |
| | 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 { |
| | |
| | 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; |
| | } |
| |
|
| |
|
| | |
| | BreakList DrawBrokenView::makeSortedBreakListCompressed(const std::vector<App::DocumentObject*>& breaks, |
| | const Base::Vector3d& moveDirection, |
| | const bool descend) const |
| | { |
| | |
| | auto sortedBreaks = makeSortedBreakList(breaks, moveDirection, descend); |
| | BreakList result; |
| | size_t iBreak{0}; |
| | for (auto& breakObj : sortedBreaks) { |
| | BreakListEntry newEntry; |
| | double breakSum{0}; |
| | if (isDirectionReversed(moveDirection)) { |
| | |
| | |
| | |
| | std::reverse(sortedBreaks.begin(), sortedBreaks.end()); |
| | for (size_t iSum = iBreak + 1; iSum < sortedBreaks.size(); iSum++) { |
| | |
| | breakSum += sortedBreaks.at(iSum).netRemoved; |
| | } |
| | newEntry.breakObj = breakObj.breakObj; |
| | newEntry.lowLimit = breakObj.lowLimit - breakSum; |
| | newEntry.highLimit = newEntry.lowLimit + Gap.getValue(); |
| | newEntry.netRemoved = breakObj.netRemoved; |
| | result.push_back(newEntry); |
| | } else { |
| | |
| | for (size_t iSum = iBreak + 1; iSum < sortedBreaks.size(); iSum++) { |
| | |
| | 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; |
| | } |
| |
|
| |
|
| | |
| | bool DrawBrokenView::breakLess(const BreakListEntry& entry0, const BreakListEntry& entry1) |
| | { |
| | return (entry0.lowLimit < entry1.lowLimit); |
| | } |
| |
|
| |
|
| | |
| | |
| | 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())); |
| |
|
| | |
| | auto sortedXBreaks = makeSortedBreakList(breaksAll, moveXDirection, descend); |
| | double xLimit = DU::coordinateForDirection(point3d, moveXDirection); |
| |
|
| | double xShift = shiftAmountShrink(xLimit, moveXDirection, sortedXBreaks); |
| | Base::Vector3d xMove = moveXDirection * xShift; |
| |
|
| | auto moveYDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().YDirection())); |
| | descend = false; |
| | |
| | auto sortedYBreaks = makeSortedBreakList(breaksAll, moveYDirection, descend); |
| | double yLimit = DU::coordinateForDirection(point3d, moveYDirection); |
| | double yShift = shiftAmountShrink(yLimit, moveYDirection, sortedYBreaks); |
| | Base::Vector3d yMove = moveYDirection * yShift; |
| |
|
| | auto moved3d = point3d + xMove + yMove; |
| | result = moved3d - getCompressedCentroid(); |
| |
|
| | result = projectPoint(result, false); |
| | return result; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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); |
| |
|
| | |
| | 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)); |
| |
|
| | |
| | auto breaksAll = Breaks.getValues(); |
| |
|
| | auto moveXDirection = DU::closestBasisOriented(Base::convertTo<Base::Vector3d>(getProjectionCS().XDirection())); |
| | |
| | |
| | auto moveXReverser = isDirectionReversed(moveXDirection) ? 1.0 : -1.0; |
| | bool descend = false; |
| | 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; |
| | } |
| |
|
| |
|
| | |
| | |
| | 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) { |
| | |
| | |
| | |
| | |
| | continue; |
| | } |
| |
|
| | if (pointCoord > breakItem.highLimit || |
| | DU::fpCompare(pointCoord, breakItem.highLimit, EWTOLERANCE) ) { |
| | |
| | |
| | |
| | shift += breakItem.netRemoved; |
| | continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | double penetration = fabs(pointCoord - breakItem.highLimit); |
| | double removed = removedLengthFromObj(*breakItem.breakObj); |
| | double factor = 1 - (penetration / removed); |
| | double toShift = pointCoord - (breakItem.lowLimit - factor * Gap.getValue()); |
| | shift += fabs(toShift); |
| |
|
| |
|
| | } else { |
| | if (pointCoord >= breakItem.highLimit) { |
| | |
| | |
| | |
| | continue; |
| | } |
| |
|
| | if (pointCoord < breakItem.lowLimit || |
| | DU::fpCompare(pointCoord, breakItem.lowLimit, EWTOLERANCE) ) { |
| | |
| | |
| | |
| | shift += removedLengthFromObj(*breakItem.breakObj) - Gap.getValue(); |
| | continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | 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; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | double DrawBrokenView::getExpandGaps (double pointCoord, |
| | const BreakList& compressedBreakList, |
| | Base::Vector3d moveDirection, |
| | std::vector<size_t>& fullGaps, |
| | int& partialGapIndex) const |
| | { |
| | double partialPenetrationFactor{0}; |
| | |
| | size_t iBreak{0}; |
| | for (auto& gap : compressedBreakList) { |
| | if (isDirectionReversed(moveDirection)) { |
| | |
| | if (pointCoord < gap.lowLimit) { |
| | |
| | iBreak++; |
| | continue; |
| | } |
| | if (pointCoord > gap.highLimit || |
| | DU::fpCompare(pointCoord, gap.highLimit, EWTOLERANCE) ) { |
| | |
| | fullGaps.push_back(iBreak); |
| | iBreak++; |
| | continue; |
| | } |
| | |
| | |
| | partialGapIndex = static_cast<int>(iBreak); |
| | partialPenetrationFactor = (pointCoord - gap.lowLimit) / Gap.getValue(); |
| | iBreak++; |
| | } else { |
| | |
| | if (pointCoord > gap.highLimit) { |
| | |
| | iBreak++; |
| | continue; |
| | } |
| | if (pointCoord < gap.lowLimit || |
| | DU::fpCompare(pointCoord, gap.lowLimit, EWTOLERANCE) ) { |
| | |
| | fullGaps.push_back(iBreak); |
| | iBreak++; |
| | continue; |
| | } |
| | |
| | |
| | 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); |
| | } |
| |
|
| | |
| | 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); |
| | } |
| |
|
| | |
| | bool DrawBrokenView::moveThisPiece(PieceLimitEntry piece, |
| | BreakListEntry breakItem, |
| | Base::Vector3d moveDirection) const |
| | { |
| | if (isDirectionReversed(moveDirection)) { |
| | |
| | if (piece.lowLimit > breakItem.highLimit || |
| | DU::fpCompare(piece.lowLimit, breakItem.highLimit, EWTOLERANCE) ) { |
| | return true; |
| | } |
| | } else { |
| | |
| | if (piece.highLimit < breakItem.lowLimit || |
| | DU::fpCompare(piece.highLimit, breakItem.lowLimit, EWTOLERANCE) ) { |
| | return true; |
| | } |
| | } |
| | return false; |
| | } |
| |
|
| | |
| | |
| | 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())) { |
| | |
| | PythonObject = Py::Object(new DrawBrokenViewPy(this),true); |
| | } |
| | return Py::new_reference_to(PythonObject); |
| | } |
| |
|
| |
|
| |
|
| | namespace App |
| | { |
| | |
| | PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawBrokenViewPython, TechDraw::DrawBrokenView) |
| | template<> |
| | const char* TechDraw::DrawBrokenViewPython::getViewProviderName() const |
| | { |
| | return "TechDrawGui::ViewProviderViewPart"; |
| | } |
| | |
| |
|
| | |
| | template class TechDrawExport FeaturePythonT<TechDraw::DrawBrokenView>; |
| | } |
| |
|