/*************************************************************************** * Copyright (c) 2019 WandererFan * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ # include # include # include # include # include # include # include # include # include # include # include #include #include #include #include #include #include "DrawDimHelper.h" #include "Cosmetic.h" #include "DrawPage.h" #include "DrawUtil.h" #include "DrawViewDimension.h" #include "DrawViewDimExtent.h" #include "DrawViewPart.h" #include "Geometry.h" #include "GeometryObject.h" #define HORIZONTAL 0 #define VERTICAL 1 #define LENGTH 2 using namespace TechDraw; DrawViewDimension* DrawDimHelper::makeExtentDim(DrawViewPart* dvp, const std::string& dimType, ReferenceVector references2d) { std::vector edgeNames; for (auto& ref : references2d) { if (ref.getSubName().empty()) { continue; } std::string geomType = DrawUtil::getGeomTypeFromName(ref.getSubName()); if (geomType == "Edge") { edgeNames.push_back(ref.getSubName()); } } int direction = dimType == "DistanceX" ? 0 : dimType == "DistanceY" ? 1 : 2; return makeExtentDim(dvp, edgeNames, direction); } DrawViewDimension* DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector edgeNames, int direction) { // Base::Console().message("DDH::makeExtentDim() - dvp: %s edgeNames: %d\n", // dvp->Label.getValue(), edgeNames.size()); if (!dvp) { return nullptr; } std::string dimType = "DistanceX"; int dimNum = 0; if (direction == VERTICAL) { dimType = "DistanceY"; dimNum = 1; } else if (direction == LENGTH) { dimType = "Distance"; dimNum = 2; } DrawPage* page = dvp->findParentPage(); std::string pageName = page->getNameInDocument(); App::Document* doc = dvp->getDocument(); std::string dimName = doc->getUniqueObjectName("DimExtent"); Base::Interpreter().runStringArg( "App.activeDocument().addObject('TechDraw::DrawViewDimExtent', '%s')", dimName.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.translateLabel('DrawViewDimExtent', 'DimExtent', '%s')", dimName.c_str(), dimName.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.Type = '%s'", dimName.c_str(), dimType.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.DirExtent = %d", dimName.c_str(), dimNum); auto* dimExt = freecad_cast(doc->getObject(dimName.c_str())); if (!dimExt) { throw Base::TypeError("Dim extent not found"); } dimExt->Source.setValue(dvp, edgeNames); ReferenceVector newRefs; if (edgeNames.empty()) { ReferenceEntry emptyRef(dvp, std::string()); newRefs.push_back(emptyRef); } else { for (auto& edge : edgeNames) { ReferenceEntry ref(dvp, edge); newRefs.push_back(ref); } } dimExt->setReferences2d(newRefs); Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", pageName.c_str(), dimName.c_str()); dimExt->recomputeFeature(); return dimExt; } void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, const std::string& dimType, ReferenceVector references3d) { int direction = dimType == "DistanceX" ? 0 : dimType == "DistanceY" ? 1 : 2; return makeExtentDim3d(dvp, references3d, direction); } void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, ReferenceVector references, int direction) { // Base::Console().message("DDH::makeExtentDim3d() - dvp: %s references: %d\n", // dvp->Label.getValue(), references.size()); if (!dvp) { return; } std::string dimType = "DistanceX"; int dimNum = 0; if (direction == VERTICAL) { dimType = "DistanceY"; dimNum = 1; } DrawPage* page = dvp->findParentPage(); std::string pageName = page->getNameInDocument(); App::Document* doc = dvp->getDocument(); std::string dimName = doc->getUniqueObjectName("DimExtent"); Base::Interpreter().runStringArg( "App.activeDocument().addObject('TechDraw::DrawViewDimExtent', '%s')", dimName.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.translateLabel('DrawViewDimExtent', 'DimExtent', '%s')", dimName.c_str(), dimName.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.Type = '%s'", dimName.c_str(), dimType.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.DirExtent = %d", dimName.c_str(), dimNum); auto* dimExt = freecad_cast(doc->getObject(dimName.c_str())); if (!dimExt) { throw Base::TypeError("Dim extent not found"); } dimExt->Source.setValue(dvp); std::vector objs3d; std::vector subs3d; for (auto& ref : references) { objs3d.push_back(ref.getObject()); subs3d.push_back(ref.getSubName()); } dimExt->Source3d.setValues(objs3d, subs3d); ReferenceVector newRefs2d; ReferenceEntry emptyRef(dvp, std::string()); newRefs2d.push_back(emptyRef); dimExt->setReferences2d(newRefs2d); dimExt->setReferences3d(references); Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", pageName.c_str(), dimName.c_str()); dimExt->recomputeFeature(); } std::pair DrawDimHelper::minMax(DrawViewPart* dvp, std::vector edgeNames, int direction) { // Base::Console().message("DDH::minMax() - edgeName: %d\n", edgeNames.size()); std::pair result; Base::Vector3d refMin; Base::Vector3d refMax; gp_Ax3 projAx3;// OXYZ gp_Pln projPlane(projAx3); BaseGeomPtrVector edgeGeomList; if (!edgeNames.empty() && !edgeNames.front().empty()) { //we have edge names and the first one isn't null for (auto& n : edgeNames) { std::string geomType = DrawUtil::getGeomTypeFromName(n); if (geomType == "Edge") { int i = DrawUtil::getIndexFromName(n); BaseGeomPtr bg = dvp->getGeomByIndex(i); if (bg) { edgeGeomList.push_back(bg); } } } } else { for (auto& edge : dvp->getEdgeGeometry()) { if (!edge->getCosmetic()) { // skip cosmetic edges edgeGeomList.push_back(edge); } } } if (edgeGeomList.empty()) { return result; } Bnd_Box edgeBbx; edgeBbx.SetGap(1.0);//make the box a bit bigger std::vector inEdges; for (auto& bg : edgeGeomList) { inEdges.push_back(bg->getOCCEdge()); BRepBndLib::Add(bg->getOCCEdge(), edgeBbx); } double minX, minY, minZ, maxX, maxY, maxZ; edgeBbx.Get(minX, minY, minZ, maxX, maxY, maxZ); double xMid = (maxX + minX) / 2.0; double yMid = (maxY + minY) / 2.0; gp_Pnt rightMid(maxX, yMid, 0.0); gp_Pnt leftMid(minX, yMid, 0.0); gp_Pnt topMid(xMid, maxY, 0.0); gp_Pnt bottomMid(xMid, minY, 0.0); gp_Dir xDir(1.0, 0.0, 0.0); gp_Dir yDir(0.0, 1.0, 0.0); if (direction == HORIZONTAL) { Handle(Geom_Line) lineLeft = new Geom_Line(leftMid, yDir); BRepBuilderAPI_MakeEdge mkEdgeLeft(lineLeft); TopoDS_Edge edgeLeft = mkEdgeLeft.Edge(); gp_Pnt leftPoint = findClosestPoint(inEdges, edgeLeft); Handle(Geom_Line) lineRight = new Geom_Line(rightMid, yDir); BRepBuilderAPI_MakeEdge mkEdgeRight(lineRight); TopoDS_Edge edgeRight = mkEdgeRight.Edge(); gp_Pnt rightPoint = findClosestPoint(inEdges, edgeRight); refMin = Base::Vector3d(leftPoint.X(), leftPoint.Y(), 0.0); refMax = Base::Vector3d(rightPoint.X(), rightPoint.Y(), 0.0); } else if (direction == VERTICAL) { Handle(Geom_Line) lineBottom = new Geom_Line(bottomMid, xDir); BRepBuilderAPI_MakeEdge mkEdgeBottom(lineBottom); TopoDS_Edge edgeBottom = mkEdgeBottom.Edge(); gp_Pnt bottomPoint = findClosestPoint(inEdges, edgeBottom); Handle(Geom_Line) lineTop = new Geom_Line(topMid, xDir); BRepBuilderAPI_MakeEdge mkEdgeTop(lineTop); TopoDS_Edge edgeTop = mkEdgeTop.Edge(); gp_Pnt topPoint = findClosestPoint(inEdges, edgeTop); refMin = Base::Vector3d(bottomPoint.X(), bottomPoint.Y(), 0.0); refMax = Base::Vector3d(topPoint.X(), topPoint.Y(), 0.0); } result.first = refMin; result.second = refMax; return result; } //given list of curves, find the closest point from any curve to a boundary //computation intensive for a cosmetic result. gp_Pnt DrawDimHelper::findClosestPoint(std::vector inEdges, TopoDS_Edge& boundary) { // Base::Console().message("DDH::findClosestPoint() - edges: %d\n", inEdges.size()); // //find an extent point that is actually on one of the curves double minDistance(std::numeric_limits::max()); gp_Pnt nearPoint; for (auto& edge : inEdges) { BRepExtrema_DistShapeShape extss(edge, boundary); if (!extss.IsDone()) { Base::Console().warning( "DDH::findClosestPoint - BRepExtrema_DistShapeShape failed - 1\n"); continue; } if (extss.NbSolution() == 0) { Base::Console().warning( "DDH::findClosestPoint - BRepExtrema_DistShapeShape failed - 2\n"); continue; } if (extss.Value() < minDistance) { minDistance = extss.Value(); nearPoint = extss.PointOnShape1(1); } } return nearPoint; } std::pair DrawDimHelper::minMax3d(DrawViewPart* dvp, ReferenceVector references, int direction) { // Base::Console().message("DDH::minMax3d() - references: %d\n", references.size()); std::pair result; Base::Vector3d refMin; Base::Vector3d refMax; gp_Ax3 projAx3;//OXYZ gp_Pln projPlane(projAx3); BRep_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); for (auto& ref : references) { auto tempGeom = ref.getGeometry(); if (tempGeom.IsNull()) { continue; } builder.Add(comp, tempGeom); } Base::Vector3d centroid = dvp->getOriginalCentroid(); TopoDS_Shape centeredShape =//this result is a throw away. We will work with comp. DrawViewPart::centerScaleRotate(dvp, comp, centroid); //project the selected 3d shapes in the dvp's coord system TechDraw::GeometryObjectPtr go( std::make_shared(std::string(), nullptr)); go->setIsoCount(0); go->isPerspective(false); go->usePolygonHLR(false); go->projectShape(comp, dvp->getProjectionCS()); auto edges = go->getEdgeGeometry(); if (edges.empty()) { return result; } Bnd_Box shapeBbx; shapeBbx.SetGap(1.0);//make the box a bit bigger std::vector inEdges; for (auto& bg : edges) { inEdges.push_back(bg->getOCCEdge()); BRepBndLib::Add(bg->getOCCEdge(), shapeBbx); } //from here on this is the same as 2d method double minX, minY, minZ, maxX, maxY, maxZ; shapeBbx.Get(minX, minY, minZ, maxX, maxY, maxZ); double xMid = (maxX + minX) / 2.0; double yMid = (maxY + minY) / 2.0; gp_Pnt rightMid(maxX, yMid, 0.0); gp_Pnt leftMid(minX, yMid, 0.0); gp_Pnt topMid(xMid, maxY, 0.0); gp_Pnt bottomMid(xMid, minY, 0.0); gp_Dir xDir(1.0, 0.0, 0.0); gp_Dir yDir(0.0, 1.0, 0.0); if (direction == HORIZONTAL) { Handle(Geom_Line) lineLeft = new Geom_Line(leftMid, yDir); BRepBuilderAPI_MakeEdge mkEdgeLeft(lineLeft); TopoDS_Edge edgeLeft = mkEdgeLeft.Edge(); gp_Pnt leftPoint = findClosestPoint(inEdges, edgeLeft); Handle(Geom_Line) lineRight = new Geom_Line(rightMid, yDir); BRepBuilderAPI_MakeEdge mkEdgeRight(lineRight); TopoDS_Edge edgeRight = mkEdgeRight.Edge(); gp_Pnt rightPoint = findClosestPoint(inEdges, edgeRight); refMin = Base::Vector3d(leftPoint.X(), leftPoint.Y(), 0.0); refMax = Base::Vector3d(rightPoint.X(), rightPoint.Y(), 0.0); } else if (direction == VERTICAL) { Handle(Geom_Line) lineBottom = new Geom_Line(bottomMid, xDir); BRepBuilderAPI_MakeEdge mkEdgeBottom(lineBottom); TopoDS_Edge edgeBottom = mkEdgeBottom.Edge(); gp_Pnt bottomPoint = findClosestPoint(inEdges, edgeBottom); Handle(Geom_Line) lineTop = new Geom_Line(topMid, xDir); BRepBuilderAPI_MakeEdge mkEdgeTop(lineTop); TopoDS_Edge edgeTop = mkEdgeTop.Edge(); gp_Pnt topPoint = findClosestPoint(inEdges, edgeTop); refMin = Base::Vector3d(bottomPoint.X(), bottomPoint.Y(), 0.0); refMax = Base::Vector3d(topPoint.X(), topPoint.Y(), 0.0); } result.first = refMin; result.second = refMax; return result; } DrawViewDimension* DrawDimHelper::makeDistDim(DrawViewPart* dvp, std::string dimType, Base::Vector3d inMin,//is this scaled or unscaled?? Base::Vector3d inMax,//expects scaled from makeExtentDim bool extent) { // Base::Console().message("DDH::makeDistDim() - inMin: %s inMax: %s\n", // DrawUtil::formatVector(inMin).c_str(), // DrawUtil::formatVector(inMax).c_str()); TechDraw::DrawPage* page = dvp->findParentPage(); std::string pageName = page->getNameInDocument(); TechDraw::DrawViewDimension* dim = nullptr; App::Document* doc = dvp->getDocument(); std::string dimName = doc->getUniqueObjectName("Dimension"); if (extent) { dimName = doc->getUniqueObjectName("DimExtent"); } std::vector gVerts = dvp->getVertexGeometry(); // invert the point so the math works correctly Base::Vector3d cleanMin = DrawUtil::invertY(inMin); cleanMin = CosmeticVertex::makeCanonicalPoint(dvp, cleanMin); std::string tag1 = dvp->addCosmeticVertex(cleanMin); int iGV1 = dvp->add1CVToGV(tag1); Base::Vector3d cleanMax = DrawUtil::invertY(inMax); cleanMax = CosmeticVertex::makeCanonicalPoint(dvp, cleanMax); std::string tag2 = dvp->addCosmeticVertex(cleanMax); int iGV2 = dvp->add1CVToGV(tag2); gVerts = dvp->getVertexGeometry(); std::vector objs; std::vector subs; std::stringstream ss; ss << "Vertex" << iGV1; std::string vertexName = ss.str(); subs.push_back(vertexName); objs.push_back(dvp); ss.clear(); ss.str(std::string()); ss << "Vertex" << iGV2; vertexName = ss.str(); subs.push_back(vertexName); objs.push_back(dvp); if (extent) { Base::Interpreter().runStringArg( "App.activeDocument().addObject('TechDraw::DrawViewDimExtent', '%s')", dimName.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.translateLabel('DrawViewDimExtent', 'DimExtent', '%s')", dimName.c_str(), dimName.c_str()); } else { Base::Interpreter().runStringArg( "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", dimName.c_str()); Base::Interpreter().runStringArg( "App.activeDocument().%s.translateLabel('DrawViewDimimension', 'Dimension', '%s')", dimName.c_str(), dimName.c_str()); } Base::Interpreter().runStringArg( "App.activeDocument().%s.Type = '%s'", dimName.c_str(), dimType.c_str()); dim = dynamic_cast(doc->getObject(dimName.c_str())); if (!dim) { throw Base::TypeError("DDH::makeDistDim - dim not found\n"); } dim->References2D.setValues(objs, subs); Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", pageName.c_str(), dimName.c_str()); dvp->requestPaint(); return dim; }