/*************************************************************************** * 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 "CenterLine.h" #include "CosmeticExtension.h" #include "CosmeticExtensionPy.h" #include "Cosmetic.h" #include "DrawUtil.h" #include "DrawViewPart.h" #include "GeometryObject.h" using namespace TechDraw; using namespace std; using DU = DrawUtil; EXTENSION_PROPERTY_SOURCE(TechDraw::CosmeticExtension, App::DocumentObjectExtension) CosmeticExtension::CosmeticExtension() { static const char *cgroup = "Cosmetics"; EXTENSION_ADD_PROPERTY_TYPE(CosmeticVertexes, (nullptr), cgroup, App::Prop_Output, "CosmeticVertex Save/Restore"); EXTENSION_ADD_PROPERTY_TYPE(CosmeticEdges, (nullptr), cgroup, App::Prop_Output, "CosmeticEdge Save/Restore"); EXTENSION_ADD_PROPERTY_TYPE(CenterLines ,(nullptr), cgroup, App::Prop_Output, "CenterLine Save/Restore"); EXTENSION_ADD_PROPERTY_TYPE(GeomFormats ,(nullptr), cgroup, App::Prop_Output, "Geometry format Save/Restore"); initExtensionType(CosmeticExtension::getExtensionClassTypeId()); } CosmeticExtension::~CosmeticExtension() { // do not free memory here as the destruction of the properties will // delete any entries. } /// get a pointer to the parent view TechDraw::DrawViewPart* CosmeticExtension::getOwner() { return static_cast(getExtendedObject()); } //! remove cosmetic elements for a list of subelement names void CosmeticExtension::deleteCosmeticElements(std::vector removables) { DrawViewPart* viewPart = getOwner(); for (auto& name : removables) { if (DU::getGeomTypeFromName(name) == "Vertex" && viewPart->isCosmeticVertex(name)) { CosmeticVertex* vert = getCosmeticVertexBySelection(name); removeCosmeticVertex(vert->getTagAsString()); continue; } if (DU::getGeomTypeFromName(name) == "Edge" && ( viewPart->isCosmeticEdge(name) || viewPart->isCenterLine(name))) { CosmeticEdge* edge = getCosmeticEdgeBySelection(name); if (edge) { // if not edge, something has gone very wrong! removeCosmeticEdge(edge->getTagAsString()); continue; } CenterLine* line = getCenterLineBySelection(name); if (line) { removeCenterLine(line->getTagAsString()); continue; } } } } //============================================================================== //CosmeticVertex x, y are stored as unscaled, but mirrored (inverted Y) values. //if you are creating a CV based on calculations of scaled geometry, you need to //unscale x, y before creation. //if you are creating a CV based on calculations of mirrored geometry, you need to //mirror again before creation. // this is never called! /// remove all the cosmetic vertices in the property list void CosmeticExtension::clearCosmeticVertexes() { std::vector cVerts = CosmeticVertexes.getValues(); for (auto& vert : cVerts) { delete vert; } std::vector noVerts; CosmeticVertexes.setValues(noVerts); } /// add the cosmetic verts in the property list to view's vertex geometry list void CosmeticExtension::addCosmeticVertexesToGeom() { const std::vector cVerts = CosmeticVertexes.getValues(); for (auto& cv : cVerts) { double scale = getOwner()->getScale(); double rotDegrees = getOwner()->Rotation.getValue(); Base::Vector3d cvPosition = cv->rotatedAndScaled(scale, rotDegrees); int iGV = getOwner()->getGeometryObject()->addCosmeticVertex(cvPosition, cv->getTagAsString()); cv->linkGeom = iGV; } } /// add a single cosmetic vertex in the property list to the view's vertex geometry list int CosmeticExtension::add1CVToGV(const std::string& tag) { TechDraw::CosmeticVertex* cv = getCosmeticVertex(tag); if (!cv) { Base::Console().message("CE::add1CVToGV - cv %s not found\n", tag.c_str()); return 0; } double scale = getOwner()->getScale(); double rotDegrees = getOwner()->Rotation.getValue(); Base::Vector3d cvPosition = cv->rotatedAndScaled(scale, rotDegrees); int iGV = getOwner()->getGeometryObject()->addCosmeticVertex(cvPosition, cv->getTagAsString()); cv->linkGeom = iGV; return iGV; } /// update the parent view's vertex geometry with all the cosmetic vertices in the list property void CosmeticExtension::refreshCVGeoms() { std::vector gVerts = getOwner()->getVertexGeometry(); std::vector newGVerts; for (auto& gv : gVerts) { if (gv->getCosmeticTag().empty()) {//keep only non-cv vertices newGVerts.push_back(gv); } } getOwner()->getGeometryObject()->setVertexGeometry(newGVerts); addCosmeticVertexesToGeom(); } //what is the CV's position in the big geometry q /// find the position of a cosmetic vertex with the given tag in the parent view's geometry list int CosmeticExtension::getCVIndex(const std::string& tag) { // Base::Console().message("CE::getCVIndex(%s)\n", tag.c_str()); std::vector gVerts = getOwner()->getVertexGeometry(); std::vector cVerts = CosmeticVertexes.getValues(); int i = 0; for (auto& gv : gVerts) { if (gv->getCosmeticTag() == tag) { return i; } i++; } // Nothing found int base = gVerts.size(); i = 0; for (auto& cv : cVerts) { // Base::Console().message("CE::getCVIndex - cv tag: %s\n", // cv->getTagAsString().c_str()); if (cv->getTagAsString() == tag) { return base + i; } i++; } // Base::Console().message("CE::getCVIndex - returns: %d\n", result); return -1; } /// adds a cosmetic vertex to the property list. does not add to display geometry until dvp executes. /// returns unique CV id. if the pos parameter is in real world coordinates, then invert should be true /// (the default). if pos is in TD geometry or scene coordinates, then it is already inverted, and /// invert should be set to false. std::string CosmeticExtension::addCosmeticVertex(const Base::Vector3d& pos, bool invert) { // Base::Console().message("CEx::addCosmeticVertex(%s)\n", // DrawUtil::formatVector(pos).c_str()); std::vector verts = CosmeticVertexes.getValues(); Base::Vector3d tempPos = pos; if (invert) { tempPos = DrawUtil::invertY(pos); } TechDraw::CosmeticVertex* cv = new TechDraw::CosmeticVertex(tempPos); verts.push_back(cv); CosmeticVertexes.setValues(verts); return cv->getTagAsString(); } /// retrieve a cosmetic vertex by unique id TechDraw::CosmeticVertex* CosmeticExtension::getCosmeticVertex(const std::string& tagString) const { // Base::Console().message("CEx::getCosmeticVertex(%s)\n", tagString.c_str()); const std::vector verts = CosmeticVertexes.getValues(); for (auto& cv: verts) { std::string cvTag = cv->getTagAsString(); if (cvTag == tagString) { return cv; } } return nullptr; } /// retrieve a cosmetic vertex by selection name (Vertex5) /// used when selecting TechDraw::CosmeticVertex* CosmeticExtension::getCosmeticVertexBySelection(const std::string& name) const { // Base::Console().message("CEx::getCVBySelection(%s)\n", name.c_str()); App::DocumentObject* extObj = const_cast (getExtendedObject()); TechDraw::DrawViewPart* dvp = dynamic_cast(extObj); if (!dvp) { return nullptr; } int idx = DrawUtil::getIndexFromName(name); TechDraw::VertexPtr v = dvp->getProjVertexByIndex(idx); if (!v || v->getCosmeticTag().empty()) { return nullptr; } return getCosmeticVertex(v->getCosmeticTag()); } /// retrieve a cosmetic vertex by index (the 5 in Vertex5) TechDraw::CosmeticVertex* CosmeticExtension::getCosmeticVertexBySelection(const int i) const { std::stringstream ss; ss << "Vertex" << i; std::string vName = ss.str(); return getCosmeticVertexBySelection(vName); } /// remove the cosmetic vertex with the given tag from the list property void CosmeticExtension::removeCosmeticVertex(const std::string& delTag) { std::vector cVerts = CosmeticVertexes.getValues(); std::vector newVerts; for (auto& cv: cVerts) { if (cv->getTagAsString() != delTag) { newVerts.push_back(cv); } } CosmeticVertexes.setValues(newVerts); } /// remove the cosmetic vertices with the given tags from the list property void CosmeticExtension::removeCosmeticVertex(const std::vector& delTags) { for (auto& t: delTags) { removeCosmeticVertex(t); } } //********** Cosmetic Edge ***************************************************** // this is never called! /// remove all the cosmetic edges for this view void CosmeticExtension::clearCosmeticEdges() { std::vector cEdges = CosmeticEdges.getValues(); for (auto& edge : cEdges) { delete edge; } std::vector noEdges; CosmeticEdges.setValues(noEdges); } /// add the cosmetic edges to geometry edge list void CosmeticExtension::addCosmeticEdgesToGeom() { const std::vector cEdges = CosmeticEdges.getValues(); for (auto& ce : cEdges) { double scale = getOwner()->getScale(); double rotDegrees = getOwner()->Rotation.getValue(); TechDraw::BaseGeomPtr scaledGeom = ce->scaledAndRotatedGeometry(scale, rotDegrees); if (!scaledGeom) continue; // int iGE = getOwner()->getGeometryObject()->addCosmeticEdge(scaledGeom, ce->getTagAsString()); } } /// add a single cosmetic edge to the geometry edge list int CosmeticExtension::add1CEToGE(const std::string& tag) { TechDraw::CosmeticEdge* ce = getCosmeticEdge(tag); if (!ce) { Base::Console().message("CEx::add1CEToGE 2 - ce %s not found\n", tag.c_str()); return -1; } double scale = getOwner()->getScale(); double rotDegrees = getOwner()->Rotation.getValue(); TechDraw::BaseGeomPtr scaledGeom = ce->scaledAndRotatedGeometry(scale, rotDegrees); int iGE = getOwner()->getGeometryObject()->addCosmeticEdge(scaledGeom, tag); return iGE; } /// update Edge geometry with current CE's void CosmeticExtension::refreshCEGeoms() { std::vector gEdges = getOwner()->getEdgeGeometry(); std::vector oldGEdges; for (auto& ge : gEdges) { if (ge->source() != SourceType::COSMETICEDGE) { oldGEdges.push_back(ge); } } getOwner()->getGeometryObject()->setEdgeGeometry(oldGEdges); addCosmeticEdgesToGeom(); } /// adds a new cosmetic edge to the list property. does not add to display geometry until dvp executes. /// returns unique CE id std::string CosmeticExtension::addCosmeticEdge(Base::Vector3d start, Base::Vector3d end) { std::vector edges = CosmeticEdges.getValues(); TechDraw::CosmeticEdge* ce = new TechDraw::CosmeticEdge(start, end); edges.push_back(ce); CosmeticEdges.setValues(edges); return ce->getTagAsString(); } /// adds a new cosmetic edge to the list property. does not add to display geometry until dvp executes. /// returns unique CE id std::string CosmeticExtension::addCosmeticEdge(TechDraw::BaseGeomPtr bg) { std::vector edges = CosmeticEdges.getValues(); TechDraw::CosmeticEdge* ce = new TechDraw::CosmeticEdge(bg); edges.push_back(ce); CosmeticEdges.setValues(edges); return ce->getTagAsString(); } /// retrieve a CE by unique id TechDraw::CosmeticEdge* CosmeticExtension::getCosmeticEdge(const std::string& tagString) const { const std::vector edges = CosmeticEdges.getValues(); for (auto& ce: edges) { std::string ceTag = ce->getTagAsString(); if (ceTag == tagString) { return ce; } } // None found return nullptr; } /// find the cosmetic edge corresponding to selection name (Edge5) /// used when selecting TechDraw::CosmeticEdge* CosmeticExtension::getCosmeticEdgeBySelection(const std::string& name) const { App::DocumentObject* extObj = const_cast (getExtendedObject()); TechDraw::DrawViewPart* dvp = dynamic_cast(extObj); if (!dvp) { return nullptr; } int idx = DrawUtil::getIndexFromName(name); TechDraw::BaseGeomPtr base = dvp->getGeomByIndex(idx); if (!base || base->getCosmeticTag().empty()) { return nullptr; } return getCosmeticEdge(base->getCosmeticTag()); } /// find the cosmetic edge corresponding to the input parameter (the 5 in Edge5) TechDraw::CosmeticEdge* CosmeticExtension::getCosmeticEdgeBySelection(int i) const { std::stringstream edgeName; edgeName << "Edge" << i; return getCosmeticEdgeBySelection(edgeName.str()); } /// remove the cosmetic edge with the given tag from the list property void CosmeticExtension::removeCosmeticEdge(const std::string& delTag) { std::vector cEdges = CosmeticEdges.getValues(); std::vector newEdges; for (auto& ce: cEdges) { if (ce->getTagAsString() != delTag) { newEdges.push_back(ce); } // delete ce; here leads to a crash. https://github.com/FreeCAD/FreeCAD/issues/24196 // Something(?) is still accessing the edge. Also applies to CosmeticVertex and CenterLine. } CosmeticEdges.setValues(newEdges); } /// remove the cosmetic edges with the given tags from the list property void CosmeticExtension::removeCosmeticEdge(const std::vector& delTags) { std::vector cEdges = CosmeticEdges.getValues(); for (auto& t: delTags) { removeCosmeticEdge(t); } } //********** Center Line ******************************************************* // this is never called! void CosmeticExtension::clearCenterLines() { std::vector cLines = CenterLines.getValues(); for (auto& line : cLines) { delete line; } std::vector noLines; CenterLines.setValues(noLines); } int CosmeticExtension::add1CLToGE(const std::string& tag) { TechDraw::CenterLine* cl = getCenterLine(tag); if (!cl) { return -1; } TechDraw::BaseGeomPtr scaledGeom = cl->scaledAndRotatedGeometry(getOwner()); int iGE = getOwner()->getGeometryObject()->addCenterLine(scaledGeom, tag); return iGE; } //update Edge geometry with current CL's void CosmeticExtension::refreshCLGeoms() { std::vector gEdges = getOwner()->getEdgeGeometry(); std::vector newGEdges; for (auto& ge : gEdges) { if (ge->source() != SourceType::CENTERLINE) { newGEdges.push_back(ge); } } getOwner()->getGeometryObject()->setEdgeGeometry(newGEdges); addCenterLinesToGeom(); } //add the center lines to geometry Edges list void CosmeticExtension::addCenterLinesToGeom() { const std::vector lines = CenterLines.getValues(); for (auto& cl : lines) { TechDraw::BaseGeomPtr scaledGeom = cl->scaledAndRotatedGeometry(getOwner()); if (!scaledGeom) { Base::Console().error("CE::addCenterLinesToGeom - scaledGeometry is null\n"); continue; } // int idx = getOwner()->getGeometryObject()->addCenterLine(scaledGeom, cl->getTagAsString()); } } //returns unique CL id //only adds cl to cllist property. does not add to display geometry until dvp executes. std::string CosmeticExtension::addCenterLine(Base::Vector3d start, Base::Vector3d end) { std::vector cLines = CenterLines.getValues(); TechDraw::CenterLine* cl = new TechDraw::CenterLine(start, end); cLines.push_back(cl); CenterLines.setValues(cLines); return cl->getTagAsString(); } std::string CosmeticExtension::addCenterLine(TechDraw::CenterLine* cl) { std::vector cLines = CenterLines.getValues(); cLines.push_back(cl); CenterLines.setValues(cLines); return cl->getTagAsString(); } std::string CosmeticExtension::addCenterLine(TechDraw::BaseGeomPtr bg) { std::vector cLines = CenterLines.getValues(); TechDraw::CenterLine* cl = new TechDraw::CenterLine(bg); cLines.push_back(cl); CenterLines.setValues(cLines); return cl->getTagAsString(); } //get CL by unique id TechDraw::CenterLine* CosmeticExtension::getCenterLine(const std::string& tagString) const { const std::vector cLines = CenterLines.getValues(); for (auto& cl: cLines) { std::string clTag = cl->getTagAsString(); if (clTag == tagString) { return cl; } } return nullptr; } // find the center line corresponding to selection name (Edge5) // used when selecting TechDraw::CenterLine* CosmeticExtension::getCenterLineBySelection(const std::string& name) const { App::DocumentObject* extObj = const_cast (getExtendedObject()); TechDraw::DrawViewPart* dvp = dynamic_cast(extObj); if (!dvp) { return nullptr; } int idx = DrawUtil::getIndexFromName(name); TechDraw::BaseGeomPtr base = dvp->getGeomByIndex(idx); if (!base || base->getCosmeticTag().empty()) { return nullptr; } return getCenterLine(base->getCosmeticTag()); } //overload for index only TechDraw::CenterLine* CosmeticExtension::getCenterLineBySelection(int i) const { std::stringstream edgeName; edgeName << "Edge" << i; return getCenterLineBySelection(edgeName.str()); } void CosmeticExtension::removeCenterLine(const std::string& delTag) { std::vector cLines = CenterLines.getValues(); std::vector newLines; for (auto& cl: cLines) { if (cl->getTagAsString() != delTag) { newLines.push_back(cl); } } CenterLines.setValues(newLines); } void CosmeticExtension::removeCenterLine(const std::vector& delTags) { for (auto& t: delTags) { removeCenterLine(t); } } //********** Geometry Formats ************************************************** void CosmeticExtension::clearGeomFormats() { // setValues takes care of deletion of old entries as well GeomFormats.setValues({}); } //returns unique GF id //only adds gf to gflist property. does not add to display geometry until dvp repaints. std::string CosmeticExtension::addGeomFormat(TechDraw::GeomFormat* gf) { std::vector formats = GeomFormats.getValues(); TechDraw::GeomFormat* newGF = new TechDraw::GeomFormat(gf); formats.push_back(newGF); GeomFormats.setValues(formats); return newGF->getTagAsString(); } //get GF by unique id TechDraw::GeomFormat* CosmeticExtension::getGeomFormat(const std::string& tagString) const { const std::vector formats = GeomFormats.getValues(); for (auto& gf: formats) { std::string gfTag = gf->getTagAsString(); if (gfTag == tagString) { return gf; } } // Nothing found return nullptr; } // find the GeomFormat for a cosmetic edge corresponding to selection name (Edge5) // used when selecting TechDraw::GeomFormat* CosmeticExtension::getGeomFormatBySelection(const std::string& name) const { App::DocumentObject* extObj = const_cast (getExtendedObject()); TechDraw::DrawViewPart* dvp = dynamic_cast(extObj); if (!dvp) { return nullptr; } int idx = DrawUtil::getIndexFromName(name); const std::vector formats = GeomFormats.getValues(); for (auto& gf: formats) { if (gf->m_geomIndex == idx) { return gf; } } // Nothing found return nullptr; } //overload for index only TechDraw::GeomFormat* CosmeticExtension::getGeomFormatBySelection(int i) const { std::stringstream edgeName; edgeName << "Edge" << i; return getGeomFormatBySelection(edgeName.str()); } void CosmeticExtension::removeGeomFormat(const std::string& delTag) { std::vector cFormats = GeomFormats.getValues(); std::vector newFormats; for (auto& gf: cFormats) { if (gf->getTagAsString() != delTag) { newFormats.push_back(gf); } } GeomFormats.setValues(newFormats); } //================================================================================ PyObject* CosmeticExtension::getExtensionPyObject(void) { if (ExtensionPythonObject.is(Py::_None())){ // ref counter is set to 1 ExtensionPythonObject = Py::Object(new CosmeticExtensionPy(this), true); } return Py::new_reference_to(ExtensionPythonObject); } namespace App { /// @cond DOXERR EXTENSION_PROPERTY_SOURCE_TEMPLATE(TechDraw::CosmeticExtensionPython, TechDraw::CosmeticExtension) /// @endcond // explicit template instantiation template class TechDrawExport ExtensionPythonT; }