/*************************************************************************** * 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 "PreCompiled.h" #ifndef _PreComp_ # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ShapeExtractor.h" #include "DrawUtil.h" using namespace TechDraw; std::vector ShapeExtractor::getShapes2d(const std::vector links) { // Base::Console().Message("SE::getShapes2d()\n"); std::vector shapes2d; if (!prefAdd2d()) { return shapes2d; } for (auto& l:links) { const App::GroupExtension* gex = dynamic_cast(l); if (gex != nullptr) { std::vector objs = gex->Group.getValues(); for (auto& d: objs) { if (is2dObject(d)) { if (d->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { //need to apply global placement here. ??? because 2d shapes (Points so far) //don't get gp from Part::feature::getShape() ???? const Part::Feature* pf = static_cast(d); Part::TopoShape ts = pf->Shape.getShape(); ts.setPlacement(pf->globalPlacement()); shapes2d.push_back(ts.getShape()); } } } } else { if (is2dObject(l)) { if (l->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { //need to apply placement here const Part::Feature* pf = static_cast(l); Part::TopoShape ts = pf->Shape.getShape(); ts.setPlacement(pf->globalPlacement()); shapes2d.push_back(ts.getShape()); } } } } return shapes2d; } TopoDS_Shape ShapeExtractor::getShapes(const std::vector links) { // Base::Console().Message("SE::getShapes() - links in: %d\n", links.size()); TopoDS_Shape result; std::vector sourceShapes; for (auto& l:links) { if (l->getTypeId().isDerivedFrom(App::Link::getClassTypeId())) { App::Link* xLink = dynamic_cast(l); std::vector xShapes = getXShapes(xLink); if (!xShapes.empty()) { sourceShapes.insert(sourceShapes.end(), xShapes.begin(), xShapes.end()); continue; } } else { auto shape = Part::Feature::getShape(l); if(!shape.IsNull()) { // BRepTools::Write(shape, "DVPgetShape.brep"); //debug if (shape.ShapeType() > TopAbs_COMPSOLID) { //simple shape //do we need to apply placement here too?? sourceShapes.push_back(shape); } else { //complex shape std::vector drawable = extractDrawableShapes(shape); if (!drawable.empty()) { sourceShapes.insert(sourceShapes.end(),drawable.begin(),drawable.end()); } } } else { std::vector shapeList = getShapesFromObject(l); sourceShapes.insert(sourceShapes.end(),shapeList.begin(),shapeList.end()); } } } BRep_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); bool found = false; for (auto& s:sourceShapes) { if (s.IsNull()) { continue; //has no shape } found = true; BRepBuilderAPI_Copy BuilderCopy(s); TopoDS_Shape shape = BuilderCopy.Shape(); builder.Add(comp, shape); } //it appears that an empty compound is !IsNull(), so we need to check a different way //if we added anything to the compound. if (!found) { Base::Console().Error("SE::getSourceShapes - source shape is empty!\n"); } else { result = comp; } return result; } std::vector ShapeExtractor::getXShapes(const App::Link* xLink) { // Base::Console().Message("SE::getXShapes(%X) - %s\n", xLink, xLink->getNameInDocument()); std::vector xSourceShapes; if (xLink == nullptr) { return xSourceShapes; } std::vector children = xLink->getLinkedChildren(); Base::Placement linkPlm; if (xLink->hasPlacement()) { linkPlm = xLink->getLinkPlacementProperty()->getValue(); } if (!children.empty()) { for (auto& l:children) { //What to do with LinkGroup??? // if (l->getTypeId().isDerivedFrom(App::LinkGroup::getClassTypeId())) { // Base::Console().Message("SE::getXShapes - found a LinkGroup\n"); // } Base::Placement childPlm; if (l->getTypeId().isDerivedFrom(App::LinkElement::getClassTypeId())) { App::LinkElement* cLinkElem = static_cast(l); if (cLinkElem->hasPlacement()) { childPlm = cLinkElem->getLinkPlacementProperty()->getValue(); } } auto shape = Part::Feature::getShape(l); if(!shape.IsNull()) { Base::Placement netPlm = linkPlm; netPlm *= childPlm; if (xLink->hasPlacement()) { Part::TopoShape ts(shape); ts.setPlacement(netPlm); shape = ts.getShape(); } if (shape.ShapeType() > TopAbs_COMPSOLID) { //simple shape xSourceShapes.push_back(shape); } else { //complex shape std::vector drawable = extractDrawableShapes(shape); if (!drawable.empty()) { xSourceShapes.insert(xSourceShapes.end(),drawable.begin(),drawable.end()); } } } else { Base::Console().Message("SE::getXShapes - no shape from getXShape\n"); } } } else { int depth = 1; //0 is default value, related to recursion of Links??? App::DocumentObject* link = xLink->getLink(depth); if (link != nullptr) { auto shape = Part::Feature::getShape(link); if(!shape.IsNull()) { if (xLink->hasPlacement()) { Part::TopoShape ts(shape); ts.setPlacement(linkPlm); shape = ts.getShape(); } if (shape.ShapeType() > TopAbs_COMPSOLID) { //simple shape xSourceShapes.push_back(shape); } else { //complex shape std::vector drawable = extractDrawableShapes(shape); if (!drawable.empty()) { xSourceShapes.insert(xSourceShapes.end(),drawable.begin(),drawable.end()); } } } } } return xSourceShapes; } std::vector ShapeExtractor::getShapesFromObject(const App::DocumentObject* docObj) { // Base::Console().Message("SE::getShapesFromObject(%s)\n", docObj->getNameInDocument()); std::vector result; const App::GroupExtension* gex = dynamic_cast(docObj); App::Property* gProp = docObj->getPropertyByName("Group"); App::Property* sProp = docObj->getPropertyByName("Shape"); if (docObj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { const Part::Feature* pf = static_cast(docObj); Part::TopoShape ts = pf->Shape.getShape(); ts.setPlacement(pf->globalPlacement()); result.push_back(ts.getShape()); } else if (gex != nullptr) { //is a group extension std::vector objs = gex->Group.getValues(); std::vector shapes; for (auto& d: objs) { shapes = getShapesFromObject(d); if (!shapes.empty()) { result.insert(result.end(),shapes.begin(),shapes.end()); } } //the next 2 bits are mostly for Arch module objects } else if (gProp != nullptr) { //has a Group property App::PropertyLinkList* list = dynamic_cast(gProp); if (list != nullptr) { std::vector objs = list->getValues(); std::vector shapes; for (auto& d: objs) { shapes = getShapesFromObject(d); if (!shapes.empty()) { result.insert(result.end(),shapes.begin(),shapes.end()); } } } else { Base::Console().Log("SE::getShapesFromObject - Group is not a PropertyLinkList!\n"); } } else if (sProp != nullptr) { //has a Shape property Part::PropertyPartShape* shape = dynamic_cast(sProp); if (shape != nullptr) { TopoDS_Shape occShape = shape->getValue(); result.push_back(occShape); } else { Base::Console().Log("SE::getShapesFromObject - Shape is not a PropertyPartShape!\n"); } } return result; } TopoDS_Shape ShapeExtractor::getShapesFused(const std::vector links) { // Base::Console().Message("SE::getShapesFused()\n"); TopoDS_Shape baseShape = getShapes(links); if (!baseShape.IsNull()) { TopoDS_Iterator it(baseShape); TopoDS_Shape fusedShape = it.Value(); it.Next(); for (; it.More(); it.Next()) { const TopoDS_Shape& aChild = it.Value(); BRepAlgoAPI_Fuse mkFuse(fusedShape, aChild); // Let's check if the fusion has been successful if (!mkFuse.IsDone()) { Base::Console().Error("SE - Fusion failed\n"); return baseShape; } fusedShape = mkFuse.Shape(); } baseShape = fusedShape; } return baseShape; } std::vector ShapeExtractor::extractDrawableShapes(const TopoDS_Shape shapeIn) { // Base::Console().Message("SE::extractDrawableShapes()\n"); std::vector result; std::vector extShapes; //extracted Shapes (solids mostly) std::vector extEdges; //extracted loose Edges if (shapeIn.ShapeType() == TopAbs_COMPOUND) { //Compound is most general shape type //getSolids from Compound TopExp_Explorer expSolid(shapeIn, TopAbs_SOLID); for (int i = 1; expSolid.More(); expSolid.Next(), i++) { TopoDS_Solid s = TopoDS::Solid(expSolid.Current()); if (!s.IsNull()) { extShapes.push_back(s); } } //get edges not part of a solid //???? should this look for Faces(Wires?) before Edges? TopExp_Explorer expEdge(shapeIn, TopAbs_EDGE, TopAbs_SOLID); for (int i = 1; expEdge.More(); expEdge.Next(), i++) { TopoDS_Shape s = expEdge.Current(); if (!s.IsNull()) { extEdges.push_back(s); } } } else if (shapeIn.ShapeType() == TopAbs_COMPSOLID) { //get Solids from compSolid TopExp_Explorer expSolid(shapeIn, TopAbs_SOLID); for (int i = 1; expSolid.More(); expSolid.Next(), i++) { TopoDS_Solid s = TopoDS::Solid(expSolid.Current()); if (!s.IsNull()) { extShapes.push_back(s); } } //vs using 2d geom as construction geom? //get edges not part of a solid //???? should this look for Faces(Wires?) before Edges? TopExp_Explorer expEdge(shapeIn, TopAbs_EDGE, TopAbs_SOLID); for (int i = 1; expEdge.More(); expEdge.Next(), i++) { TopoDS_Shape s = expEdge.Current(); if (!s.IsNull()) { extEdges.push_back(s); } } } else { //not a Compound or a CompSolid just push_back shape_In) extShapes.push_back(shapeIn); } result = extShapes; if (!extEdges.empty()) { result.insert(std::end(result), std::begin(extEdges), std::end(extEdges)); } return result; } bool ShapeExtractor::is2dObject(App::DocumentObject* obj) { bool result = false; if (isEdgeType(obj) || isPointType(obj)) { result = true; } return result; } //skip edges for now. bool ShapeExtractor::isEdgeType(App::DocumentObject* obj) { (void) obj; bool result = false; // Base::Type t = obj->getTypeId(); // if (t.isDerivedFrom(Part::Line::getClassTypeId()) ) { // result = true; // } else if (t.isDerivedFrom(Part::Circle::getClassTypeId())) { // result = true; // } else if (t.isDerivedFrom(Part::Ellipse::getClassTypeId())) { // result = true; // } else if (t.isDerivedFrom(Part::RegularPolygon::getClassTypeId())) { // result = true; // } return result; } bool ShapeExtractor::isPointType(App::DocumentObject* obj) { bool result = false; Base::Type t = obj->getTypeId(); if (t.isDerivedFrom(Part::Vertex::getClassTypeId())) { result = true; } else if (isDraftPoint(obj)) { result = true; } return result; } bool ShapeExtractor::isDraftPoint(App::DocumentObject* obj) { // Base::Console().Message("SE::isDraftPoint()\n"); bool result = false; //if the docObj doesn't have a Proxy property, it definitely isn't a Draft point App::PropertyPythonObject* proxy = dynamic_cast(obj->getPropertyByName("Proxy")); if (proxy != nullptr) { std::string pp = proxy->toString(); // Base::Console().Message("SE::isDraftPoint - pp: %s\n", pp.c_str()); if (pp.find("Point") != std::string::npos) { result = true; } } return result; } Base::Vector3d ShapeExtractor::getLocation3dFromFeat(App::DocumentObject* obj) { // Base::Console().Message("SE::getLocation3dFromFeat()\n"); Base::Vector3d result(0.0, 0.0, 0.0); if (!isPointType(obj)) { return result; } // if (isDraftPoint(obj) { // //Draft Points are not necc. Part::PartFeature?? // //if Draft option "use part primitives" is not set are Draft points still PartFeature? Part::Feature* pf = dynamic_cast(obj); if (pf != nullptr) { Part::TopoShape pts = pf->Shape.getShape(); pts.setPlacement(pf->globalPlacement()); TopoDS_Shape ts = pts.getShape(); if (ts.ShapeType() == TopAbs_VERTEX) { TopoDS_Vertex v = TopoDS::Vertex(ts); result = DrawUtil::vertex2Vector(v); } } // Base::Console().Message("SE::getLocation3dFromFeat - returns: %s\n", // DrawUtil::formatVector(result).c_str()); return result; } bool ShapeExtractor::prefAdd2d(void) { Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/General"); bool result = hGrp->GetBool("ShowLoose2d", false); return result; }