/*************************************************************************** * Copyright (c) 2019 WandererFan * * Copyright (c) 2022 Benjamin Bræstrup Sayoc * * * * 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 "CenterLine.h" #include "DrawUtil.h" #include "DrawViewPart.h" #include "Geometry.h" #include "CenterLinePy.h" #include "ShapeUtils.h" 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 (); 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& subNames, const Mode mode, const bool flip) { // Base::Console().message("CL::CLBuilder()\n - subNames: %d\n", subNames.size()); std::pair ends; std::vector faces; std::vector edges; std::vector 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 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 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(p1)); gp_Pnt gp2(Base::convertTo(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(DU::invertY(p1 * scale))); gp_Pnt gp2(Base::convertTo(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 CenterLine::rotatePointsAroundMid(const Base::Vector3d& p1, const Base::Vector3d& p2, const Base::Vector3d& mid, const double angleDeg) { std::pair 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 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 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 CenterLine::calcEndPoints(const DrawViewPart* partFeat, const std::vector& 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(); } Bnd_Box faceBox; faceBox.SetGap(0.0); double scale = partFeat->getScale(); std::vector faceEdgesAll; for (auto& fn: faceNames) { if (TechDraw::DrawUtil::getGeomTypeFromName(fn) != "Face") { continue; } int idx = TechDraw::DrawUtil::getIndexFromName(fn); std::vector 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 result; result.first = p1 / scale; result.second = p2 / scale; return result; } std::pair CenterLine::calcEndPoints2Lines(const DrawViewPart* partFeat, const std::vector& 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 result; if (edgeNames.empty()) { Base::Console().warning("CL::calcEndPoints2Lines - no edges!\n"); return result; } double scale = partFeat->getScale(); std::vector 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 CenterLine::calcEndPoints2Points(const DrawViewPart* partFeat, const std::vector& 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(); } double scale = partFeat->getScale(); std::vector 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 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() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.incInd(); for (auto& f: m_faces) { writer.Stream() << writer.ind() << "" << std::endl; } writer.decInd(); writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.incInd(); for (auto& e: m_edges) { writer.Stream() << writer.ind() << "" << std::endl; } writer.decInd(); writer.Stream() << writer.ind() << "" << std::endl; writer.Stream() << writer.ind() << "" << std::endl; writer.incInd(); for (auto& p: m_verts) { writer.Stream() << writer.ind() << "" << std::endl; } writer.decInd(); writer.Stream() << writer.ind() << "" << 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() << "