Download src/Mod/TechDraw/App/CenterLine.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 40 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/CenterLine.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/TechDraw/App/CenterLine.cpp
-
curl -L -o CenterLine.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/CenterLine.cpp
40 kB
| /*************************************************************************** | |
| * Copyright (c) 2019 WandererFan <wandererfan@gmail.com> * | |
| * Copyright (c) 2022 Benjamin Bræstrup Sayoc <benj5378@outlook.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 * | |
| * * | |
| ***************************************************************************/ | |
| using namespace TechDraw; | |
| using DU = DrawUtil; | |
| TYPESYSTEM_SOURCE(TechDraw::CenterLine, Base::Persistence) | |
| CenterLine::CenterLine() | |
| { | |
| m_start = Base::Vector3d(0.0, 0.0, 0.0); | |
| m_end = Base::Vector3d(0.0, 0.0, 0.0); | |
| m_mode = Mode::VERTICAL; | |
| m_hShift = 0.0; | |
| m_vShift = 0.0; | |
| m_rotate = 0.0; | |
| m_extendBy = 0.0; | |
| m_type = Type::FACE; | |
| m_flip2Line = false; | |
| m_geometry = std::make_shared<TechDraw::BaseGeom> (); | |
| initialize(); | |
| } | |
| CenterLine::CenterLine(const TechDraw::CenterLine* cl) | |
| { | |
| m_start = cl->m_start; | |
| m_end = cl->m_end; | |
| m_mode = cl->m_mode; | |
| m_hShift = cl->m_hShift; | |
| m_vShift = cl->m_vShift; | |
| m_rotate = cl->m_rotate; | |
| m_extendBy = cl->m_extendBy; | |
| m_type = cl->m_type; | |
| m_flip2Line = cl->m_flip2Line; | |
| m_geometry = cl->m_geometry; //new BaseGeom(cl->m_geometry);?? | |
| initialize(); | |
| } | |
| CenterLine::CenterLine(const TechDraw::BaseGeomPtr& bg, | |
| const Mode m, | |
| const double h, | |
| const double v, | |
| const double r, | |
| const double x) | |
| { | |
| m_start = bg->getStartPoint(); | |
| m_end = bg->getEndPoint(); | |
| m_mode = m; | |
| m_hShift = h; | |
| m_vShift = v; | |
| m_rotate = r; | |
| m_extendBy = x; | |
| m_type = Type::FACE; | |
| m_flip2Line = false; | |
| m_geometry = bg; | |
| initialize(); | |
| } | |
| CenterLine::CenterLine(const Base::Vector3d& pt1, | |
| const Base::Vector3d& pt2, | |
| const Mode m, | |
| const double h, | |
| const double v, | |
| const double r, | |
| const double x) | |
| { | |
| m_start = pt1; | |
| m_end = pt2; | |
| m_mode = m; | |
| m_hShift = h; | |
| m_vShift = v; | |
| m_rotate = r; | |
| m_extendBy = x; | |
| m_type = Type::FACE; | |
| m_flip2Line = false; | |
| m_geometry = BaseGeomPtrFromVectors(pt1, pt2); | |
| initialize(); | |
| } | |
| CenterLine::~CenterLine() | |
| { | |
| } | |
| void CenterLine::initialize() | |
| { | |
| m_geometry->setClassOfEdge(EdgeClass::HARD); | |
| m_geometry->setHlrVisible( true); | |
| m_geometry->setCosmetic(true); | |
| m_geometry->source(SourceType::CENTERLINE); | |
| m_geometry->setCosmeticTag(getTagAsString()); | |
| } | |
| TechDraw::BaseGeomPtr CenterLine::BaseGeomPtrFromVectors(Base::Vector3d pt1, Base::Vector3d pt2) | |
| { | |
| // Base::Console().message("CE::CE(p1, p2)\n"); | |
| Base::Vector3d p1 = DrawUtil::invertY(pt1); | |
| Base::Vector3d p2 = DrawUtil::invertY(pt2); | |
| gp_Pnt gp1(p1.x, p1.y, p1.z); | |
| gp_Pnt gp2(p2.x, p2.y, p2.z); | |
| TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp1, gp2); | |
| TechDraw::BaseGeomPtr bg = TechDraw::BaseGeom::baseFactory(e); | |
| return bg; | |
| } | |
| CenterLine* CenterLine::CenterLineBuilder(const DrawViewPart* partFeat, | |
| const std::vector<std::string>& subNames, | |
| const Mode mode, | |
| const bool flip) | |
| { | |
| // Base::Console().message("CL::CLBuilder()\n - subNames: %d\n", subNames.size()); | |
| std::pair<Base::Vector3d, Base::Vector3d> ends; | |
| std::vector<std::string> faces; | |
| std::vector<std::string> edges; | |
| std::vector<std::string> verts; | |
| std::string geomType = TechDraw::DrawUtil::getGeomTypeFromName(subNames.front()); | |
| Type type = Type::FACE; | |
| if (geomType == "Face") { | |
| type = Type::FACE; | |
| ends = TechDraw::CenterLine::calcEndPoints(partFeat, | |
| subNames, | |
| mode, | |
| 0.0, | |
| 0.0, 0.0, 0.0); | |
| faces = subNames; | |
| } else if (geomType == "Edge") { | |
| type = Type::EDGE; | |
| ends = TechDraw::CenterLine::calcEndPoints2Lines(partFeat, | |
| subNames, | |
| mode, | |
| 0.0, | |
| 0.0, 0.0, 0.0, flip); | |
| edges = subNames; | |
| } else if (geomType == "Vertex") { | |
| type = Type::VERTEX; | |
| ends = TechDraw::CenterLine::calcEndPoints2Points(partFeat, | |
| subNames, | |
| mode, | |
| 0.0, | |
| 0.0, 0.0, 0.0, flip); | |
| verts = subNames; | |
| } | |
| if ((ends.first).IsEqual(ends.second, Precision::Confusion())) { | |
| Base::Console().warning("CenterLineBuilder - endpoints are equal: %s\n", | |
| DrawUtil::formatVector(ends.first).c_str()); | |
| Base::Console().warning("CenterLineBuilder - check V/H/A and/or Flip parameters\n"); | |
| return nullptr; | |
| } | |
| auto * cl = new TechDraw::CenterLine(ends.first, ends.second); | |
| cl->m_type = type; | |
| cl->m_mode = mode; | |
| cl->m_faces = faces; | |
| cl->m_edges = edges; | |
| cl->m_verts = verts; | |
| cl->m_flip2Line = flip; | |
| return cl; | |
| } | |
| TechDraw::BaseGeomPtr CenterLine::scaledGeometry(const TechDraw::DrawViewPart* partFeat) | |
| { | |
| // Base::Console().message("CL::scaledGeometry() - m_type: %d\n", m_type); | |
| double scale = partFeat->getScale(); | |
| double viewAngleDeg = partFeat->Rotation.getValue(); | |
| std::pair<Base::Vector3d, Base::Vector3d> ends; | |
| try { | |
| if (m_faces.empty() && | |
| m_edges.empty() && | |
| m_verts.empty() ) { | |
| // Base::Console().message("CL::scaledGeometry - no geometry to scale!\n"); | |
| //CenterLine was created by points without a geometry reference, | |
| ends = calcEndPointsNoRef(m_start, m_end, scale, m_extendBy, | |
| m_hShift, m_vShift, m_rotate, viewAngleDeg); | |
| } else if (m_type == Type::FACE) { | |
| ends = calcEndPoints(partFeat, | |
| m_faces, | |
| m_mode, m_extendBy, | |
| m_hShift, m_vShift, m_rotate); | |
| } else if (m_type == Type::EDGE) { | |
| ends = calcEndPoints2Lines(partFeat, | |
| m_edges, | |
| m_mode, | |
| m_extendBy, | |
| m_hShift, m_vShift, m_rotate, m_flip2Line); | |
| } else if (m_type == Type::VERTEX) { | |
| ends = calcEndPoints2Points(partFeat, | |
| m_verts, | |
| m_mode, | |
| m_extendBy, | |
| m_hShift, m_vShift, m_rotate, m_flip2Line); | |
| } | |
| } | |
| catch (...) { | |
| Base::Console().error("CL::scaledGeometry - failed to calculate endpoints!\n"); | |
| return nullptr; | |
| } | |
| Base::Vector3d p1 = ends.first; | |
| Base::Vector3d p2 = ends.second; | |
| if (p1.IsEqual(p2, 0.00001)) { | |
| Base::Console().warning("Centerline endpoints are equal. Could not draw.\n"); | |
| //what to do here? //return current geom? | |
| return m_geometry; | |
| } | |
| gp_Pnt gp1(p1.x, p1.y, p1.z); | |
| gp_Pnt gp2(p2.x, p2.y, p2.z); | |
| TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp1, gp2); | |
| TopoDS_Shape s = ShapeUtils::scaleShape(e, scale); | |
| TopoDS_Edge newEdge = TopoDS::Edge(s); | |
| TechDraw::BaseGeomPtr newGeom = TechDraw::BaseGeom::baseFactory(newEdge); | |
| newGeom->setClassOfEdge(EdgeClass::HARD); | |
| newGeom->setHlrVisible( true); | |
| newGeom->setCosmetic(true); | |
| newGeom->source(SourceType::CENTERLINE); | |
| newGeom->setCosmeticTag(getTagAsString()); | |
| return newGeom; | |
| } | |
| TechDraw::BaseGeomPtr CenterLine::scaledAndRotatedGeometry(TechDraw::DrawViewPart* partFeat) | |
| { | |
| // Base::Console().message("CL::scaledGeometry() - m_type: %d\n", m_type); | |
| double scale = partFeat->getScale(); | |
| double viewAngleDeg = partFeat->Rotation.getValue(); | |
| std::pair<Base::Vector3d, Base::Vector3d> ends; | |
| try { | |
| if (m_faces.empty() && | |
| m_edges.empty() && | |
| m_verts.empty() ) { | |
| // Base::Console().message("CL::scaledGeometry - no geometry to scale!\n"); | |
| //CenterLine was created by points without a geometry reference, | |
| ends = calcEndPointsNoRef(m_start, m_end, scale, m_extendBy, | |
| m_hShift, m_vShift, m_rotate, viewAngleDeg); | |
| } else if (m_type == Type::FACE) { | |
| ends = calcEndPoints(partFeat, | |
| m_faces, | |
| m_mode, m_extendBy, | |
| m_hShift, m_vShift, m_rotate); | |
| } else if (m_type == Type::EDGE) { | |
| ends = calcEndPoints2Lines(partFeat, | |
| m_edges, | |
| m_mode, | |
| m_extendBy, | |
| m_hShift, m_vShift, m_rotate, m_flip2Line); | |
| } else if (m_type == Type::VERTEX) { | |
| ends = calcEndPoints2Points(partFeat, | |
| m_verts, | |
| m_mode, | |
| m_extendBy, | |
| m_hShift, m_vShift, m_rotate, m_flip2Line); | |
| } | |
| } | |
| catch (...) { | |
| Base::Console().error("CL::scaledGeometry - failed to calculate endpoints!\n"); | |
| return nullptr; | |
| } | |
| // inversion here breaks face cl. | |
| Base::Vector3d p1 = ends.first; | |
| Base::Vector3d p2 = ends.second; | |
| if (p1.IsEqual(p2, 0.00001)) { | |
| Base::Console().warning("Centerline endpoints are equal. Could not draw.\n"); | |
| //what to do here? //return current geom? | |
| return m_geometry; | |
| } | |
| TopoDS_Edge newEdge; | |
| if (getType() == Type::FACE ) { | |
| gp_Pnt gp1(Base::convertTo<gp_Pnt>(p1)); | |
| gp_Pnt gp2(Base::convertTo<gp_Pnt>(p2)); | |
| TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp1, gp2); | |
| // Mirror shape in Y and scale | |
| TopoDS_Shape s = ShapeUtils::mirrorShape(e, gp_Pnt(0.0, 0.0, 0.0), scale); | |
| // rotate using OXYZ as the coordinate system | |
| s = ShapeUtils::rotateShape(s, gp_Ax2(), - partFeat->Rotation.getValue()); | |
| newEdge = TopoDS::Edge(s); | |
| } else if (getType() == Type::EDGE || | |
| getType() == Type::VERTEX) { | |
| gp_Pnt gp1(Base::convertTo<gp_Pnt>(DU::invertY(p1 * scale))); | |
| gp_Pnt gp2(Base::convertTo<gp_Pnt>(DU::invertY(p2 * scale))); | |
| newEdge = BRepBuilderAPI_MakeEdge(gp1, gp2); | |
| } | |
| TechDraw::BaseGeomPtr newGeom = TechDraw::BaseGeom::baseFactory(newEdge); | |
| if (!newGeom) { | |
| throw Base::RuntimeError("Failed to create center line"); | |
| } | |
| newGeom->setClassOfEdge(EdgeClass::HARD); | |
| newGeom->setHlrVisible( true); | |
| newGeom->setCosmetic(true); | |
| newGeom->source(SourceType::CENTERLINE); | |
| newGeom->setCosmeticTag(getTagAsString()); | |
| return newGeom; | |
| } | |
| std::string CenterLine::toString() const | |
| { | |
| std::stringstream ss; | |
| ss << m_start.x << ", " << | |
| m_start.y << ", " << | |
| m_start.z << ", " << | |
| m_end.x << ", " << | |
| m_end.y << ", " << | |
| m_end.z << ", " << | |
| m_mode << ", " << | |
| m_type << ", " << | |
| m_hShift << ", " << | |
| m_vShift << ", " << | |
| m_rotate << ", " << | |
| m_flip2Line << ", " << | |
| m_extendBy << ", " << | |
| m_faces.size(); | |
| if (!m_faces.empty()) { | |
| for (auto& f: m_faces) { | |
| if (!f.empty()) { | |
| ss << ", " << f ; | |
| } | |
| } | |
| } | |
| std::string clCSV = ss.str(); | |
| std::string fmtCSV = m_format.toString(); | |
| return clCSV + ", $$$, " + fmtCSV; | |
| } | |
| void CenterLine::dump(const char* title) | |
| { | |
| Base::Console().message("CL::dump - %s \n", title); | |
| Base::Console().message("CL::dump - %s \n", toString().c_str()); | |
| } | |
| //! rotate a notional 2d vector from p1 to p2 around its midpoint by angleDeg | |
| std::pair<Base::Vector3d, Base::Vector3d> CenterLine::rotatePointsAroundMid(const Base::Vector3d& p1, | |
| const Base::Vector3d& p2, | |
| const Base::Vector3d& mid, | |
| const double angleDeg) | |
| { | |
| std::pair<Base::Vector3d, Base::Vector3d> result; | |
| double angleRad = Base::toRadians(angleDeg); | |
| result.first.x = ((p1.x - mid.x) * cos(angleRad)) - ((p1.y - mid.y) * sin(angleRad)) + mid.x; | |
| result.first.y = ((p1.x - mid.x) * sin(angleRad)) + ((p1.y - mid.y) * cos(angleRad)) + mid.y; | |
| result.first.z = 0.0; | |
| result.second.x = ((p2.x - mid.x) * cos(angleRad)) - ((p2.y - mid.y) * sin(angleRad)) + mid.x; | |
| result.second.y = ((p2.x - mid.x) * sin(angleRad)) + ((p2.y - mid.y) * cos(angleRad)) + mid.y; | |
| result.second.z = 0.0; | |
| return result; | |
| } | |
| //end points for centerline with no geometry reference | |
| std::pair<Base::Vector3d, Base::Vector3d> CenterLine::calcEndPointsNoRef(const Base::Vector3d& start, | |
| const Base::Vector3d& end, | |
| const double scale, | |
| const double ext, | |
| const double hShift, | |
| const double vShift, | |
| const double rotate, const double viewAngleDeg) | |
| { | |
| // Base::Console().message("CL::calcEndPointsNoRef()\n"); | |
| Base::Vector3d p1 = start; | |
| Base::Vector3d p2 = end; | |
| Base::Vector3d mid = (p1 + p2) / 2.0; | |
| //extend | |
| Base::Vector3d clDir = p2 - p1; | |
| clDir.Normalize(); | |
| p1 = p1 - (clDir * ext); | |
| p2 = p2 + (clDir * ext); | |
| //rotate | |
| if (!DrawUtil::fpCompare(rotate, 0.0)) { | |
| //rotate p1, p2 about mid point | |
| std::tie(p1, p2) = rotatePointsAroundMid(p1, p2, mid, rotate); | |
| } | |
| //shift | |
| if (!DrawUtil::fpCompare(hShift, 0.0)) { | |
| double hss = hShift * scale; | |
| p1.x = p1.x + hss; | |
| p2.x = p2.x + hss; | |
| } | |
| if (!DrawUtil::fpCompare(vShift, 0.0)) { | |
| double vss = vShift * scale; | |
| p1.y = p1.y + vss; | |
| p2.y = p2.y + vss; | |
| } | |
| // rotate the endpoints so that when the View's Rotation is applied, the | |
| // centerline is aligned correctly | |
| std::pair<Base::Vector3d, Base::Vector3d> result; | |
| result.first = p1 / scale; | |
| result.second = p2 / scale; | |
| Base::Vector3d midpoint = (result.first + result.second) / 2.0; | |
| result = rotatePointsAroundMid(result.first, result.second, midpoint, viewAngleDeg * -1.0); | |
| return result; | |
| } | |
| //end points for face centerline | |
| std::pair<Base::Vector3d, Base::Vector3d> CenterLine::calcEndPoints(const DrawViewPart* partFeat, | |
| const std::vector<std::string>& faceNames, | |
| const CenterLine::Mode mode, | |
| const double ext, | |
| const double hShift, | |
| const double vShift, | |
| const double rotate) | |
| { | |
| // Base::Console().message("CL::calcEndPoints()\n"); | |
| if (faceNames.empty()) { | |
| Base::Console().warning("CL::calcEndPoints - no faces!\n"); | |
| return std::pair<Base::Vector3d, Base::Vector3d>(); | |
| } | |
| Bnd_Box faceBox; | |
| faceBox.SetGap(0.0); | |
| double scale = partFeat->getScale(); | |
| std::vector<TopoDS_Edge> faceEdgesAll; | |
| for (auto& fn: faceNames) { | |
| if (TechDraw::DrawUtil::getGeomTypeFromName(fn) != "Face") { | |
| continue; | |
| } | |
| int idx = TechDraw::DrawUtil::getIndexFromName(fn); | |
| std::vector<TechDraw::BaseGeomPtr> faceGeoms = | |
| partFeat->getFaceEdgesByIndex(idx); | |
| if (!faceGeoms.empty()) { | |
| for (auto& fe: faceGeoms) { | |
| if (!fe->getCosmetic()) { | |
| faceEdgesAll.push_back(fe->getOCCEdge()); | |
| } | |
| } | |
| } | |
| } | |
| TopoDS_Shape faceEdgeCompound = DU::vectorToCompound(faceEdgesAll); | |
| if (partFeat->Rotation.getValue() != 0.0) { | |
| // unrotate the input shape to align with the cardinal axes so we can use bbox to | |
| // get size measurements | |
| faceEdgeCompound = ShapeUtils::rotateShape(faceEdgeCompound, | |
| partFeat->getProjectionCS(), | |
| partFeat->Rotation.getValue() * -1.0); | |
| } | |
| // get the center of the unrotated, scaled face | |
| Base::Vector3d faceCenter = ShapeUtils::findCentroidVec(faceEdgeCompound, partFeat->getProjectionCS()); | |
| // we need to move the edges to the origin here to get the right limits from the bounding box | |
| faceEdgeCompound = ShapeUtils::moveShape(faceEdgeCompound, faceCenter * -1.0); | |
| // get the bounding box of the centered and unrotated face | |
| BRepBndLib::AddOptimal(faceEdgeCompound, faceBox); | |
| if (faceBox.IsVoid()) { | |
| Base::Console().error("CL::calcEndPoints - faceBox is void!\n"); | |
| throw Base::IndexError("CenterLine wrong number of faces."); | |
| } | |
| double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax; | |
| faceBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax); | |
| double Xspan = fabs(Xmax - Xmin); | |
| Xspan = (Xspan / 2.0); | |
| double Xmid = 0.0; | |
| Xmax = Xmid + Xspan; | |
| Xmin = Xmid - Xspan; | |
| double Yspan = fabs(Ymax - Ymin); | |
| Yspan = (Yspan / 2.0); | |
| double Ymid = 0.0; | |
| Ymax = Ymid + Yspan; | |
| Ymin = Ymid - Yspan; | |
| Base::Vector3d p1, p2; | |
| if (mode == Mode::VERTICAL) { //vertical | |
| p1 = Base::Vector3d(Xmid, Ymax, 0.0); | |
| p2 = Base::Vector3d(Xmid, Ymin, 0.0); | |
| } else if (mode == Mode::HORIZONTAL) { //horizontal | |
| p1 = Base::Vector3d(Xmin, Ymid, 0.0); | |
| p2 = Base::Vector3d(Xmax, Ymid, 0.0); | |
| } else { //vert == Mode::ALIGNED //aligned, but aligned doesn't make sense for face(s) bbox | |
| Base::Console().message("CL::calcEndPoints - aligned is not applicable to Face center lines\n"); | |
| p1 = Base::Vector3d(Xmid, Ymax, 0.0); | |
| p2 = Base::Vector3d(Xmid, Ymin, 0.0); | |
| } | |
| // now move the extents back to the face center. this should give us the scaled, | |
| // unrotated ends of the cl (but in 3d coordinates, which will be handled at the time | |
| // the cl is added to the view) | |
| p1 += faceCenter; | |
| p2 += faceCenter; | |
| Base::Vector3d mid = (p1 + p2) / 2.0; | |
| //extend | |
| Base::Vector3d clDir = p2 - p1; | |
| clDir.Normalize(); | |
| p1 = p1 - (clDir * ext); | |
| p2 = p2 + (clDir * ext); | |
| //rotate | |
| if (!DrawUtil::fpCompare(rotate, 0.0)) { | |
| //rotate p1, p2 about mid point | |
| std::tie(p1, p2) = rotatePointsAroundMid(p1, p2, mid, rotate); | |
| } | |
| //shift | |
| if (!DrawUtil::fpCompare(hShift, 0.0)) { | |
| double hss = hShift * scale; | |
| p1.x = p1.x + hss; | |
| p2.x = p2.x + hss; | |
| } | |
| if (!DrawUtil::fpCompare(vShift, 0.0)) { | |
| double vss = vShift * scale; | |
| p1.y = p1.y + vss; | |
| p2.y = p2.y + vss; | |
| } | |
| std::pair<Base::Vector3d, Base::Vector3d> result; | |
| result.first = p1 / scale; | |
| result.second = p2 / scale; | |
| return result; | |
| } | |
| std::pair<Base::Vector3d, Base::Vector3d> CenterLine::calcEndPoints2Lines(const DrawViewPart* partFeat, | |
| const std::vector<std::string>& edgeNames, | |
| const Mode mode, | |
| const double ext, | |
| const double hShift, | |
| const double vShift, | |
| const double rotate, | |
| const bool flip) | |
| { | |
| Q_UNUSED(flip) | |
| // Base::Console().message("CL::calc2Lines() - mode: %d flip: %d edgeNames: %d\n", mode, flip, edgeNames.size()); | |
| std::pair<Base::Vector3d, Base::Vector3d> result; | |
| if (edgeNames.empty()) { | |
| Base::Console().warning("CL::calcEndPoints2Lines - no edges!\n"); | |
| return result; | |
| } | |
| double scale = partFeat->getScale(); | |
| std::vector<TechDraw::BaseGeomPtr> edges; | |
| for (auto& en: edgeNames) { | |
| if (TechDraw::DrawUtil::getGeomTypeFromName(en) != "Edge") { | |
| continue; | |
| } | |
| int idx = TechDraw::DrawUtil::getIndexFromName(en); | |
| TechDraw::BaseGeomPtr bg = partFeat->getGeomByIndex(idx); | |
| if (bg) { | |
| edges.push_back(bg); | |
| } else { | |
| Base::Console().message("CL::calcEndPoints2Lines - no geom for index: %d\n", idx); | |
| } | |
| } | |
| if (edges.size() != 2) { | |
| Base::Console().message("CL::calcEndPoints2Lines - wrong number of edges: %d!\n", edges.size()); | |
| throw Base::IndexError("CenterLine wrong number of edges."); | |
| } | |
| // these points are centered, rotated, scaled and inverted. | |
| // invert the points so the math works correctly | |
| Base::Vector3d l1p1 = DU::invertY(edges.front()->getStartPoint()); | |
| Base::Vector3d l1p2 = DU::invertY(edges.front()->getEndPoint()); | |
| Base::Vector3d l2p1 = DU::invertY(edges.back()->getStartPoint()); | |
| Base::Vector3d l2p2 = DU::invertY(edges.back()->getEndPoint()); | |
| // The centerline is drawn using the midpoints of the two lines that connect l1p1-l2p1 and l1p2-l2p2. | |
| // However, we don't know which point should be l1p1 to get a geometrically correct result, see | |
| // https://wiki.freecad.org/File:TD-CenterLineFlip.png for an illustration of the problem. | |
| // Thus we test this by a circulation test, see this post for a brief explanation: | |
| // https://forum.freecad.org/viewtopic.php?p=505733#p505615 | |
| if (DrawUtil::circulation(l1p1, l1p2, l2p1) != DrawUtil::circulation(l1p2, l2p2, l2p1)) { | |
| Base::Vector3d temp; // reverse line 1 | |
| temp = l1p1; | |
| l1p1 = l1p2; | |
| l1p2 = temp; | |
| } | |
| Base::Vector3d p1 = (l1p1 + l2p1) / 2.0; | |
| Base::Vector3d p2 = (l1p2 + l2p2) / 2.0; | |
| Base::Vector3d mid = (p1 + p2) / 2.0; | |
| // if the proposed end points prevent the creation of a vertical or horizontal centerline, we need | |
| // to prevent the "orientation" code below from creating identical endpoints. This would create a | |
| // zero length edge and cause problems later. | |
| bool inhibitVertical = false; | |
| bool inhibitHorizontal = false; | |
| if (DU::fpCompare(p1.y, p2.y, EWTOLERANCE)) { | |
| // proposed end points are aligned vertically, so we can't draw a vertical line to connect them | |
| inhibitVertical = true; | |
| } | |
| if (DU::fpCompare(p1.x, p2.x, EWTOLERANCE)) { | |
| // proposed end points are aligned horizontally, so we can't draw a horizontal line to connect them | |
| inhibitHorizontal = true; | |
| } | |
| //orientation | |
| if (partFeat->Rotation.getValue() == 0.0) { | |
| // if the view is rotated, then horizontal and vertical lose their meaning | |
| if (mode == CenterLine::Mode::VERTICAL && !inhibitVertical) { | |
| p1.x = mid.x; | |
| p2.x = mid.x; | |
| } else if (mode == CenterLine::Mode::HORIZONTAL && !inhibitHorizontal) { | |
| p1.y = mid.y; | |
| p2.y = mid.y; | |
| } else if (mode == CenterLine::Mode::ALIGNED) { | |
| // no op | |
| } | |
| } | |
| //extend | |
| Base::Vector3d clDir = p2 - p1; | |
| clDir.Normalize(); | |
| p1 = p1 - (clDir * ext); | |
| p2 = p2 + (clDir * ext); | |
| //rotate | |
| if (!DrawUtil::fpCompare(rotate, 0.0)) { | |
| //rotate p1, p2 about mid | |
| std::tie(p1, p2) = rotatePointsAroundMid(p1, p2, mid, rotate); | |
| } | |
| //shift | |
| if (!DrawUtil::fpCompare(hShift, 0.0)) { | |
| double hss = hShift * scale; | |
| p1.x = p1.x + hss; | |
| p2.x = p2.x + hss; | |
| } | |
| if (!DrawUtil::fpCompare(vShift, 0.0)) { | |
| double vss = vShift * scale; | |
| p1.y = p1.y + vss; | |
| p2.y = p2.y + vss; | |
| } | |
| // the cl will be scaled when drawn, so unscale now. | |
| result.first = p1 / scale; | |
| result.second = p2 / scale; | |
| return result; | |
| } | |
| std::pair<Base::Vector3d, Base::Vector3d> CenterLine::calcEndPoints2Points(const DrawViewPart* partFeat, | |
| const std::vector<std::string>& vertNames, | |
| const Mode mode, | |
| const double ext, | |
| const double hShift, | |
| const double vShift, | |
| const double rotate, | |
| const bool flip) | |
| { | |
| // Base::Console().message("CL::calc2Points() - mode: %d\n", mode); | |
| if (vertNames.empty()) { | |
| Base::Console().warning("CL::calcEndPoints2Points - no points!\n"); | |
| return std::pair<Base::Vector3d, Base::Vector3d>(); | |
| } | |
| double scale = partFeat->getScale(); | |
| std::vector<TechDraw::VertexPtr> points; | |
| for (auto& vn: vertNames) { | |
| if (TechDraw::DrawUtil::getGeomTypeFromName(vn) != "Vertex") { | |
| continue; | |
| } | |
| int idx = TechDraw::DrawUtil::getIndexFromName(vn); | |
| TechDraw::VertexPtr v = partFeat->getProjVertexByIndex(idx); | |
| if (v) { | |
| points.push_back(v); | |
| } | |
| } | |
| if (points.size() != 2) { | |
| throw Base::IndexError("CenterLine wrong number of points."); | |
| } | |
| Base::Vector3d v1 = DU::invertY(points.front()->point()); | |
| Base::Vector3d v2 = DU::invertY(points.back()->point()); | |
| Base::Vector3d mid = (v1 + v2) / 2.0; | |
| Base::Vector3d dir = v2 - v1; | |
| // if the proposed end points prevent the creation of a vertical or horizontal centerline, we need | |
| // to prevent the "orientation" code below from creating identical endpoints. This would create a | |
| // zero length edge and cause problems later. | |
| bool inhibitVertical = false; | |
| bool inhibitHorizontal = false; | |
| if (DU::fpCompare(v1.y, v2.y, EWTOLERANCE)) { | |
| // proposed end points are aligned horizontally, can not draw horizontal CL | |
| inhibitHorizontal = true; | |
| } | |
| if (DU::fpCompare(v1.x, v2.x, EWTOLERANCE)) { | |
| // proposed end points are aligned vertically, can not draw vertical CL | |
| inhibitVertical = true; | |
| } | |
| //orientation | |
| if (partFeat->Rotation.getValue() == 0.0) { | |
| // if the view is rotated, then horizontal and vertical lose their meaning | |
| if (mode == Mode::VERTICAL && !inhibitVertical) { | |
| //Vertical | |
| v1.x = mid.x; | |
| v2.x = mid.x; | |
| } else if (mode == Mode::HORIZONTAL && !inhibitHorizontal) { | |
| //Horizontal | |
| v1.y = mid.y; | |
| v2.y = mid.y; | |
| } else if (mode == Mode::ALIGNED) { //Aligned | |
| // no op | |
| } | |
| } | |
| double length = dir.Length(); | |
| dir.Normalize(); | |
| Base::Vector3d clDir(dir.y, -dir.x, dir.z); | |
| Base::Vector3d p1 = mid + clDir * (length / 2.0); | |
| Base::Vector3d p2 = mid - clDir * (length / 2.0); | |
| if (flip) { //is flip relevant to 2 point??? | |
| Base::Vector3d temp = p1; | |
| p1 = p2; | |
| p2 = temp; | |
| } | |
| //extend | |
| p1 = p1 + (clDir * ext); | |
| p2 = p2 - (clDir * ext); | |
| //rotate | |
| if (!DrawUtil::fpCompare(rotate, 0.0)) { | |
| //rotate p1, p2 about mid | |
| std::tie(p1, p2) = rotatePointsAroundMid(p1, p2, mid, rotate); | |
| } | |
| //shift | |
| if (!DrawUtil::fpCompare(hShift, 0.0)) { | |
| double hss = hShift * scale; | |
| p1.x = p1.x + hss; | |
| p2.x = p2.x + hss; | |
| } | |
| if (!DrawUtil::fpCompare(vShift, 0.0)) { | |
| double vss = vShift * scale; | |
| p1.y = p1.y + vss; | |
| p2.y = p2.y + vss; | |
| } | |
| std::pair<Base::Vector3d, Base::Vector3d> result; | |
| result.first = p1 / scale; | |
| result.second = p2 / scale; | |
| return result; | |
| } | |
| // Persistence implementers | |
| unsigned int CenterLine::getMemSize () const | |
| { | |
| return 1; | |
| } | |
| void CenterLine::Save(Base::Writer &writer) const | |
| { | |
| writer.Stream() << writer.ind() << "<Start " | |
| << "X=\"" << m_start.x << | |
| "\" Y=\"" << m_start.y << | |
| "\" Z=\"" << m_start.z << | |
| "\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<End " | |
| << "X=\"" << m_end.x << | |
| "\" Y=\"" << m_end.y << | |
| "\" Z=\"" << m_end.z << | |
| "\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Mode value=\"" << m_mode <<"\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<HShift value=\"" << m_hShift <<"\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<VShift value=\"" << m_vShift <<"\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Rotate value=\"" << m_rotate <<"\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Extend value=\"" << m_extendBy <<"\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Type value=\"" << m_type <<"\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Flip value=\"" << m_flip2Line <<"\"/>" << std::endl; | |
| writer.Stream() | |
| << writer.ind() | |
| << "<Faces " | |
| << "FaceCount=\"" << m_faces.size() << | |
| "\">" << std::endl; | |
| writer.incInd(); | |
| for (auto& f: m_faces) { | |
| writer.Stream() | |
| << writer.ind() | |
| << "<Face value=\"" << f <<"\"/>" << std::endl; | |
| } | |
| writer.decInd(); | |
| writer.Stream() << writer.ind() << "</Faces>" << std::endl; | |
| writer.Stream() | |
| << writer.ind() | |
| << "<Edges " | |
| << "EdgeCount=\"" << m_edges.size() << | |
| "\">" << std::endl; | |
| writer.incInd(); | |
| for (auto& e: m_edges) { | |
| writer.Stream() | |
| << writer.ind() | |
| << "<Edge value=\"" << e <<"\"/>" << std::endl; | |
| } | |
| writer.decInd(); | |
| writer.Stream() << writer.ind() << "</Edges>" << std::endl; | |
| writer.Stream() | |
| << writer.ind() | |
| << "<CLPoints " | |
| << "CLPointCount=\"" << m_verts.size() << | |
| "\">" << std::endl; | |
| writer.incInd(); | |
| for (auto& p: m_verts) { | |
| writer.Stream() | |
| << writer.ind() | |
| << "<CLPoint value=\"" << p <<"\"/>" << std::endl; | |
| } | |
| writer.decInd(); | |
| writer.Stream() << writer.ind() << "</CLPoints>" << std::endl ; | |
| // style is deprecated in favour of line number, but we still save and restore it | |
| // to avoid problems with old documents. | |
| writer.Stream() << writer.ind() << "<Style value=\"" << m_format.getStyle() << "\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.getWidth() << "\"/>" << std::endl; | |
| writer.Stream() << writer.ind() << "<Color value=\"" << m_format.getColor().asHexString() << "\"/>" << std::endl; | |
| const char v = m_format.getVisible() ? '1' : '0'; | |
| writer.Stream() << writer.ind() << "<Visible value=\"" << v << "\"/>" << std::endl; | |
| //stored geometry | |
| if (!m_geometry) { | |
| return Base::Console().error("CL::Save - m_geometry is null\n"); | |
| } | |
| writer.Stream() << writer.ind() << "<GeometryType value=\"" << m_geometry->getGeomType() <<"\"/>" << std::endl; | |
| if (m_geometry->getGeomType() == GeomType::GENERIC) { | |
| GenericPtr gen = std::static_pointer_cast<Generic>(m_geometry); | |
| gen->Save(writer); | |
| } else if (m_geometry->getGeomType() == GeomType::CIRCLE) { | |
| TechDraw::CirclePtr circ = std::static_pointer_cast<TechDraw::Circle>(m_geometry); | |
| circ->Save(writer); | |
| } else if (m_geometry->getGeomType() == GeomType::ARCOFCIRCLE) { | |
| TechDraw::AOCPtr aoc = std::static_pointer_cast<TechDraw::AOC>(m_geometry); | |
| aoc->Save(writer); | |
| } else { | |
| Base::Console().message("CL::Save - unimplemented geomType: %d\n", static_cast<int>(m_geometry->getGeomType())); | |
| } | |
| writer.Stream() << writer.ind() << "<LineNumber value=\"" << m_format.getLineNumber() << "\"/>" << std::endl; | |
| } | |
| void CenterLine::Restore(Base::XMLReader &reader) | |
| { | |
| if (!CosmeticVertex::restoreCosmetic()) { | |
| return; | |
| } | |
| // Base::Console().message("CL::Restore - reading elements\n"); | |
| // read my Element | |
| reader.readElement("Start"); | |
| // get the value of my Attribute | |
| m_start.x = reader.getAttribute<double>("X"); | |
| m_start.y = reader.getAttribute<double>("Y"); | |
| m_start.z = reader.getAttribute<double>("Z"); | |
| reader.readElement("End"); | |
| m_end.x = reader.getAttribute<double>("X"); | |
| m_end.y = reader.getAttribute<double>("Y"); | |
| m_end.z = reader.getAttribute<double>("Z"); | |
| reader.readElement("Mode"); | |
| m_mode = reader.getAttribute<Mode>("value"); | |
| reader.readElement("HShift"); | |
| m_hShift = reader.getAttribute<double>("value"); | |
| reader.readElement("VShift"); | |
| m_vShift = reader.getAttribute<double>("value"); | |
| reader.readElement("Rotate"); | |
| m_rotate = reader.getAttribute<double>("value"); | |
| reader.readElement("Extend"); | |
| m_extendBy = reader.getAttribute<double>("value"); | |
| reader.readElement("Type"); | |
| m_type = reader.getAttribute<Type>("value"); | |
| reader.readElement("Flip"); | |
| m_flip2Line = reader.getAttribute<bool>("value") == 0; | |
| reader.readElement("Faces"); | |
| int count = reader.getAttribute<long>("FaceCount"); | |
| int i = 0; | |
| for ( ; i < count; i++) { | |
| reader.readElement("Face"); | |
| std::string f = reader.getAttribute<const char*>("value"); | |
| m_faces.push_back(f); | |
| } | |
| reader.readEndElement("Faces"); | |
| reader.readElement("Edges"); | |
| count = reader.getAttribute<long>("EdgeCount"); | |
| i = 0; | |
| for ( ; i < count; i++) { | |
| reader.readElement("Edge"); | |
| std::string e = reader.getAttribute<const char*>("value"); | |
| m_edges.push_back(e); | |
| } | |
| reader.readEndElement("Edges"); | |
| reader.readElement("CLPoints"); | |
| count = reader.getAttribute<long>("CLPointCount"); | |
| i = 0; | |
| for ( ; i < count; i++) { | |
| reader.readElement("CLPoint"); | |
| std::string p = reader.getAttribute<const char*>("value"); | |
| m_verts.push_back(p); | |
| } | |
| reader.readEndElement("CLPoints"); | |
| // style is deprecated in favour of line number, but we still save and restore it | |
| // to avoid problems with old documents. | |
| reader.readElement("Style"); | |
| m_format.setStyle(reader.getAttribute<long>("value")); | |
| reader.readElement("Weight"); | |
| m_format.setWidth(reader.getAttribute<double>("value")); | |
| reader.readElement("Color"); | |
| std::string tempHex = reader.getAttribute<const char*>("value"); | |
| Base::Color tempColor; | |
| tempColor.fromHexString(tempHex); | |
| m_format.setColor(tempColor); | |
| reader.readElement("Visible"); | |
| m_format.setVisible(reader.getAttribute<bool>("value")); | |
| //stored geometry | |
| reader.readElement("GeometryType"); | |
| GeomType gType = reader.getAttribute<GeomType>("value"); | |
| if (gType == GeomType::GENERIC) { | |
| TechDraw::GenericPtr gen = std::make_shared<TechDraw::Generic> (); | |
| gen->Restore(reader); | |
| gen->setOCCEdge(GeometryUtils::edgeFromGeneric(gen)); | |
| m_geometry = gen; | |
| } else if (gType == GeomType::CIRCLE) { | |
| TechDraw::CirclePtr circ = std::make_shared<TechDraw::Circle> (); | |
| circ->Restore(reader); | |
| circ->setOCCEdge(GeometryUtils::edgeFromCircle(circ)); | |
| m_geometry = circ; | |
| } else if (gType == GeomType::ARCOFCIRCLE) { | |
| TechDraw::AOCPtr aoc = std::make_shared<TechDraw::AOC> (); | |
| aoc->Restore(reader); | |
| aoc->setOCCEdge(GeometryUtils::edgeFromCircleArc(aoc)); | |
| m_geometry = aoc; | |
| } else { | |
| Base::Console().warning("CL::Restore - unimplemented geomType: %d\n", static_cast<int>(gType)); | |
| } | |
| // older documents may not have the LineNumber element, so we need to check the | |
| // next entry. if it is a start element, then we check if it is a start element | |
| // for LineNumber. | |
| // test for ISOLineNumber can be removed after testing. It is a left over for the earlier | |
| // ISO only line handling. | |
| if (reader.readNextElement()) { | |
| if(strcmp(reader.localName(),"LineNumber") == 0 || | |
| strcmp(reader.localName(),"ISOLineNumber") == 0 ) { | |
| // this centerline has an LineNumber attribute | |
| m_format.setLineNumber(reader.getAttribute<long>("value")); | |
| } else { | |
| // LineNumber not found. | |
| m_format.setLineNumber(LineFormat::InvalidLine); | |
| } | |
| } | |
| } | |
| CenterLine* CenterLine::copy() const | |
| { | |
| CenterLine* newCL = new CenterLine(); | |
| newCL->m_start = m_start; | |
| newCL->m_end = m_end; | |
| newCL->m_mode = m_mode; | |
| newCL->m_hShift = m_hShift; | |
| newCL->m_vShift = m_vShift; | |
| newCL->m_rotate = m_rotate; | |
| newCL->m_extendBy = m_extendBy; | |
| newCL->m_type = m_type; | |
| newCL->m_flip2Line = m_flip2Line; | |
| newCL->m_faces = m_faces; | |
| newCL->m_edges = m_edges; | |
| newCL->m_verts = m_verts; | |
| TechDraw::BaseGeomPtr newGeom = m_geometry->copy(); | |
| newCL->m_geometry = newGeom; | |
| newCL->m_format = m_format; | |
| return newCL; | |
| } | |
| CenterLine *CenterLine::clone() const | |
| { | |
| CenterLine* cpy = this->copy(); | |
| cpy->setTag(this->getTag()); | |
| return cpy; | |
| } | |
| // To do: make const | |
| PyObject* CenterLine::getPyObject() | |
| { | |
| if (PythonObject.is(Py::_None())) { | |
| // ref counter is set to 1 | |
| PythonObject = Py::Object(new CenterLinePy(this), true); | |
| } | |
| return Py::new_reference_to(PythonObject); | |
| } | |
| void CenterLine::setShifts(const double h, const double v) | |
| { | |
| m_hShift = h; | |
| m_vShift = v; | |
| } | |
| double CenterLine::getHShift() const | |
| { | |
| return m_hShift; | |
| } | |
| double CenterLine::getVShift() const | |
| { | |
| return m_vShift; | |
| } | |
| void CenterLine::setRotate(const double r) | |
| { | |
| m_rotate = r; | |
| } | |
| double CenterLine::getRotate() const | |
| { | |
| return m_rotate; | |
| } | |
| void CenterLine::setExtend(const double e) | |
| { | |
| m_extendBy = e; | |
| } | |
| double CenterLine::getExtend() const | |
| { | |
| return m_extendBy; | |
| } | |
| void CenterLine::setFlip(const bool f) | |
| { | |
| m_flip2Line = f; | |
| } | |
| bool CenterLine::getFlip() const | |
| { | |
| return m_flip2Line; | |
| } | |