From 42bf92ad12f1a12cf1517d945dda7ab5284bfc21 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 18 Jul 2024 21:12:29 -0400 Subject: [PATCH 1/5] Toponaming: Transfer in ExportGeo and associated --- src/Mod/Sketcher/App/SketchObject.cpp | 2426 ++++++++++++++++++++++--- src/Mod/Sketcher/App/SketchObject.h | 145 +- 2 files changed, 2302 insertions(+), 269 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 103828376153..ff8705c54b2b 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -27,13 +27,24 @@ #include #include +#include #include #include +#include +#include #include #include +#include +#include +#include #include #include +#include +#include +#include +#include #include +#include #include #include #include @@ -42,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -58,9 +71,17 @@ #include #endif + +#include + +#include +#include + +#include #include #include #include +#include #include #include @@ -79,6 +100,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +112,7 @@ #include #include +#include #include @@ -125,6 +148,30 @@ SketchObject::SketchObject() "Sketch", (App::PropertyType)(App::Prop_Output | App::Prop_ReadOnly | App::Prop_Hidden), "Sketch is fully constrained"); + ADD_PROPERTY_TYPE(Exports, + (nullptr), + "Sketch", + (App::PropertyType)(App::Prop_Hidden),"Sketch export geometry"); + ADD_PROPERTY_TYPE(ExternalGeo, + (nullptr), + "Sketch", + (App::PropertyType)(App::Prop_Hidden),"Sketch external geometry"); + ADD_PROPERTY_TYPE(ArcFitTolerance, + (0.0), + "Sketch", + (App::PropertyType)(App::Prop_None), + "Tolerance for fitting arcs of projected external geometry"); + // ADD_PROPERTY_TYPE(ExternalBSplineMaxDegree, + // (0), + // "Sketch", + // (App::Prop_None), + // "Maximum degree of imported external BSpline. Zero to disable simplification"); + // ADD_PROPERTY_TYPE(ExternalBSplineTolerance, + // (0.0), + // "Sketch", + // (App::Prop_None), + // "Tolerance for simplifying imported external BSpline"); + geoLastId = 0; ADD_PROPERTY(InternalShape, (Part::TopoShape())); @@ -139,21 +186,24 @@ SketchObject::SketchObject() allowOtherBody = true; allowUnaligned = true; - for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); - ++it) - if (*it) - delete *it; - ExternalGeo.clear(); - auto HLine = GeometryTypedFacade::getTypedFacade(); - auto VLine = GeometryTypedFacade::getTypedFacade(); - HLine->getTypedGeometry()->setPoints(Base::Vector3d(0, 0, 0), Base::Vector3d(1, 0, 0)); - VLine->getTypedGeometry()->setPoints(Base::Vector3d(0, 0, 0), Base::Vector3d(0, 1, 0)); - HLine->setConstruction(true); - VLine->setConstruction(true); - ExternalGeo.push_back(HLine->getGeometry()); - ExternalGeo.push_back(VLine->getGeometry()); - HLine->setOwner(false);// we have transferred the ownership to ExternalGeo - VLine->setOwner(false);// we have transferred the ownership to ExternalGeo +// for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); +// ++it) +// if (*it) +// delete *it; +// ExternalGeo.clear(); +// auto HLine = GeometryTypedFacade::getTypedFacade(); +// auto VLine = GeometryTypedFacade::getTypedFacade(); +// HLine->getTypedGeometry()->setPoints(Base::Vector3d(0, 0, 0), Base::Vector3d(1, 0, 0)); +// VLine->getTypedGeometry()->setPoints(Base::Vector3d(0, 0, 0), Base::Vector3d(0, 1, 0)); +// HLine->setConstruction(true); +// VLine->setConstruction(true); +// ExternalGeo.push_back(HLine->getGeometry()); +// ExternalGeo.push_back(VLine->getGeometry()); +// HLine->setOwner(false);// we have transferred the ownership to ExternalGeo +// VLine->setOwner(false);// we have transferred the ownership to ExternalGeo + + initExternalGeo(); + rebuildVertexIndex(); lastDoF = 0; @@ -188,11 +238,11 @@ SketchObject::SketchObject() SketchObject::~SketchObject() { - for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); - ++it) - if (*it) - delete *it; - ExternalGeo.clear(); +// for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); +// ++it) +// if (*it) +// delete *it; +// ExternalGeo.clear(); delete analyser; } @@ -201,10 +251,30 @@ void SketchObject::setupObject() { ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher"); + ArcFitTolerance.setValue(hGrpp->GetFloat("ArcFitTolerance", Precision::Confusion()*10.0)); +// ExternalBSplineMaxDegree.setValue(hGrpp->GetInt("ExternalBSplineMaxDegree", 5)); +// ExternalBSplineTolerance.setValue(hGrpp->GetFloat("ExternalBSplineTolerance", 1e-4)); MakeInternals.setValue(hGrpp->GetBool("MakeInternals", false)); inherited::setupObject(); } +void SketchObject::initExternalGeo() { + std::vector geos; + auto HLine = GeometryTypedFacade::getTypedFacade(); + auto VLine = GeometryTypedFacade::getTypedFacade(); + HLine->getTypedGeometry()->setPoints(Base::Vector3d(0,0,0),Base::Vector3d(1,0,0)); + VLine->getTypedGeometry()->setPoints(Base::Vector3d(0,0,0),Base::Vector3d(0,1,0)); + HLine->setConstruction(true); + HLine->setId(-1); + VLine->setConstruction(true); + VLine->setId(-2); + geos.push_back(HLine->getGeometry()); + geos.push_back(VLine->getGeometry()); + HLine->setOwner(false); // we have transferred the ownership to ExternalGeo + VLine->setOwner(false); // we have transferred the ownership to ExternalGeo + ExternalGeo.setValues(std::move(geos)); +} + short SketchObject::mustExecute() const { if (Geometry.isTouched()) @@ -213,6 +283,8 @@ short SketchObject::mustExecute() const return 1; if (ExternalGeometry.isTouched()) return 1; + if (ExternalGeo.isTouched()) + return 1; return Part2DObject::mustExecute(); } @@ -274,8 +346,16 @@ App::DocumentObjectExecReturn* SketchObject::execute() return App::DocumentObject::StdReturn; } -void SketchObject::buildShape() +static bool inline checkSmallEdge(const Part::TopoShape &s) { + if (s.shapeType() != TopAbs_EDGE) + return false; + BRepAdaptor_Curve adapt(TopoDS::Edge(s.getShape())); + return GCPnts_AbscissaPoint::Length(adapt, Precision::Confusion()) <= Precision::Confusion(); +} + +void SketchObject::buildShape() { + // Shape.setValue(solvedSketch.toShape()); // We use the following instead to map element names @@ -298,13 +378,15 @@ void SketchObject::buildShape() vertex.resetElementMap(std::make_shared()); } vertex.setElementName(Data::IndexedName::fromConst("Vertex", 1), - Data::MappedName::fromRawData(name.c_str()), 0L); + Data::MappedName::fromRawData(name.c_str()),0L); vertices.push_back(vertex); vertices.back().copyElementMap(vertex, Part::OpCodes::Sketch); - } - else { + } else { auto indexedName = Data::IndexedName::fromConst("Edge", geoId); shapes.push_back(getEdge(geo,convertSubName(indexedName, false).c_str())); + if (checkSmallEdge(shapes.back())) { + FC_WARN("Edge too small: " << indexedName); + } } #else @@ -320,13 +402,16 @@ void SketchObject::buildShape() } // FIXME: Commented since ExternalGeometryFacade is not added - for(size_t i=2;itestFlag(ExternalGeometryExtension::Defining)) continue; - shapes.push_back(getEdge(geo, convertSubName( - Data::IndexedName::fromConst("ExternalEdge", i-1), false).c_str())); + auto indexedName = Data::IndexedName::fromConst("ExternalEdge", i-1); + shapes.push_back(getEdge(geo, convertSubName(indexedName, false).c_str())); + if (checkSmallEdge(shapes.back())) { + FC_WARN("Edge too small: " << indexedName); + } } internalElementMap.clear(); @@ -547,21 +632,238 @@ int SketchObject::solve(bool updateGeoAfterSolving /*=true*/) if (err == 0 && updateGeoAfterSolving) { // set the newly solved geometry std::vector geomlist = solvedSketch.extractGeometry(); - Geometry.setValues(geomlist); - for (std::vector::iterator it = geomlist.begin(); it != geomlist.end(); - ++it) - if (*it) - delete *it; +// Geometry.setValues(geomlist); +// for (std::vector::iterator it = geomlist.begin(); it != geomlist.end(); +// ++it) +// if (*it) +// delete *it; + Part::PropertyGeometryList tmp; + tmp.setValues(std::move(geomlist)); + // Only set values if there is actual changes + if (!Geometry.isSame(tmp)) + Geometry.moveValues(std::move(tmp)); } else if (err < 0) { // if solver failed, invalid constraints were likely added before solving // (see solve in addConstraint), so solver information is definitely invalid. - this->Constraints.touch(); + // + // Update: ViewProviderSketch shall now rely on the signalSolverUpdate below for update + // this->Constraints.touch(); } + signalSolverUpdate(); + return err; } +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +BOOST_GEOMETRY_REGISTER_POINT_3D( + Base::Vector3d,double,bg::cs::cartesian,x,y,z) + +class SketchObject::GeoHistory { +public: + typedef bgi::linear<16> Parameters; + + typedef std::set IdSet; + typedef std::pair IdSets; + typedef std::list AdjList; + + //associate a geo with connected ones on both points + typedef std::map AdjMap; + + // maps start/end points to all existing geo to query and update adjacencies + typedef std::pair Value; + + AdjList adjlist; + AdjMap adjmap; + bgi::rtree rtree; + + AdjList::iterator find(const Base::Vector3d &pt,bool strict=true){ + std::vector ret; + rtree.query(bgi::nearest(pt,1),std::back_inserter(ret)); + if(ret.size()) { + // NOTE: we are using square distance here, the 1e-6 threshold is + // very forgiving. We should have used Precision::SquareConfisuion(), + // which is 1e-14. However, there is a problem with current + // commandGeoCreate. They create new geometry with initial point of + // the exact mouse position, instead of the pre-selected point + // position, and rely on auto constraint to snap in the new + // geometry. So, we cannot use a very strict threshold here. + double tol = strict?Precision::SquareConfusion()*10:1e-6; + double d = Base::DistanceP2(ret[0].first,pt); + if(dinsert(id); + } + + void finishUpdate(const std::map &geomap) { + IdSet oldset; + for(auto &idset : adjlist) { + oldset.clear(); + for(long _id : idset) { + long id = abs(_id); + auto &v = adjmap[id]; + auto &adj = _id>0?v.first:v.second; + for(auto it=adj.begin(),itNext=it;it!=adj.end();it=itNext) { + ++itNext; + long other = *it; + if(geomap.find(other) == geomap.end()) { + // remember those deleted id's + oldset.insert(other); + FC_TRACE("insert old " << id << ", " << other); + } else if(idset.find(other)==idset.end()) { + // delete any existing id's that are no longer in the adj list + FC_TRACE("erase " << id << ", " << other); + adj.erase(it); + } + } + // now merge the current ones + for(long _id2 : idset) { + long id2 = abs(_id2); + if(id!=id2) { + adj.insert(id2); + FC_TRACE("insert new " << id << ", " << id2); + } + } + } + // now reset the adjacency list with only those deleted id's, + // because the whole purpose of this history is to try to reuse + // deleted id. + idset.swap(oldset); + } + } + + AdjList::iterator end() { + return adjlist.end(); + } + + size_t size() { + return rtree.size(); + } +}; + +void SketchObject::updateGeoHistory() { + if(!geoHistoryLevel) return; + + if(!geoHistory) + geoHistory.reset(new GeoHistory); + + FC_TIME_INIT(t); + const auto &geos = getInternalGeometry(); + geoHistory->clear(); + for(int i=0;i<(int)geos.size();++i) { + auto geo = geos[i]; + auto pstart = getPoint(geo,PointPos::start); + auto pend = getPoint(geo,PointPos::end); + int id = GeometryFacade::getId(geo); + geoHistory->update(pstart,id); + if(pstart!=pend) + geoHistory->update(pend,-id); + } + geoHistory->finishUpdate(geoMap); + FC_TIME_LOG(t,"update geometry history (" << geoHistory->size() << ", " << geoMap.size()<<')'); +} + +void SketchObject::generateId(Part::Geometry *geo) { + if(!geoHistoryLevel) { + GeometryFacade::setId(geo, ++geoLastId); + geoMap[GeometryFacade::getId(geo)] = (long)Geometry.getSize(); + return; + } + + if(!geoHistory) + updateGeoHistory(); + + // Search geo history to see if the start point and end point belongs to + // some deleted geometries. Prefer matching both start and end point. If + // can't then try start and then end. Generate new id if none is found. + auto pstart = getPoint(geo,PointPos::start); + auto it = geoHistory->find(pstart,false); + auto pend = getPoint(geo,PointPos::end); + auto it2 = it; + if(pstart!=pend) { + it2 = geoHistory->find(pend,false); + if(it2 == geoHistory->end()) + it2 = it; + } + long newId = -1; + std::vector found; + + if(geoHistoryLevel<=1 && (it==geoHistory->end() || it2==it)) { + // level<=1 means we only reuse id if both start and end matches + newId = ++geoLastId; + goto END; + } + + if(it!=geoHistory->end()) { + for(long id : *it) { + if(geoMap.find(id)==geoMap.end()) { + if(it2 == it) { + newId = id; + goto END; + } + found.push_back(id); + }else + FC_TRACE("ignore " << id); + } + } + if(it2!=it) { + if(found.empty()) { + // no candidate exists + for(long id : *it2) { + if(geoMap.find(id)==geoMap.end()) { + newId = id; + goto END; + } + FC_TRACE("ignore " << id); + } + }else{ + // already some candidate exists, search for matching of both + // points + for(long id : found) { + if(it2->find(id)!=it2->end()) { + newId = id; + goto END; + } + FC_TRACE("ignore " << id); + } + } + } + if(found.size()) { + FC_TRACE("found " << found.front()); + newId = found.front(); + }else + newId = ++geoLastId; +END: + GeometryFacade::setId(geo, newId); + geoMap[newId] = (long)Geometry.getSize(); +} + int SketchObject::setDatum(int ConstrId, double Datum) { // no need to check input data validity as this is an sketchobject managed operation. @@ -973,6 +1275,10 @@ int SketchObject::setVirtualSpace(int ConstrId, bool isinvirtualspace) this->Constraints.setValues(std::move(newVals)); + // Solver didn't actually update, but we need this to inform view provider + // to redraw + signalSolverUpdate(); + return 0; } @@ -1004,6 +1310,10 @@ int SketchObject::setVirtualSpace(std::vector constrIds, bool isinvirtualsp this->Constraints.setValues(std::move(newVals)); + // Solver didn't actually update, but we need this to inform view provider + // to redraw + signalSolverUpdate(); + return 0; } @@ -1241,6 +1551,7 @@ void SketchObject::acceptGeometry() { Constraints.acceptGeometry(getCompleteGeometry()); rebuildVertexIndex(); + signalElementsChanged(); } bool SketchObject::isSupportedGeometry(const Part::Geometry* geo) const @@ -1295,6 +1606,7 @@ int SketchObject::addGeometry(const std::vector& geoList, newVals.reserve(newVals.size() + geoList.size()); for (auto& v : geoList) { Part::Geometry* copy = v->copy(); + generateId(copy); if (copy->is()) { // creation mode for points is always construction not to @@ -1333,6 +1645,7 @@ int SketchObject::addGeometry(std::unique_ptr newgeo, bool const std::vector newVals(vals); auto* geoNew = newgeo.release(); + generateId(geoNew); if (geoNew->is()) { // creation mode for points is always construction not to @@ -1354,6 +1667,12 @@ int SketchObject::addGeometry(std::unique_ptr newgeo, bool const int SketchObject::delGeometry(int GeoId, bool deleteinternalgeo) { + if (GeoId < 0) { + if(GeoId > GeoEnum::RefExt) + return -1; + return delExternal(-GeoId-1); + } + // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); @@ -1723,6 +2042,9 @@ int SketchObject::addCopyOfConstraints(const SketchObject& orig) } } + if (noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + return this->Constraints.getSize() - 1; } @@ -2635,6 +2957,17 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, oc2pf.x, oc2pf.y, oc2pf.z); + + /*auto printoffsetcurve = [](Part::GeomOffsetCurve *c) { + + for(double param = c->getFirstParameter(); param < c->getLastParameter(); param = param + + (c->getLastParameter()-c->getFirstParameter())/10) Base::Console().Log("\n%f: + (%f,%f,0)\n", param, c->pointAtParameter(param).x,c->pointAtParameter(param).y); + + }; + + printoffsetcurve(ocurve1); + printoffsetcurve(ocurve2);*/ #endif // Next we calculate the intersection of offset curves to get the center of the fillet @@ -3423,6 +3756,7 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) auto newVals(vals); newVals[GeoId] = newVals[GeoId]->clone(); newVals.push_back(newVals[GeoId]->clone()); + generateId(newVals.back()); int newGeoId = newVals.size() - 1; chooseCurveForPointOnObject(GeoId, firstParam, point1Param, newGeoId, point2Param, lastParam, isBSpline); @@ -3481,10 +3815,8 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) Sketcher::PointPos::mid); } - if (isNonPeriodicBSpline) { + if (isNonPeriodicBSpline) exposeInternalGeometry(GeoId); - exposeInternalGeometry(newGeoId); - } // if we do not have a recompute, the sketch must be solved to update the DoF of the // solver @@ -3616,6 +3948,7 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) geoNew = std::move(bspline); } + GeometryFacade::setId(geoNew.get(), GeometryFacade::getId(geo)); this->Geometry.set1Value(GeoId, std::move(geoNew)); //****** Step B.2 (4) => Constraint end points ******// @@ -4417,9 +4750,19 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); + const std::vector& geovals = getInternalGeometry(); + std::vector newgeoVals(geovals); + const std::vector& constrvals = this->Constraints.getValues(); std::vector newconstrVals(constrvals); + newgeoVals.reserve(geovals.size() + geoIdList.size()); + + int cgeoid = getHighestCurveIndex() + 1; + + std::map geoIdMap; + std::map isStartEndInverted; + // Find out if reference is aligned with V or H axis, // if so we can keep Vertical and Horizontal constraints in the mirrored geometry. bool refIsLine = refPosId == Sketcher::PointPos::none; @@ -4437,9 +4780,9 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, } // add the geometry - std::map geoIdMap; - std::map isStartEndInverted; - std::vector newgeoVals(getInternalGeometry()); +// std::map geoIdMap; +// std::map isStartEndInverted; +// std::vector newgeoVals(getInternalGeometry()); std::vector symmetricVals = getSymmetric(geoIdList, geoIdMap, isStartEndInverted, refGeoId, refPosId); newgeoVals.insert(newgeoVals.end(), symmetricVals.begin(), symmetricVals.end()); @@ -4804,6 +5147,7 @@ std::vector SketchObject::getSymmetric(const std::vector& auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); + // double df= geosymaoe->getFocal(); Base::Vector3d f1 = geosymaoe->getFocus(); Base::Vector3d sf1 = @@ -4998,6 +5342,7 @@ std::vector SketchObject::getSymmetric(const std::vector& geosymbsp->setPoles(poles); + // isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { auto* geosympoint = static_cast(geosym); @@ -5153,9 +5498,10 @@ int SketchObject::addCopy(const std::vector& geoIdList, const Base::Vector3 // We have already cloned all geometry and constraints, we only need a copy if not // moving - if (!moveonly) + if (!moveonly) { geocopy = geo->copy(); - else + generateId(geocopy); + } else geocopy = newgeoVals[*it]; // Handle Geometry @@ -5395,6 +5741,7 @@ int SketchObject::addCopy(const std::vector& geoIdList, const Base::Vector3 constrline->setPoints(sp, ep); GeometryFacade::setConstruction(constrline, true); + generateId(constrline); newgeoVals.push_back(constrline); Constraint* constNew; @@ -6531,7 +6878,8 @@ bool SketchObject::convertToNURBS(int GeoId) Base::StateLocker lock(managedoperation, true); if (GeoId > getHighestCurveIndex() - || (GeoId < 0 && -GeoId > static_cast(ExternalGeo.size())) || GeoId == -1 +// || (GeoId < 0 && -GeoId > static_cast(ExternalGeo.size())) || GeoId == -1 + || (GeoId < 0 && -GeoId > static_cast(ExternalGeo.getSize())) || GeoId == -1 || GeoId == -2) return false; @@ -6570,10 +6918,12 @@ bool SketchObject::convertToNURBS(int GeoId) if (GeoId < 0) {// external geometry newVals.push_back(bspline); + generateId(bspline); } else {// normal geometry newVals[GeoId] = bspline; + GeometryFacade::copyId(geo, bspline); const std::vector& cvals = Constraints.getValues(); @@ -6645,6 +6995,7 @@ bool SketchObject::increaseBSplineDegree(int GeoId, int degreeincrement /*= 1*/) std::vector newVals(vals); + GeometryFacade::copyId(geo, bspline.get()); newVals[GeoId] = bspline.release(); // AcceptGeometry called from onChanged @@ -6713,7 +7064,7 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m if (GeoId < 0 || GeoId > getHighestCurveIndex()) THROWMT(Base::ValueError, - QT_TRANSLATE_NOOP("Exceptions", "B-spline Geometry Index (GeoID) is out of bounds.")) + QT_TRANSLATE_NOOP("Exceptions", "BSpline Geometry Index (GeoID) is out of bounds.")) if (multiplicityincr == 0)// no change in multiplicity THROWMT( @@ -6725,7 +7076,7 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m if (geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) THROWMT(Base::TypeError, QT_TRANSLATE_NOOP("Exceptions", - "The Geometry Index (GeoId) provided is not a B-spline.")) + "The Geometry Index (GeoId) provided is not a B-spline curve.")) const Part::GeomBSplineCurve* bsp = static_cast(geo); @@ -6865,6 +7216,7 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m std::vector newVals(vals); + GeometryFacade::copyId(geo, bspline.get()); newVals[GeoId] = bspline.release(); // Block acceptGeometry in OnChanged to avoid unnecessary checks and updates @@ -6897,7 +7249,7 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity) if (GeoId < 0 || GeoId > getHighestCurveIndex()) THROWMT( Base::ValueError, - QT_TRANSLATE_NOOP("Exceptions", "B-spline Geometry Index (GeoID) is out of bounds.")); + QT_TRANSLATE_NOOP("Exceptions", "BSpline Geometry Index (GeoID) is out of bounds.")); if (multiplicity == 0) THROWMT(Base::ValueError, @@ -6908,7 +7260,7 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity) if (geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) THROWMT(Base::TypeError, QT_TRANSLATE_NOOP("Exceptions", - "The Geometry Index (GeoId) provided is not a B-spline.")); + "The Geometry Index (GeoId) provided is not a B-spline curve.")); const Part::GeomBSplineCurve* bsp = static_cast(geo); @@ -6925,7 +7277,7 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity) if (param > lastParam || param < firstParam) THROWMT(Base::ValueError, QT_TRANSLATE_NOOP("Exceptions", - "Knot cannot be inserted outside the B-spline parameter range.")); + "Knot cannot be inserted outside the BSpline parameter range.")); std::unique_ptr bspline; @@ -7094,6 +7446,43 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction) newVals.reserve(vals.size() + svals.size()); newcVals.reserve(cvals.size() + scvals.size()); + std::map extMap; + if (psObj->ExternalGeo.getSize() > 1) { + int i = -1; + auto geos = this->ExternalGeo.getValues(); + std::string myName(this->getNameInDocument()); + myName += "."; + for (const auto &geo : psObj->ExternalGeo.getValues()) { + if (++i < 2) // skip h/v axes + continue; + else { + auto egf = ExternalGeometryFacade::getFacade(geo); + const auto &ref = egf->getRef(); + if (boost::starts_with(ref, myName)) { + int geoId; + PointPos posId; + if (this->geoIdFromShapeType(ref.c_str()+myName.size(), geoId, posId)) { + extMap[-i-1] = geoId; + continue; + } + } + } + auto copy = geo->copy(); + auto egf = ExternalGeometryFacade::getFacade(copy); + egf->setId(++geoLastId); + if (!egf->getRef().empty()) { + auto &refs = this->externalGeoRefMap[egf->getRef()]; + refs.push_back(geoLastId); + } + this->externalGeoMap[geoLastId] = (int)geos.size(); + geos.push_back(copy); + extMap[-i-1] = -(int)geos.size(); + } + Base::ObjectStatusLocker + guard(App::Property::User3, &this->ExternalGeo); + this->ExternalGeo.setValues(std::move(geos)); + } + if (psObj->ExternalGeometry.getSize() > 0) { std::vector Objects = ExternalGeometry.getValues(); std::vector SubElements = ExternalGeometry.getSubValues(); @@ -7148,6 +7537,7 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction) for (std::vector::const_iterator it = svals.begin(); it != svals.end(); ++it) { Part::Geometry* geoNew = (*it)->copy(); + generateId(geoNew); if (construction && geoNew->getTypeId() != Part::GeomPoint::getClassTypeId()) { GeometryFacade::setConstruction(geoNew, true); } @@ -7221,10 +7611,22 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction) } } + // We shall solve in all cases, because recompute may fail, and leave the + // sketch in an inconsistent state. A concrete example. If the copied sketch + // has broken external geometry, its recomputation will fail. And because we + // use expression for copied constraint to add dependency to the copied + // sketch, this sketch will not be recomputed (because its dependency fails + // to recompute). +#if 0 + if (noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver +#endif + solve(); + + return svals.size(); } -int SketchObject::addExternal(App::DocumentObject* Obj, const char* SubName) +int SketchObject::addExternal(App::DocumentObject *Obj, const char* SubName, bool defining, bool intersection) { // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); @@ -7233,6 +7635,58 @@ int SketchObject::addExternal(App::DocumentObject* Obj, const char* SubName) if (!isExternalAllowed(Obj->getDocument(), Obj)) return -1; + auto wholeShape = Part::Feature::getTopoShape(Obj); + auto shape = wholeShape.getSubTopoShape(SubName, /*silent*/true); + TopAbs_ShapeEnum shapeType = TopAbs_SHAPE; + if (shape.shapeType(/*silent*/true) != TopAbs_FACE) { + if (shape.hasSubShape(TopAbs_FACE)) + shapeType = TopAbs_FACE; + else if (shape.shapeType(/*silent*/true) != TopAbs_EDGE + && shape.hasSubShape(TopAbs_EDGE)) + shapeType = TopAbs_EDGE; + } + + if (shapeType != TopAbs_SHAPE) { + std::string element = Part::TopoShape::shapeName(shapeType); + std::size_t elementNameSize = element.size(); + int geometryCount = ExternalGeometry.getSize(); + + gp_Pln sketchPlane; + if (intersection) { + Base::Placement Plm = Placement.getValue(); + Base::Vector3d Pos = Plm.getPosition(); + Base::Rotation Rot = Plm.getRotation(); + Base::Vector3d dN(0,0,1); + Rot.multVec(dN,dN); + Base::Vector3d dX(1,0,0); + Rot.multVec(dX,dX); + gp_Ax3 sketchAx3(gp_Pnt(Pos.x,Pos.y,Pos.z), + gp_Dir(dN.x,dN.y,dN.z), + gp_Dir(dX.x,dX.y,dX.z)); + sketchPlane.SetPosition(sketchAx3); + } + for (const auto &subShape : shape.getSubShapes(shapeType)) { + int idx = wholeShape.findShape(subShape); + if (idx == 0) + continue; + if (intersection) { + try { + BRepAlgoAPI_Section maker(subShape, sketchPlane); + if (!maker.IsDone() || maker.Shape().IsNull()) + continue; + } catch (Standard_Failure &) { + continue; + } + } + element += std::to_string(idx); + addExternal(Obj, element.c_str(), defining, intersection); + element.resize(elementNameSize); + } + if (ExternalGeometry.getSize() == geometryCount) + return -1; + return geometryCount; + } + // get the actual lists of the externals std::vector Objects = ExternalGeometry.getValues(); std::vector SubElements = ExternalGeometry.getSubValues(); @@ -7261,7 +7715,8 @@ int SketchObject::addExternal(App::DocumentObject* Obj, const char* SubName) // set the Link list. ExternalGeometry.setValues(Objects, SubElements); try { - rebuildExternalGeometry(); +// rebuildExternalGeometry(); + rebuildExternalGeometry(defining, intersection); } catch (const Base::Exception& e) { Base::Console().Error("%s\n", e.what()); @@ -7342,8 +7797,125 @@ int SketchObject::delExternal(int ExtGeoId) return 0; } +void SketchObject::delExternalPrivate(const std::set &ids, bool removeRef) { + + Base::StateLocker lock(managedoperation, true); // no need to check input data validity as this is an sketchobject managed operation. + + std::set refs; + // Must sort in reverse order so as to delete geo from back to front to + // avoid index change + std::set> geoIds; + + for(auto id : ids) { + auto it = externalGeoMap.find(id); + if(it == externalGeoMap.end()) + continue; + + auto egf = ExternalGeometryFacade::getFacade(ExternalGeo[it->second]); + if(removeRef && egf->getRef().size()) + refs.insert(egf->getRef()); + geoIds.insert(-it->second-1); + } + + if(geoIds.empty()) + return; + + std::vector< Constraint * > newConstraints; + for(auto cstr : Constraints.getValues()) { + if(!geoIds.count(cstr->First) && + (cstr->Second==GeoEnum::GeoUndef || !geoIds.count(cstr->Second)) && + (cstr->Third==GeoEnum::GeoUndef || !geoIds.count(cstr->Third))) + { + bool cloned = false; + int offset = 0; + for(auto GeoId : geoIds) { + GeoId += offset++; + bool done = true; + if (cstr->First < GeoId && cstr->First != GeoEnum::GeoUndef) { + if (!cloned) { + cloned = true; + cstr = cstr->clone(); + } + cstr->First += 1; + done = false; + } + if (cstr->Second < GeoId && cstr->Second != GeoEnum::GeoUndef) { + if (!cloned) { + cloned = true; + cstr = cstr->clone(); + } + cstr->Second += 1; + done = false; + } + if (cstr->Third < GeoId && cstr->Third != GeoEnum::GeoUndef) { + if (!cloned) { + cloned = true; + cstr = cstr->clone(); + } + cstr->Third += 1; + done = false; + } + if(done) break; + } + newConstraints.push_back(cstr); + } + } + + auto geos = ExternalGeo.getValues(); + int offset = 0; + for(auto geoId : geoIds) { + int idx = -geoId-1; + geos.erase(geos.begin()+idx-offset); + ++offset; + } + + if(refs.size()) { + std::vector newSubs; + std::vector newObjs; + const auto &subs = ExternalGeometry.getSubValues(); + auto itSub = subs.begin(); + const auto &objs = ExternalGeometry.getValues(); + auto itObj = objs.begin(); + bool touched = false; + assert(externalGeoRef.size() == objs.size()); + assert(externalGeoRef.size() == subs.size()); + for(auto it=externalGeoRef.begin();it!=externalGeoRef.end();++it,++itObj,++itSub) { + if(refs.count(*it)) { + if(!touched) { + touched = true; + if(newObjs.empty()) { + newObjs.insert(newObjs.end(),objs.begin(),itObj); + newSubs.insert(newSubs.end(),subs.begin(),itSub); + } + } + }else if(touched) { + newObjs.push_back(*itObj); + newSubs.push_back(*itSub); + } + } + if(touched) + ExternalGeometry.setValues(newObjs,newSubs); + } + + ExternalGeo.setValues(std::move(geos)); + + solverNeedsUpdate = true; + Constraints.setValues(std::move(newConstraints)); + acceptGeometry(); // This may need to be refactored into OnChanged for ExternalGeometry. +} + int SketchObject::delAllExternal() { + int count = 0; // the remaining count of the detached external geometry + std::map indexMap; // the index map of the remain external geometry + std::vector geos; // the remaining external geometry + for(int i=0;igetRef().empty()) + indexMap[i] = count++; + geos.push_back(geo); + } // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); @@ -7385,6 +7957,8 @@ int SketchObject::delAllExternal() return -1; } + ExternalGeometry.setValue(0); + ExternalGeo.setValues(std::move(geos)); solverNeedsUpdate = true; Constraints.setValues(std::move(newConstraints)); acceptGeometry();// This may need to be refactored into OnChanged for ExternalGeometry @@ -7417,13 +7991,144 @@ int SketchObject::delConstraintsToExternal() return 0; } +int SketchObject::attachExternal( + const std::vector &geoIds, App::DocumentObject *Obj, const char* SubName) +{ + if (!isExternalAllowed(Obj->getDocument(), Obj)) + return -1; + + std::set detached; + std::set idSet; + for(int geoId : geoIds) { + if(geoId > GeoEnum::RefExt || -geoId-1 >= ExternalGeo.getSize()) +// if(geoId > GeoEnum::RefExt || -geoId-1 >= ExternalGeo.size()) + continue; + auto geo = getGeometry(geoId); + if(!geo) + continue; + auto egf = ExternalGeometryFacade::getFacade(geo); + if(egf->getRef().size()) + detached.insert(egf->getRef()); + for(int id : getRelatedGeometry(geoId)) + idSet.insert(id); + } + + auto geos = ExternalGeo.getValues(); +// auto geos = ExternalGeo; + + std::vector Objects = ExternalGeometry.getValues(); + auto itObj = Objects.begin(); + std::vector SubElements = ExternalGeometry.getSubValues(); + auto itSub = SubElements.begin(); + + assert(Objects.size()==SubElements.size()); + assert(externalGeoRef.size() == Objects.size()); + + for(auto &key : externalGeoRef) { + if (*itObj == Obj && *itSub == SubName){ + FC_ERR("Duplicdate external element reference in " << getFullName() << ": " << key); + return -1; + } + // detach old reference + if(detached.count(key)) { + itObj = Objects.erase(itObj); + itSub = SubElements.erase(itSub); + }else{ + ++itObj; + ++itSub; + } + } + + // add the new ones + Objects.push_back(Obj); + SubElements.push_back(std::string(SubName)); + + ExternalGeometry.setValues(Objects,SubElements); + if(externalGeoRef.size()!=Objects.size()) + return -1; + + std::string ref = externalGeoRef.back(); + for(auto geoId : idSet) { + auto &geo = geos[-geoId-1]; + geo = geo->clone(); + ExternalGeometryFacade::getFacade(geo)->setRef(ref); + } + + ExternalGeo.setValues(std::move(geos)); + rebuildExternalGeometry(); + return ExternalGeometry.getSize()-1; +} + +std::vector SketchObject::getRelatedGeometry(int GeoId) const { + std::vector res; + if(GeoId>GeoEnum::RefExt || -GeoId-1>=ExternalGeo.getSize()) +// if(GeoId>GeoEnum::RefExt || -GeoId-1>=ExternalGeo.size()) + return res; + auto geo = getGeometry(GeoId); + if(!geo) + return res; + const std::string &ref = ExternalGeometryFacade::getFacade(geo)->getRef(); + if(!ref.size()) + return {GeoId}; + auto iter = externalGeoRefMap.find(ref); + if(iter == externalGeoRefMap.end()) + return {GeoId}; + for(auto id : iter->second) { + auto it = externalGeoMap.find(id); + if(it!=externalGeoMap.end()) + res.push_back(-it->second-1); + } + return res; +} + +int SketchObject::syncGeometry(const std::vector &geoIds) { + bool touched = false; + auto geos = ExternalGeo.getValues(); +// auto geos = ExternalGeo; + std::set idSet; + for(int geoId : geoIds) { + auto geo = getGeometry(geoId); + if(!geo || !ExternalGeometryFacade::getFacade(geo)->testFlag(ExternalGeometryExtension::Frozen)) + continue; + for(int gid : getRelatedGeometry(geoId)) + idSet.insert(gid); + } + for(int geoId : idSet) { + if(geoId <= GeoEnum::RefExt && -geoId-1 < ExternalGeo.getSize()) { +// if(geoId <= GeoEnum::RefExt && -geoId-1 < ExternalGeo.size()) { + auto &geo = geos[-geoId-1]; + geo = geo->clone(); + ExternalGeometryFacade::getFacade(geo)->setFlag(ExternalGeometryExtension::Sync); + touched = true; + } + } + if(touched) + ExternalGeo.setValues(std::move(geos)); + return 0; +} + +const Part::Geometry* SketchObject::_getGeometry(int GeoId) const +{ + if (GeoId >= 0) { + const std::vector &geomlist = getInternalGeometry(); + if (GeoId < int(geomlist.size())) + return geomlist[GeoId]; + } + else if (GeoId < 0 && -GeoId-1 < ExternalGeo.getSize()) +// else if (GeoId < 0 && -GeoId-1 < ExternalGeo.size()) + return ExternalGeo[-GeoId-1]; + + return nullptr; +} + int SketchObject::getCompleteGeometryIndex(int GeoId) const { if (GeoId >= 0) { if (GeoId < int(Geometry.getSize())) return GeoId; } - else if (-GeoId <= int(ExternalGeo.size())) +// else if (-GeoId <= int(ExternalGeo.size())) + else if (-GeoId <= int(ExternalGeo.getSize())) return -GeoId - 1; return GeoEnum::GeoUndef; @@ -7431,7 +8136,8 @@ int SketchObject::getCompleteGeometryIndex(int GeoId) const int SketchObject::getGeoIdFromCompleteGeometryIndex(int completeGeometryIndex) const { - int completeGeometryCount = int(Geometry.getSize() + ExternalGeo.size()); +// int completeGeometryCount = int(Geometry.getSize() + ExternalGeo.size()); + int completeGeometryCount = int(Geometry.getSize() + ExternalGeo.getSize()); if (completeGeometryIndex < 0 || completeGeometryIndex >= completeGeometryCount) return GeoEnum::GeoUndef; @@ -7447,6 +8153,19 @@ std::unique_ptr SketchObject::getGeometryFacade(int GeoId) return GeometryFacade::getFacade(getGeometry(GeoId)); } +int SketchObject::setGeometry(int GeoId, const Part::Geometry *geo) { + std::unique_ptr g(geo->clone()); + if(GeoId>=0 && GeoId isDerivedFrom()) - return false; - return true; -} +// Project an edge to a line. Only works if the edge is planar and its plane is +// perpendicular to the projection plane. This function is to work around OCC +// normal projection bug which seems to only repsect the start and ending points +// of an arc but disregarding any extreme points. OCC also has trouble handling +// BSpline projection to a straight line. Although it does correctly projects +// the line including extreme bounds, it will produce a BSpline with degree +// more than one. +// +// The work around here is to use an aligned bounding box of the edge to get +// the projection of the extremem points to construct the projected line. +Part::Geometry* projectEdgeToLine(const TopoDS_Edge &edge, + const Base::Placement& invPlm) +{ + auto shape = Part::TopoShape(edge); + // First, transform the shape to the projection plane local coordinates. + // TODO: Both of these methods are heavily deprecated. Is this really required? + // shape.setShapePlacement(invPlm * shape.getShapePlacement()); + + gp_Pln plane; + // Check if the edge is planar + if (!shape.findPlane(plane)) + return nullptr; -void SketchObject::validateExternalLinks() -{ - // no need to check input data validity as this is an sketchobject managed operation. - Base::StateLocker lock(managedoperation, true); + // Check if the edge plane is perpendicular to the projection plane + if (!plane.Axis().IsNormal(gp_Ax1(), Precision::Angular())) + return nullptr; - std::vector Objects = ExternalGeometry.getValues(); - std::vector SubElements = ExternalGeometry.getSubValues(); + // Align the z axis of the edge plane to the y axis of the projection + // plane, so that the extreme bound will be a line in the x axis direction + // of the projection plane. + double angle = plane.Axis().Direction().Angle(gp_Dir(0, 1, 0)); + gp_Trsf trsf; + if (fabs(angle) > Precision::Angular()) { + trsf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(0, 0, 1)), angle); + shape.move(trsf); + } - bool rebuild = false; + // Make a copy to work around OCC circular edge transformation bug + shape = shape.makeElementCopy(); - for (int i = 0; i < int(Objects.size()); i++) { - const App::DocumentObject* Obj = Objects[i]; - const std::string SubElement = SubElements[i]; + // Explicitly make the mesh, or else getBoundBox() will be very loosely + // bound. + // + // Use very small deflection to make more accurate measurement. Could be slow! + BRepMesh_IncrementalMesh aMesh(shape.getShape(), 0.005, false, 0.1, true); - TopoDS_Shape refSubShape; - bool removeBadLink = false; - try { - if (Obj->isDerivedFrom()) { - const Part::Datum* datum = static_cast(Obj); - refSubShape = datum->getShape(); - } - else { - const Part::Feature* refObj = static_cast(Obj); - const Part::TopoShape& refShape = refObj->Shape.getShape(); - refSubShape = refShape.getSubShape(SubElement.c_str()); - } - } - catch ( Base::IndexError& indexError) { - removeBadLink = true; + // Obtain the bounding box and move the extreme points back to its original + // location + auto bbox = shape.getBoundBox(); + if (!bbox.IsValid()) + return nullptr; + + gp_Pnt p1(bbox.MinX, bbox.MinY, 0); + gp_Pnt p2(bbox.MaxX, bbox.MaxY, 0); + if (fabs(angle) > Precision::Angular()) { + trsf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(0, 0, 1)), -angle); + p1.Transform(trsf); + p2.Transform(trsf); + } + + Base::Vector3d P1(p1.X(), p1.Y(), 0); + Base::Vector3d P2(p2.X(), p2.Y(), 0); + + // check for degenerated case when the line is collapsed to a point + if (p1.SquareDistance(p2) < Precision::SquareConfusion()) { + Part::GeomPoint* point = new Part::GeomPoint((P1 + P2) / 2); + GeometryFacade::setConstruction(point, true); + return point; + } + else { + Part::GeomLineSegment* line = new Part::GeomLineSegment(); + line->setPoints(P1, P2); + GeometryFacade::setConstruction(line, true); + return line; + } +} + +void getParameterRange(Handle(Geom_Curve) curve, + const gp_Pnt &firstPoint, + const gp_Pnt &lastPoint, + double &firstParameter, + double &lastParameter) +{ + // The reason of this function is because the first/last parameter reported + // from some curve does not really corresponds to the first/last vertex of + // the edge. I can only guess this is because the curve (in some cases) is + // actually computed on demaond from surface (in BRepAdaptor_Curve maybe). + // And in the process, there is something off in tolerance causing the + // derived parameter not matching the value corresponding to the position of + // the actual vertex. + GeomAPI_ProjectPointOnCurve pfirst(firstPoint, curve); + GeomAPI_ProjectPointOnCurve plast(lastPoint, curve); + firstParameter = pfirst.LowerDistanceParameter(); + lastParameter = plast.LowerDistanceParameter(); +} + +void adjustParameterRange(const TopoDS_Edge &edge, + Handle(Geom_Plane) gPlane, + const gp_Trsf &mov, + Handle(Geom_Curve) curve, + double &firstParameter, + double &lastParameter) +{ + // This function is to deal with the ambiguity of trimming a periodic + // curve, e.g. given two points on a circle, whether to get the upper or + // lower arc. Because projection orientation may swap the first and last + // parameter of the original curve. + // + // We project the middel point of the original curve to the projected curve + // to decide whether to flip the parameters. + + Handle(Geom_Curve) origCurve = BRepAdaptor_Curve(edge).Curve().Curve(); + + // GeomAPI_ProjectPointOnCurve will project a point to an untransformed + // curve, so make sure to obtain the point on an untransformed edge. + auto e = edge.Located(TopLoc_Location()); + + gp_Pnt firstPoint = BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(e))); + double f = GeomAPI_ProjectPointOnCurve(firstPoint, origCurve).LowerDistanceParameter(); + + gp_Pnt lastPoint = BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(e))); + double l = GeomAPI_ProjectPointOnCurve(lastPoint, origCurve).LowerDistanceParameter(); + + auto adjustPeriodic = [](Handle(Geom_Curve) curve, double &f, double &l) { + // Copied from Geom_TrimmedCurve::setTrim() + if (curve->IsPeriodic()) { + Standard_Real Udeb = curve->FirstParameter(); + Standard_Real Ufin = curve->LastParameter(); + // set f in the range Udeb , Ufin + // set l in the range f , f + Period() + ElCLib::AdjustPeriodic(Udeb, Ufin, + std::min(std::abs(f-l)/2,Precision::PConfusion()), + f, l); + } + }; + + // Adjust for periodic curve to deal with orientation + adjustPeriodic(origCurve, f, l); + + // Obtain the middle parameter in order to get the mid point of the arc + double m = (l - f) * 0.5 + f; + GeomLProp_CLProps prop(origCurve,m,0,Precision::Confusion()); + gp_Pnt midPoint = prop.Value(); + + // Transform all three points to the world coordinate + auto trsf = edge.Location().Transformation(); + midPoint.Transform(trsf); + firstPoint.Transform(trsf); + lastPoint.Transform(trsf); + + // Project the points to the sketch plane. Note the coordinates are still + // in world coordinate system. + gp_Pnt pm = GeomAPI_ProjectPointOnSurf(midPoint, gPlane).NearestPoint(); + gp_Pnt pf = GeomAPI_ProjectPointOnSurf(firstPoint, gPlane).NearestPoint(); + gp_Pnt pl = GeomAPI_ProjectPointOnSurf(lastPoint, gPlane).NearestPoint(); + + // Transform the projected points to sketch plane local coordinates + pm.Transform(mov); + pf.Transform(mov); + pl.Transform(mov); + + // Obtain the corresponding parameters for those points in the projected curve + double f2 = GeomAPI_ProjectPointOnCurve(pf, curve).LowerDistanceParameter(); + double l2 = GeomAPI_ProjectPointOnCurve(pl, curve).LowerDistanceParameter(); + double m2 = GeomAPI_ProjectPointOnCurve(pm, curve).LowerDistanceParameter(); + + firstParameter = f2; + lastParameter = l2; + + adjustPeriodic(curve, f2, l2); + adjustPeriodic(curve, f2, m2); + // If the middle point is out of range, it means we need to choose the + // other half of the arc. + if (m2 > l2) + std::swap(firstParameter, lastParameter); +} + +} // anonymous namespace + +bool SketchObject::evaluateSupport() +{ + // returns false if the shape is broken, null or non-planar + App::DocumentObject* link = AttachmentSupport.getValue(); + if (!link || !link->isDerivedFrom()) + return false; + return true; +} + +static Part::Geometry *fitArcs(std::vector > &arcs, + const gp_Pnt &P1, + const gp_Pnt &P2, + double tol) +{ + double radius = 0.0; + double m; + Base::Vector3d center; + for (auto &geo : arcs) { + if (auto arc = Base::freecad_dynamic_cast(geo.get())) { + if (radius == 0.0) { + radius = arc->getRadius(); + center = arc->getCenter(); + double f = arc->getFirstParameter(); + double l = arc->getLastParameter(); + m = (l-f)*0.5 + f; // middle parameter + } else if (std::abs(radius - arc->getRadius()) > tol) + return nullptr; + } else + return nullptr; + } + if (radius == 0.0) + return nullptr; + if (P1.SquareDistance(P2) < Precision::Confusion()) { + Part::GeomCircle* circle = new Part::GeomCircle(); + circle->setCenter(center); + circle->setRadius(radius); + return circle; + } + else if (arcs.size() == 1) { + auto res = arcs.front().release(); + arcs.clear(); + return res; + } + else { + GeomLProp_CLProps prop(Handle(Geom_Curve)::DownCast(arcs.front()->handle()),m,0,Precision::Confusion()); + gp_Pnt midPoint = prop.Value(); + GC_MakeArcOfCircle arc(P1, midPoint, P2); + auto geo = new Part::GeomArcOfCircle(); + geo->setHandle(arc.Value()); + return geo; + } +} + +void SketchObject::validateExternalLinks() +{ + // no need to check input data validity as this is an sketchobject managed operation. + Base::StateLocker lock(managedoperation, true); + + std::vector Objects = ExternalGeometry.getValues(); + std::vector SubElements = ExternalGeometry.getSubValues(); + + bool rebuild = false; + + for (int i = 0; i < int(Objects.size()); i++) { + const App::DocumentObject* Obj = Objects[i]; + const std::string SubElement = SubElements[i]; + + TopoDS_Shape refSubShape; + bool removeBadLink = false; + try { + if (Obj->isDerivedFrom()) { + const Part::Datum* datum = static_cast(Obj); + refSubShape = datum->getShape(); + } + else { + const Part::Feature* refObj = static_cast(Obj); + const Part::TopoShape& refShape = refObj->Shape.getShape(); + refSubShape = refShape.getSubShape(SubElement.c_str()); + } + } + catch ( Base::IndexError& indexError) { + removeBadLink = true; Base::Console().Warning( this->getFullLabel(), (indexError.getMessage() + "\n").c_str()); } @@ -7614,11 +8565,41 @@ void SketchObject::validateExternalLinks() } } -void SketchObject::rebuildExternalGeometry() +void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) { + Base::StateLocker lock(managedoperation, true); // no need to check input data validity as this is an sketchobject managed operation. + // get the actual lists of the externals - std::vector Objects = ExternalGeometry.getValues(); - std::vector SubElements = ExternalGeometry.getSubValues(); + auto Objects = ExternalGeometry.getValues(); + auto SubElements = ExternalGeometry.getSubValues(); + assert(externalGeoRef.size() == Objects.size()); + auto keys = externalGeoRef; + + // re-check for any missing geometry element. The code here has a side + // effect that the linked external geometry will continue to work even if + // ExternalGeometry is wiped out. + for(auto &geo : ExternalGeo.getValues()) { + auto egf = ExternalGeometryFacade::getFacade(geo); + if(egf->getRef().size() && egf->testFlag(ExternalGeometryExtension::Missing)) { + const std::string &ref = egf->getRef(); + auto pos = ref.find('.'); + if(pos == std::string::npos) + continue; + std::string objName = ref.substr(0,pos); + auto obj = getDocument()->getObject(objName.c_str()); + if(!obj) + continue; + std::pair elementName; + App::GeoFeature::resolveElement(obj,ref.c_str()+pos+1,elementName); + if(elementName.second.size() + && !App::GeoFeature::hasMissingElement(elementName.second.c_str())) + { + Objects.push_back(obj); + SubElements.push_back(elementName.second); + keys.push_back(ref); + } + } + } Base::Placement Plm = Placement.getValue(); Base::Vector3d Pos = Plm.getPosition(); @@ -7653,6 +8634,37 @@ void SketchObject::rebuildExternalGeometry() BRepBuilderAPI_MakeFace mkFace(sketchPlane); TopoDS_Shape aProjFace = mkFace.Shape(); + std::set refSet; + // We use a vector here to keep the order (roughly) the same as ExternalGeometry + std::vector > > newGeos; + newGeos.reserve(Objects.size()); +#ifdef FC_USE_TNP_FIX + for (int i=0; i < int(Objects.size()); i++) { + const App::DocumentObject *Obj=Objects[i]; + const std::string &SubElement=SubElements[i]; + const std::string &key = keys[i]; + + // Skip frozen geometries + bool frozen = false; + bool sync = false; + bool intersection = addIntersection && (i+1 == (int)Objects.size()); + for(auto id : externalGeoRefMap[key]) { + auto it = externalGeoMap.find(id); + if(it != externalGeoMap.end()) { + auto egf = ExternalGeometryFacade::getFacade(ExternalGeo[it->second]); + if(egf->testFlag(ExternalGeometryExtension::Frozen)) + frozen = true; + if(egf->testFlag(ExternalGeometryExtension::Sync)) + sync = true; +// if (egf->testFlag(ExternalGeometryExtension::Intersection)) +// intersection = true; + } + } + if(frozen && !sync) { + refSet.insert(std::move(key)); + continue; + } +#else for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); ++it) if (*it) @@ -7669,7 +8681,13 @@ void SketchObject::rebuildExternalGeometry() for (int i = 0; i < int(Objects.size()); i++) { const App::DocumentObject* Obj = Objects[i]; const std::string SubElement = SubElements[i]; +#endif + if(!Obj || !Obj->getNameInDocument()) + continue; + std::vector > geos; + + try { TopoDS_Shape refSubShape; if (Obj->isDerivedFrom()) { @@ -7726,7 +8744,8 @@ void SketchObject::rebuildExternalGeometry() const TopoDS_Edge& edge = TopoDS::Edge(builder.Shape()); BRepAdaptor_Curve curve(edge); if (curve.GetType() == GeomAbs_Line) { - ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); + geos.emplace_back(projectLine(curve, gPlane, invPlm)); +// ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); } } } @@ -7744,51 +8763,17 @@ void SketchObject::rebuildExternalGeometry() const TopoDS_Edge& edge = TopoDS::Edge(refSubShape); BRepAdaptor_Curve curve(edge); if (curve.GetType() == GeomAbs_Line) { - ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); - break; +// ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); + geos.emplace_back(projectLine(curve, gPlane, invPlm)); } - - gp_Dir vec1 = sketchPlane.Axis().Direction(); - gp_Dir vec2; - if (curve.GetType() == GeomAbs_Circle) { - vec2 = curve.Circle().Axis().Direction(); - } - else if (curve.GetType() == GeomAbs_Ellipse) { - vec2 = curve.Ellipse().Axis().Direction(); - } - - gp_Pnt beg, end; - if ((curve.GetType() == GeomAbs_Circle || curve.GetType() == GeomAbs_Ellipse) - && !curve.IsClosed()) { - beg = ProjPointOnPlane_XYZ(curve.Value(curve.FirstParameter()), sketchPlane); - end = ProjPointOnPlane_XYZ(curve.Value(curve.LastParameter()), sketchPlane); - Base::Vector3d vBeg(beg.X(), beg.Y(), beg.Z()); - Base::Vector3d vEnd(end.X(), end.Y(), end.Z()); - invPlm.multVec(vBeg, vBeg); - invPlm.multVec(vEnd, vEnd); - beg = gp_Pnt(vBeg[0], vBeg[1], vBeg[2]); - end = gp_Pnt(vEnd[0], vEnd[1], vEnd[2]); - - if (vec1.IsNormal(vec2, Precision::Angular())) { - if (beg.SquareDistance(end) < Precision::SquareConfusion()) { - Base::Vector3d p = (vBeg + vEnd) / 2; - Part::GeomPoint* point = new Part::GeomPoint(p); - ExternalGeo.push_back(point); - } - else { - Part::GeomLineSegment* line = new Part::GeomLineSegment(); - line->setPoints(vBeg, vEnd); - GeometryFacade::setConstruction(line, true); - ExternalGeo.push_back(line); - } - break; - } - } - - if (curve.GetType() == GeomAbs_Circle) { + else if (curve.GetType() == GeomAbs_Circle) { + gp_Dir vec1 = sketchPlane.Axis().Direction(); + gp_Dir vec2 = curve.Circle().Axis().Direction(); if (vec1.IsParallel(vec2, Precision::Confusion())) { gp_Circ circle = curve.Circle(); gp_Pnt cnt = circle.Location(); + gp_Pnt beg = curve.Value(curve.FirstParameter()); + gp_Pnt end = curve.Value(curve.LastParameter()); GeomAPI_ProjectPointOnSurf proj(cnt, gPlane); cnt = proj.NearestPoint(); @@ -7796,12 +8781,14 @@ void SketchObject::rebuildExternalGeometry() cnt.Transform(mov); circle.Transform(mov); - if (curve.IsClosed()) { + if (beg.SquareDistance(end) < Precision::Confusion()) { Part::GeomCircle* gCircle = new Part::GeomCircle(); gCircle->setRadius(circle.Radius()); gCircle->setCenter(Base::Vector3d(cnt.X(), cnt.Y(), cnt.Z())); + GeometryFacade::setConstruction(gCircle, true); - ExternalGeo.push_back(gCircle); +// ExternalGeo.push_back(gCircle); + geos.emplace_back(gCircle); } else { Part::GeomArcOfCircle* gArc = new Part::GeomArcOfCircle(); @@ -7810,19 +8797,25 @@ void SketchObject::rebuildExternalGeometry() hCircle, curve.FirstParameter(), curve.LastParameter()); gArc->setHandle(tCurve); GeometryFacade::setConstruction(gArc, true); - ExternalGeo.push_back(gArc); +// ExternalGeo.push_back(gArc); + geos.emplace_back(gArc); } } else { // creates an ellipse or a segment + gp_Dir vec1 = sketchPlane.Axis().Direction(); + gp_Dir vec2 = curve.Circle().Axis().Direction(); gp_Circ origCircle = curve.Circle(); - gp_Pnt cnt = origCircle.Location(); - GeomAPI_ProjectPointOnSurf proj(cnt, gPlane); - cnt = proj.NearestPoint(); - if (vec1.IsNormal(vec2, Precision::Angular())) { - // projection is a segment + if (vec1.IsNormal( + vec2, Precision::Angular())) {// circle's normal vector in plane: + // projection is a line + // define center by projection + gp_Pnt cnt = origCircle.Location(); + GeomAPI_ProjectPointOnSurf proj(cnt, gPlane); + cnt = proj.NearestPoint(); + gp_Dir dirOrientation = gp_Dir(vec1 ^ vec2); gp_Dir dirLine(dirOrientation); @@ -7833,6 +8826,90 @@ void SketchObject::rebuildExternalGeometry() ligne.D0(-origCircle.Radius(), P1); ligne.D0(origCircle.Radius(), P2); + if (!curve.IsClosed()) {// arc of circle + + // start point of arc of circle + gp_Pnt pntF = curve.Value(curve.FirstParameter()); + // end point of arc of circle + gp_Pnt pntL = curve.Value(curve.LastParameter()); + + double alpha = + dirOrientation.AngleWithRef(curve.Circle().XAxis().Direction(), + curve.Circle().Axis().Direction()); + + double baseAngle = curve.FirstParameter(); + + int tours = 0; + double startAngle = baseAngle + alpha; + // bring startAngle back in [-pi/2 , 3pi/2[ + while (startAngle < -M_PI / 2.0 && tours < 10) { + startAngle = baseAngle + ++tours * 2.0 * M_PI + alpha; + } + while (startAngle >= 3.0 * M_PI / 2.0 && tours > -10) { + startAngle = baseAngle + --tours * 2.0 * M_PI + alpha; + } + + // apply same offset to end angle + double endAngle = curve.LastParameter() + startAngle - baseAngle; + + if (startAngle <= 0.0) { + if (endAngle <= 0.0) { + P1 = ProjPointOnPlane_XYZ(pntF, sketchPlane); + P2 = ProjPointOnPlane_XYZ(pntL, sketchPlane); + } + else { + if (endAngle <= fabs(startAngle)) { + // P2 = P2 already defined + P1 = ProjPointOnPlane_XYZ(pntF, sketchPlane); + } + else if (endAngle < M_PI) { + // P2 = P2, already defined + P1 = ProjPointOnPlane_XYZ(pntL, sketchPlane); + } + else { + // P1 = P1, already defined + // P2 = P2, already defined + } + } + } + else if (startAngle < M_PI) { + if (endAngle < M_PI) { + P1 = ProjPointOnPlane_XYZ(pntF, sketchPlane); + P2 = ProjPointOnPlane_XYZ(pntL, sketchPlane); + } + else if (endAngle < 2.0 * M_PI - startAngle) { + P2 = ProjPointOnPlane_XYZ(pntF, sketchPlane); + // P1 = P1, already defined + } + else if (endAngle < 2.0 * M_PI) { + P2 = ProjPointOnPlane_XYZ(pntL, sketchPlane); + // P1 = P1, already defined + } + else { + // P1 = P1, already defined + // P2 = P2, already defined + } + } + else { + if (endAngle < 2 * M_PI) { + P1 = ProjPointOnPlane_XYZ(pntF, sketchPlane); + P2 = ProjPointOnPlane_XYZ(pntL, sketchPlane); + } + else if (endAngle < 4 * M_PI - startAngle) { + P1 = ProjPointOnPlane_XYZ(pntF, sketchPlane); + // P2 = P2, already defined + } + else if (endAngle < 3 * M_PI) { + // P1 = P1, already defined + P2 = ProjPointOnPlane_XYZ(pntL, sketchPlane); + } + else { + // P1 = P1, already defined + // P2 = P2, already defined + } + } + } + Base::Vector3d p1(P1.X(), P1.Y(), P1.Z());// ends of segment FCAD style Base::Vector3d p2(P2.X(), P2.Y(), P2.Z()); invPlm.multVec(p1, p1); @@ -7840,14 +8917,20 @@ void SketchObject::rebuildExternalGeometry() projectedSegment->setPoints(p1, p2); GeometryFacade::setConstruction(projectedSegment, true); - ExternalGeo.push_back(projectedSegment); +// ExternalGeo.push_back(projectedSegment); + geos.emplace_back(projectedSegment); } - else { // projection is an ellipse + else {// general case, full circle + gp_Pnt cnt = origCircle.Location(); + GeomAPI_ProjectPointOnSurf proj(cnt, gPlane); + // projection of circle center on sketch plane, 3D space + cnt = proj.NearestPoint(); // converting to FCAD style vector Base::Vector3d p(cnt.X(), cnt.Y(), cnt.Z()); // transforming towards sketch's (x,y) coordinates invPlm.multVec(p, p); + gp_Vec vecMajorAxis = vec1 ^ vec2;// major axis in 3D space double minorRadius;// TODO use data type of vectors around... @@ -7869,32 +8952,21 @@ void SketchObject::rebuildExternalGeometry() // NB: force normal of ellipse to be normal of sketch's plane. gp_Ax2 refFrameEllipse( gp_Pnt(gp_XYZ(p[0], p[1], p[2])), gp_Vec(0, 0, 1), vecMajorAxis); - Handle(Geom_Ellipse) hEllipse = + Handle(Geom_Ellipse) curve = new Geom_Ellipse(refFrameEllipse, origCircle.Radius(), minorRadius); - if (curve.IsClosed()) { - Part::GeomEllipse* ellipse = new Part::GeomEllipse(); - ellipse->setHandle(hEllipse); - GeometryFacade::setConstruction(ellipse, true); - ExternalGeo.push_back(ellipse); - } - else { - Part::GeomArcOfEllipse* aoe = new Part::GeomArcOfEllipse(); - GeomAPI_ProjectPointOnCurve pFirst(beg, hEllipse); - GeomAPI_ProjectPointOnCurve pLast(end, hEllipse); - Handle(Geom_TrimmedCurve) tCurve = - new Geom_TrimmedCurve(hEllipse, - pFirst.LowerDistanceParameter(), - pLast.LowerDistanceParameter()); - aoe->setHandle(tCurve); - GeometryFacade::setConstruction(aoe, true); - ExternalGeo.push_back(aoe); - } + Part::GeomEllipse* ellipse = new Part::GeomEllipse(); + ellipse->setHandle(curve); + GeometryFacade::setConstruction(ellipse, true); + +// ExternalGeo.push_back(ellipse); + geos.emplace_back(ellipse); } } } else if (curve.GetType() == GeomAbs_Ellipse) { gp_Elips elipsOrig = curve.Ellipse(); + gp_Elips elipsDest; gp_Pnt origCenter = elipsOrig.Location(); gp_Pnt destCenter = ProjPointOnPlane_UVN(origCenter, sketchPlane).XYZ(); @@ -7952,62 +9024,45 @@ void SketchObject::rebuildExternalGeometry() gp_Ax2 destCurveAx2( destCenter, gp_Dir(0, 0, sens > 0.0 ? 1.0 : -1.0), gp_Dir(destAxisMajor)); + // projection is a circle if ((RDest - rDest) < (double)Precision::Confusion()) { - // projection is a circle - Handle(Geom_Circle) hCircle = new Geom_Circle( - destCurveAx2, 0.5 * (rDest + RDest)); - - if (curve.IsClosed()) { - Part::GeomCircle* circle = new Part::GeomCircle(hCircle); - GeometryFacade::setConstruction(circle, true); - ExternalGeo.push_back(circle); + Handle(Geom_Circle) curve = + new Geom_Circle(destCurveAx2, 0.5 * (rDest + RDest)); + Part::GeomCircle* circle = new Part::GeomCircle(); + circle->setHandle(curve); + GeometryFacade::setConstruction(circle, true); + +// ExternalGeo.push_back(circle); + geos.emplace_back(circle); + } + else { + if (sketchPlane.Position().Direction().IsNormal( + elipsOrig.Position().Direction(), Precision::Angular())) { + gp_Vec start = gp_Vec(destCenter.XYZ()) + destAxisMajor; + gp_Vec end = gp_Vec(destCenter.XYZ()) - destAxisMajor; + + Part::GeomLineSegment* projectedSegment = new Part::GeomLineSegment(); + projectedSegment->setPoints( + Base::Vector3d(start.X(), start.Y(), start.Z()), + Base::Vector3d(end.X(), end.Y(), end.Z())); + GeometryFacade::setConstruction(projectedSegment, true); +// ExternalGeo.push_back(projectedSegment); + geos.emplace_back(projectedSegment); } else { - Part::GeomArcOfCircle* arc = new Part::GeomArcOfCircle(); - GeomAPI_ProjectPointOnCurve pFirst(beg, hCircle); - GeomAPI_ProjectPointOnCurve pLast(end, hCircle); - Handle(Geom_TrimmedCurve) tCurve = - new Geom_TrimmedCurve(hCircle, - pFirst.LowerDistanceParameter(), - pLast.LowerDistanceParameter()); - arc->setHandle(tCurve); - GeometryFacade::setConstruction(arc, true); - ExternalGeo.push_back(arc); - } - } - else if (vec1.IsNormal(vec2, Precision::Angular())) { - // projection is a segment - gp_Vec start = gp_Vec(destCenter.XYZ()) + destAxisMajor; - gp_Vec end = gp_Vec(destCenter.XYZ()) - destAxisMajor; - - Part::GeomLineSegment* projectedSegment = new Part::GeomLineSegment(); - projectedSegment->setPoints( - Base::Vector3d(start.X(), start.Y(), start.Z()), - Base::Vector3d(end.X(), end.Y(), end.Z())); - GeometryFacade::setConstruction(projectedSegment, true); - ExternalGeo.push_back(projectedSegment); - } - else { // projection is an ellipse - Handle(Geom_Ellipse) hEllipse = new Geom_Ellipse( - destCurveAx2, destAxisMajor.Magnitude(), destAxisMinor.Magnitude()); - if (curve.IsClosed()) { + elipsDest.SetPosition(destCurveAx2); + elipsDest.SetMajorRadius(destAxisMajor.Magnitude()); + elipsDest.SetMinorRadius(destAxisMinor.Magnitude()); + + + Handle(Geom_Ellipse) curve = new Geom_Ellipse(elipsDest); Part::GeomEllipse* ellipse = new Part::GeomEllipse(); - ellipse->setHandle(hEllipse); + ellipse->setHandle(curve); GeometryFacade::setConstruction(ellipse, true); - ExternalGeo.push_back(ellipse); - } - else { - Part::GeomArcOfEllipse* aoe = new Part::GeomArcOfEllipse(); - GeomAPI_ProjectPointOnCurve pFirst(beg, hEllipse); - GeomAPI_ProjectPointOnCurve pLast(end, hEllipse); - Handle(Geom_TrimmedCurve) tCurve = - new Geom_TrimmedCurve(hEllipse, - pFirst.LowerDistanceParameter(), - pLast.LowerDistanceParameter()); - aoe->setHandle(tCurve); - GeometryFacade::setConstruction(aoe, true); - ExternalGeo.push_back(aoe); + +// ExternalGeo.push_back(ellipse); + geos.emplace_back(ellipse); } } } @@ -8030,17 +9085,19 @@ void SketchObject::rebuildExternalGeometry() Base::Vector3d p1(P1.X(), P1.Y(), P1.Z()); Base::Vector3d p2(P2.X(), P2.Y(), P2.Z()); - if (P1.SquareDistance(P2) < Precision::SquareConfusion()) { + if (Base::Distance(p1, p2) < Precision::Confusion()) { Base::Vector3d p = (p1 + p2) / 2; Part::GeomPoint* point = new Part::GeomPoint(p); GeometryFacade::setConstruction(point, true); - ExternalGeo.push_back(point); +// ExternalGeo.push_back(point); + geos.emplace_back(point); } else { Part::GeomLineSegment* line = new Part::GeomLineSegment(); line->setPoints(p1, p2); GeometryFacade::setConstruction(line, true); - ExternalGeo.push_back(line); +// ExternalGeo.push_back(line); + geos.emplace_back(line); } } else if (projCurve.GetType() == GeomAbs_Circle) { @@ -8049,13 +9106,14 @@ void SketchObject::rebuildExternalGeometry() gp_Pnt P1 = projCurve.Value(projCurve.FirstParameter()); gp_Pnt P2 = projCurve.Value(projCurve.LastParameter()); - if (P1.SquareDistance(P2) < Precision::SquareConfusion()) { + if (P1.SquareDistance(P2) < Precision::Confusion()) { Part::GeomCircle* circle = new Part::GeomCircle(); circle->setRadius(c.Radius()); circle->setCenter(Base::Vector3d(p.X(), p.Y(), p.Z())); GeometryFacade::setConstruction(circle, true); - ExternalGeo.push_back(circle); +// ExternalGeo.push_back(circle); + geos.emplace_back(circle); } else { Part::GeomArcOfCircle* arc = new Part::GeomArcOfCircle(); @@ -8066,14 +9124,43 @@ void SketchObject::rebuildExternalGeometry() projCurve.LastParameter()); arc->setHandle(tCurve); GeometryFacade::setConstruction(arc, true); - ExternalGeo.push_back(arc); +// ExternalGeo.push_back(arc); + geos.emplace_back(arc); } } else if (projCurve.GetType() == GeomAbs_BSplineCurve) { - Part::GeomBSplineCurve* bspline = - new Part::GeomBSplineCurve(projCurve.BSpline()); - GeometryFacade::setConstruction(bspline, true); - ExternalGeo.push_back(bspline); + // Unfortunately, a normal projection of a circle can also give + // a Bspline Split the spline into arcs + GeomConvert_BSplineCurveKnotSplitting bSplineSplitter( + projCurve.BSpline(), 2); + // int s = bSplineSplitter.NbSplits(); + if ((curve.GetType() == GeomAbs_Circle) + && (bSplineSplitter.NbSplits() == 2)) { + // Result of projection is actually a circle... + TColStd_Array1OfInteger splits(1, 2); + bSplineSplitter.Splitting(splits); + gp_Pnt p1 = projCurve.Value(splits(1)); + gp_Pnt p2 = projCurve.Value(splits(2)); + gp_Pnt p3 = projCurve.Value(0.5 * (splits(1) + splits(2))); + GC_MakeCircle circleMaker(p1, p2, p3); + Handle(Geom_Circle) circ = circleMaker.Value(); + Part::GeomCircle* circle = new Part::GeomCircle(); + circle->setRadius(circ->Radius()); + gp_Pnt center = circ->Axis().Location(); + circle->setCenter( + Base::Vector3d(center.X(), center.Y(), center.Z())); + + GeometryFacade::setConstruction(circle, true); +// ExternalGeo.push_back(circle); + geos.emplace_back(circle); + } + else { + Part::GeomBSplineCurve* bspline = + new Part::GeomBSplineCurve(projCurve.BSpline()); + GeometryFacade::setConstruction(bspline, true); +// ExternalGeo.push_back(bspline); + geos.emplace_back(bspline); + } } else if (projCurve.GetType() == GeomAbs_Hyperbola) { gp_Hypr e = projCurve.Hyperbola(); @@ -8085,7 +9172,7 @@ void SketchObject::rebuildExternalGeometry() gp_Dir xdir = e.XAxis().Direction(); gp_Ax2 xdirref(p, normal); - if (P1.SquareDistance(P2) < Precision::SquareConfusion()) { + if (P1.SquareDistance(P2) < Precision::Confusion()) { Part::GeomHyperbola* hyperbola = new Part::GeomHyperbola(); hyperbola->setMajorRadius(e.MajorRadius()); hyperbola->setMinorRadius(e.MinorRadius()); @@ -8093,7 +9180,8 @@ void SketchObject::rebuildExternalGeometry() hyperbola->setAngleXU( -xdir.AngleWithRef(xdirref.XDirection(), normal)); GeometryFacade::setConstruction(hyperbola, true); - ExternalGeo.push_back(hyperbola); +// ExternalGeo.push_back(hyperbola); + geos.emplace_back(hyperbola); } else { Part::GeomArcOfHyperbola* aoh = @@ -8105,7 +9193,8 @@ void SketchObject::rebuildExternalGeometry() projCurve.LastParameter()); aoh->setHandle(tCurve); GeometryFacade::setConstruction(aoh, true); - ExternalGeo.push_back(aoh); +// ExternalGeo.push_back(aoh); + geos.emplace_back(aoh); } } else if (projCurve.GetType() == GeomAbs_Parabola) { @@ -8118,14 +9207,15 @@ void SketchObject::rebuildExternalGeometry() gp_Dir xdir = e.XAxis().Direction(); gp_Ax2 xdirref(p, normal); - if (P1.SquareDistance(P2) < Precision::SquareConfusion()) { + if (P1.SquareDistance(P2) < Precision::Confusion()) { Part::GeomParabola* parabola = new Part::GeomParabola(); parabola->setFocal(e.Focal()); parabola->setCenter(Base::Vector3d(p.X(), p.Y(), p.Z())); parabola->setAngleXU( -xdir.AngleWithRef(xdirref.XDirection(), normal)); GeometryFacade::setConstruction(parabola, true); - ExternalGeo.push_back(parabola); +// ExternalGeo.push_back(parabola); + geos.emplace_back(parabola); } else { Part::GeomArcOfParabola* aop = @@ -8137,7 +9227,8 @@ void SketchObject::rebuildExternalGeometry() projCurve.LastParameter()); aop->setHandle(tCurve); GeometryFacade::setConstruction(aop, true); - ExternalGeo.push_back(aop); +// ExternalGeo.push_back(aop); + geos.emplace_back(aop); } } else if (projCurve.GetType() == GeomAbs_Ellipse) { @@ -8146,15 +9237,17 @@ void SketchObject::rebuildExternalGeometry() gp_Pnt P1 = projCurve.Value(projCurve.FirstParameter()); gp_Pnt P2 = projCurve.Value(projCurve.LastParameter()); + // gp_Dir normal = e.Axis().Direction(); gp_Dir normal = gp_Dir(0, 0, 1); gp_Ax2 xdirref(p, normal); - if (P1.SquareDistance(P2) < Precision::SquareConfusion()) { + if (P1.SquareDistance(P2) < Precision::Confusion()) { Part::GeomEllipse* ellipse = new Part::GeomEllipse(); Handle(Geom_Ellipse) curve = new Geom_Ellipse(e); ellipse->setHandle(curve); GeometryFacade::setConstruction(ellipse, true); - ExternalGeo.push_back(ellipse); +// ExternalGeo.push_back(ellipse); + geos.emplace_back(ellipse); } else { Part::GeomArcOfEllipse* aoe = new Part::GeomArcOfEllipse(); @@ -8165,7 +9258,8 @@ void SketchObject::rebuildExternalGeometry() projCurve.LastParameter()); aoe->setHandle(tCurve); GeometryFacade::setConstruction(aoe, true); - ExternalGeo.push_back(aoe); +// ExternalGeo.push_back(aoe); + geos.emplace_back(aoe); } } else { @@ -8189,33 +9283,244 @@ void SketchObject::rebuildExternalGeometry() Part::GeomPoint* point = new Part::GeomPoint(p); GeometryFacade::setConstruction(point, true); - ExternalGeo.push_back(point); +// ExternalGeo.push_back(point); + geos.emplace_back(point); } break; default: throw Base::TypeError("Unknown type of geometry"); break; + } + + if (intersection && (refSubShape.ShapeType() == TopAbs_EDGE + || refSubShape.ShapeType() == TopAbs_FACE)) + { + BRepAlgoAPI_Section maker(refSubShape, sketchPlane); + maker.Approximation(Standard_True); + if (!maker.IsDone()) + FC_THROWM(Base::CADKernelError,"Failed to get intersection"); + Part::TopoShape intersectionShape(maker.Shape()); + auto edges = intersectionShape.getSubTopoShapes(TopAbs_EDGE); +// for (const auto &s : edges) +// importEdge(s.getShape()); + // Section of some face (e.g. sphere) produce more than one arcs + // from the same circle. So we try to fit the arcs with a single + // circle/arc. + if (refSubShape.ShapeType() == TopAbs_FACE && geos.size() > 1) { + auto wires = Part::TopoShape().makeElementWires(edges); + if (wires.countSubShapes(TopAbs_WIRE) == 1) { + TopoDS_Vertex firstVertex, lastVertex; + BRepTools_WireExplorer exp(TopoDS::Wire(wires.getSubShape(TopAbs_WIRE, 1))); + firstVertex = exp.CurrentVertex(); + while (!exp.More()) + exp.Next(); + lastVertex = exp.CurrentVertex(); + gp_Pnt P1 = BRep_Tool::Pnt(firstVertex); + gp_Pnt P2 = BRep_Tool::Pnt(lastVertex); + if (auto geo = fitArcs(geos, P1, P2, ArcFitTolerance.getValue())) { + geos.clear(); + geos.emplace_back(geo); + } + } + } +// for (const auto &s : intersectionShape.getSubShapes(TopAbs_VERTEX, TopAbs_EDGE)) +// importVertex(s); + } + + } catch (Base::Exception &e) { + FC_ERR("Failed to project external geometry in " + << getFullName() << ": " << key << std::endl << e.what()); + continue; + } catch (Standard_Failure &e) { + FC_ERR("Failed to project external geometry in " + << getFullName() << ": " << key << std::endl << e.GetMessageString()); + continue; + } catch (std::exception &e) { + FC_ERR("Failed to project external geometry in " + << getFullName() << ": " << key << std::endl << e.what()); + continue; + } catch (...) { + FC_ERR("Failed to project external geometry in " + << getFullName() << ": " << key << std::endl << "Unknown exception"); + continue; + } + if(geos.empty()) + continue; + + if(!refSet.emplace(key).second) { + FC_WARN("Duplicated external reference in " << getFullName() << ": " << key); + continue; + } + if (intersection) { + for(auto &geo : geos) { + auto egf = ExternalGeometryFacade::getFacade(geo.get()); +// egf->setFlag(ExternalGeometryExtension::Intersection); + egf->setFlag(ExternalGeometryExtension::Defining, defining); + } + } else if (defining && i+1==(int)Objects.size()) { + for(auto &geo : geos) + ExternalGeometryFacade::getFacade(geo.get())->setFlag( + ExternalGeometryExtension::Defining); + } + for(auto &geo : geos) + ExternalGeometryFacade::getFacade(geo.get())->setRef(key); + newGeos.push_back(std::move(geos)); + } + + // allocate unique geometry id + for(auto &geos : newGeos) { + auto egf = ExternalGeometryFacade::getFacade(geos.front().get()); + auto &refs = externalGeoRefMap[egf->getRef()]; + while(refs.size() < geos.size()) + refs.push_back(++geoLastId); + + // In case a projection reduces output geometries, delete them + std::set geoIds; + geoIds.insert(refs.begin()+geos.size(),refs.end()); + + // Sync id and ref of the new geometries + int i = 0; + for(auto &geo : geos) + GeometryFacade::setId(geo.get(), refs[i++]); + + delExternalPrivate(geoIds,false); + } + + auto geoms = ExternalGeo.getValues(); + + // now update the geometries + for(auto &geos : newGeos) { + for(auto &geo : geos) { + auto it = externalGeoMap.find(GeometryFacade::getId(geo.get())); + if(it == externalGeoMap.end()) { + // This is a new geometries. + geoms.push_back(geo.release()); + continue; + } + // This is an existing geometry. Update it while keeping the old flags + ExternalGeometryFacade::copyFlags(geoms[it->second], geo.get()); + geoms[it->second] = geo.release(); } } + // Check for any missing references + bool hasError = false; + for(auto geo : geoms) { + auto egf = ExternalGeometryFacade::getFacade(geo); + egf->setFlag(ExternalGeometryExtension::Sync,false); + if(egf->getRef().empty()) + continue; + if(!refSet.count(egf->getRef())) { + FC_ERR( "External geometry " << getFullName() << ".e" << egf->getId() + << " missing reference: " << egf->getRef()); + hasError = true; + egf->setFlag(ExternalGeometryExtension::Missing,true); + } else { + egf->setFlag(ExternalGeometryExtension::Missing,false); + } + } + + ExternalGeo.setValues(std::move(geoms)); rebuildVertexIndex(); + + // clean up geometry reference + if(refSet.size() != (size_t)ExternalGeometry.getSize()) { + if(refSet.size() < keys.size()) { + auto itObj = Objects.begin(); + auto itSub = SubElements.begin(); + for(auto &ref : keys) { + if(!refSet.count(ref)) { + itObj = Objects.erase(itObj); + itSub = SubElements.erase(itSub); + }else { + ++itObj; + ++itSub; + } + } + } + ExternalGeometry.setValues(Objects,SubElements); + } + + solverNeedsUpdate=true; + Constraints.acceptGeometry(getCompleteGeometry()); + + if(hasError && this->isRecomputing()) + throw Base::RuntimeError("Missing external geometry reference"); +} + +void SketchObject::fixExternalGeometry(const std::vector &geoIds) { + std::set idSet(geoIds.begin(),geoIds.end()); + auto geos = ExternalGeo.getValues(); +// auto geos = ExternalGeo; + auto objs = ExternalGeometry.getValues(); + auto subs = ExternalGeometry.getSubValues(); + bool touched = false; + for(int i=2;i<(int)geos.size();++i) { + auto &geo = geos[i]; + auto egf = ExternalGeometryFacade::getFacade(geo); + int GeoId = -i-1; + if(egf->getRef().empty() + || !egf->testFlag(ExternalGeometryExtension::Missing) + || (idSet.size() && !idSet.count(GeoId))) + continue; + std::string ref = egf->getRef(); + auto pos = ref.find('.'); + if(pos == std::string::npos) { + FC_ERR("Invalid geometry reference " << ref); + continue; + } + std::string objName = ref.substr(0,pos); + auto obj = getDocument()->getObject(objName.c_str()); + if(!obj) { + FC_ERR("Cannot find object in reference " << ref); + continue; + } + + auto elements = Part::Feature::getRelatedElements(obj,ref.c_str()+pos+1); + if(!elements.size()) { + FC_ERR("No related reference found for " << ref); + continue; + } + + geo = geo->clone(); + egf->setGeometry(geo); + egf->setFlag(ExternalGeometryExtension::Missing,false); + ref = objName + "." + Data::ComplexGeoData::elementMapPrefix(); + elements.front().name.appendToBuffer(ref); + egf->setRef(ref); + objs.push_back(obj); + subs.emplace_back(); + elements.front().index.appendToStringBuffer(subs.back()); + touched = true; + } + + if(touched) { + ExternalGeo.setValues(geos); + ExternalGeometry.setValues(objs,subs); + rebuildExternalGeometry(); + } } std::vector SketchObject::getCompleteGeometry() const { std::vector vals = getInternalGeometry(); - vals.insert(vals.end(), ExternalGeo.rbegin(), ExternalGeo.rend());// in reverse order +// vals.insert(vals.end(), ExternalGeo.rbegin(), ExternalGeo.rend());// in reverse order + const auto &geos = getExternalGeometry(); + vals.insert(vals.end(), geos.rbegin(), geos.rend()); // in reverse order return vals; } GeoListFacade SketchObject::getGeoListFacade() const { std::vector facade; - facade.reserve(Geometry.getSize() + ExternalGeo.size()); +// facade.reserve(Geometry.getSize() + ExternalGeo.size()); + facade.reserve(Geometry.getSize() + ExternalGeo.getSize()); for (auto geo : Geometry.getValues()) facade.push_back(GeometryFacade::getFacade(geo)); - for (auto rit = ExternalGeo.rbegin(); rit != ExternalGeo.rend(); rit++) +// for (auto rit = ExternalGeo.rbegin(); rit != ExternalGeo.rend(); rit++) + const auto &externalGeos = ExternalGeo.getValues(); + for(auto rit = externalGeos.rbegin(); rit != externalGeos.rend(); rit++) facade.push_back(GeometryFacade::getFacade(*rit)); return GeoListFacade::getGeoListModel(std::move(facade), Geometry.getSize()); @@ -8767,6 +10072,21 @@ std::string SketchObject::validateExpression(const App::ObjectIdentifier& path, "expression."; } } + geoMap.clear(); + const auto &vals = getInternalGeometry(); + for(long i=0;i<(long)vals.size();++i) { + auto geo = vals[i]; + auto gf = GeometryFacade::getFacade(geo); + if(!gf->getId()) + gf->setId(++geoLastId); + else if(gf->getId() > geoLastId) + geoLastId = gf->getId(); + while(!geoMap.insert(std::make_pair(gf->getId(),i)).second) { + FC_WARN("duplicate geometry id " << gf->getId() << " -> " << geoLastId+1); + gf->setId(++geoLastId); + } + } + updateGeoHistory(); } return ""; } @@ -8896,6 +10216,38 @@ unsigned int SketchObject::getMemSize() const void SketchObject::Save(Writer& writer) const { + int index = -1; + auto &geos = const_cast(ExternalGeo).getValues(); + for(auto geo : geos) + ExternalGeometryFacade::getFacade(geo)->setRefIndex(-1); + + if(isExporting()) { + // We cannot export shape with the new topological naming, because it + // uses hasher indices that are unique only within its owner document. + // Therefore, we cannot rely on Geometry::Ref as key to map geometry to + // external object reference. So, before exporting, we pre-calculate + // the mapping and store them in Geometry::RefIndex. When importing, + // inside updateGeometryRefs() (called by onDocumentRestore()), we shall + // regenerate Geometry::Ref based on RefIndex. + // + // Note that the regenerated Ref will not be using the new topological + // naming either, because we didn't export them. This is exactly the + // same as if we are opening a legacy file without new names. + // updateGeometryRefs() will know how to handle the name change thanks + // to a flag setup in onUpdateElementReference(). + for(auto &key : externalGeoRef) { + ++index; + auto iter = externalGeoRefMap.find(key); + if(iter == externalGeoRefMap.end()) + continue; + for(auto id : iter->second) { + auto it = externalGeoMap.find(id); + if(it != externalGeoMap.end()) + ExternalGeometryFacade::getFacade(geos[it->second])->setRefIndex(index); + } + } + } + // save the father classes Part::Part2DObject::Save(writer); } @@ -8906,22 +10258,61 @@ void SketchObject::Restore(XMLReader& reader) Part::Part2DObject::Restore(reader); } +void SketchObject::handleChangedPropertyType(Base::XMLReader &reader, + const char *TypeName, App::Property *prop) +{ + if (prop == &Exports) { + if(strcmp(TypeName, "App::PropertyLinkList") == 0) + Exports.Restore(reader); + } +} + +static inline bool checkMigration(Part::PropertyGeometryList &prop) +{ + for (auto g : prop.getValues()) { + if(g->hasExtension(Part::GeometryMigrationExtension::getClassTypeId()) + || !g->hasExtension(SketchGeometryExtension::getClassTypeId())) + return true; + } + return false; +} + void SketchObject::onChanged(const App::Property* prop) { - if (isRestoring() && prop == &Geometry) { - std::vector geom = Geometry.getValues(); - std::vector supportedGeom = supportedGeometry(geom); - // To keep upward compatibility ignore unsupported geometry types - if (supportedGeom.size() != geom.size()) { - Geometry.setValues(supportedGeom); - return; +// if (isRestoring() && prop == &Geometry) { +// std::vector geom = Geometry.getValues(); +// std::vector supportedGeom = supportedGeometry(geom); +// // To keep upward compatibility ignore unsupported geometry types +// if (supportedGeom.size() != geom.size()) { +// Geometry.setValues(supportedGeom); +// return; +// } + if (prop == &Geometry) { + if (isRestoring() && checkMigration(Geometry)) { + // Construction migration to extension + for( auto g : Geometry.getValues()) { + if(g->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) { + auto ext = std::static_pointer_cast( + g->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock()); + + auto gf = GeometryFacade::getFacade(g); // at this point IA geometry is already migrated + + if(ext->testMigrationType(Part::GeometryMigrationExtension::Construction)) { + bool oldconstr = ext->getConstruction(); + if( g->getTypeId() == Part::GeomPoint::getClassTypeId() && !gf->isInternalAligned()) + oldconstr = true; + gf->setConstruction(oldconstr); + } + if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) + gf->setId(ext->getId()); + } + } } } - if (prop == &Geometry || prop == &Constraints) { - - auto doc = getDocument(); + auto doc = getDocument(); + if (prop == &Geometry || prop == &Constraints) { if (doc && doc->isPerformingTransaction()) {// undo/redo setStatus(App::PendingTransactionUpdate, true); } @@ -8951,6 +10342,8 @@ void SketchObject::onChanged(const App::Property* prop) "SketchObject::onChanged(): Unmanaged change of Geometry Property " "results in invalid constraint indices\n"); } + Base::StateLocker lock(internaltransaction, true); + setUpSketch(); } } else {// Change is in Constraints @@ -8979,25 +10372,260 @@ void SketchObject::onChanged(const App::Property* prop) "SketchObject::onChanged(): Unmanaged change of Constraint " "Property results in invalid constraint indices\n"); } + Base::StateLocker lock(internaltransaction, true); + setUpSketch(); } } } } + } else if ( prop == &ExternalGeo && !prop->testStatus(App::Property::User3) ) { + if(doc && doc->isPerformingTransaction()) + setStatus(App::PendingTransactionUpdate, true); + + if (isRestoring() && checkMigration(ExternalGeo)) { + for( auto g : ExternalGeo.getValues()) { + if(g->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) { + auto ext = std::static_pointer_cast( + g->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock()); + std::unique_ptr egf; + if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) { + egf = ExternalGeometryFacade::getFacade(g); + egf->setId(ext->getId()); + } + + if(ext->testMigrationType(Part::GeometryMigrationExtension::ExternalReference)) { + if (!egf) + egf = ExternalGeometryFacade::getFacade(g); + egf->setRef(ext->getRef()); + egf->setRefIndex(ext->getRefIndex()); + egf->setFlags(ext->getFlags()); + } + } + } + } + externalGeoRefMap.clear(); + externalGeoMap.clear(); + std::set detached; + for(int i=0;itestFlag(ExternalGeometryExtension::Detached)) { + if(egf->getRef().size()) { + detached.insert(egf->getRef()); + egf->setRef(std::string()); + } + egf->setFlag(ExternalGeometryExtension::Detached,false); + egf->setFlag(ExternalGeometryExtension::Missing,false); + } + if(egf->getId() > geoLastId) + geoLastId = egf->getId(); + if(!externalGeoMap.emplace(egf->getId(),i).second) { + FC_WARN("duplicate geometry id " << egf->getId() << " -> " << geoLastId+1); + egf->setId(++geoLastId); + externalGeoMap[egf->getId()] = i; + } + if(egf->getRef().size()) + externalGeoRefMap[egf->getRef()].push_back(egf->getId()); + } + if(detached.size()) { + auto objs = ExternalGeometry.getValues(); + assert(externalGeoRef.size() == objs.size()); + auto itObj = objs.begin(); + auto subs = ExternalGeometry.getSubValues(); + auto itSub = subs.begin(); + for(size_t i=0;isecond]; + ExternalGeometryFacade::getFacade(geo)->setRef(std::string()); + } + } + refs.clear(); + } else { + ++itObj; + ++itSub; + } + } + ExternalGeometry.setValues(objs,subs); + } else + signalElementsChanged(); } else if (prop == &ExternalGeometry) { +#ifdef FC_USE_TNP_FIX + if(doc && doc->isPerformingTransaction()) + setStatus(App::PendingTransactionUpdate, true); + + if(!isRestoring()) { + // must wait till onDocumentRestored() when shadow references are + // fully restored + updateGeometryRefs(); + signalElementsChanged(); + } + } else if (prop == &Placement) { + if (ExternalGeometry.getSize() > 0) + touch(); + } else if (prop == &ExpressionEngine) { + auto doc = getDocument(); + if(!isRestoring() + && doc && !doc->isPerformingTransaction() + && noRecomputes + && !managedoperation) + { + // if we do not have a recompute, the sketch must be solved to + // update the DoF of the solver, constraints and UI + try { + auto res = ExpressionEngine.execute(); + if(res) { + FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << res->Why); + delete res; + } + } catch (Base::Exception &e) { + e.ReportException(); + FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << e.what()); + } + solve(); + } +#else // make sure not to change anything while restoring this object if (!isRestoring()) { // external geometry was cleared if (ExternalGeometry.getSize() == 0) { delConstraintsToExternal(); } + } +#endif + } +#if 0 + // For now do not delete anything (#0001791). When changing the support + // face it might be better to check which external geometries can be kept. + else if (prop == &AttachmentSupport) { + // make sure not to change anything while restoring this object + if (!isRestoring()) { + // if support face has changed then clear the external geometry + delConstraintsToExternal(); + for (int i=0; i < getExternalGeometryCount(); i++) { + delExternal(0); + } rebuildExternalGeometry(); } } - +#endif Part::Part2DObject::onChanged(prop); } +void SketchObject::onUpdateElementReference(const App::Property *prop) { + if(prop == &ExternalGeometry) { + updateGeoRef = true; + // Must call updateGeometryRefs() now to avoid the case of recursive + // property change (e.g. temporary object removal in SubShapeBinder) + // afterwards causing assertion failure, although this may mean extra + // call of updateGeometryRefs() later in onChange(). + updateGeometryRefs(); + signalElementsChanged(); + } +} + +void SketchObject::updateGeometryRefs() { + const auto &objs = ExternalGeometry.getValues(); + const auto &subs = ExternalGeometry.getSubValues(); + const auto &shadows = ExternalGeometry.getShadowSubs(); + assert(subs.size() == shadows.size()); + std::vector originalRefs; + std::map refMap; + if(updateGeoRef) { + assert(externalGeoRef.size() == objs.size()); + updateGeoRef = false; + originalRefs = std::move(externalGeoRef); + } + externalGeoRef.clear(); + std::unordered_map legacyMap; + for(int i=0;i<(int)objs.size();++i) { + auto obj = objs[i]; + const std::string &sub=shadows[i].first.size()?shadows[i].first:subs[i]; + externalGeoRef.emplace_back(obj->getNameInDocument()); + auto &key = externalGeoRef.back(); + key += '.'; + + legacyMap[key + Data::oldElementName(sub.c_str())] = i; + + if (!obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) + key += Data::newElementName(sub.c_str()); + if (originalRefs.size() && originalRefs[i] != key) + refMap[originalRefs[i]] = key; + } + bool touched = false; + auto geos = ExternalGeo.getValues(); + if(refMap.empty()) { + for(auto geo : geos) { + auto egf = ExternalGeometryFacade::getFacade(geo); + if(egf->getRefIndex()<0) { + if (egf->getId() < 0 && egf->getRef().size()) { + FC_ERR("External geometry reference corrupted in " << getFullName() + << " Please check."); + // This could happen if someone saved the sketch containing + // external geometries using some rouge releases during the + // migration period. As a remedy, We re-initiate the + // external geometry here to trigger rebuild later, with + // call to rebuildExternalGeometry() + initExternalGeo(); + return; + } + auto it = legacyMap.find(egf->getRef()); + if (it != legacyMap.end() && egf->getRef() != externalGeoRef[it->second]) { + if(getDocument() && !getDocument()->isPerformingTransaction()) { + // FIXME: this is a bug. Find out when and why does this happen + // + // Amendment: maybe the original bug is because of not + // handling external geometry changes during undo/redo, + // which should be considered as normal. So warning only + // if not undo/redo. + // + FC_WARN("Update legacy external reference " << egf->getRef() << " -> " + << externalGeoRef[it->second] << " in " << getFullName()); + } else + FC_LOG("Update undo/redo external reference " << egf->getRef() << " -> " + << externalGeoRef[it->second] << " in " << getFullName()); + touched = true; + egf->setRef(externalGeoRef[it->second]); + } + continue; + } + else if(egf->getRefIndex() < (int)externalGeoRef.size() + && egf->getRef() != externalGeoRef[egf->getRefIndex()]) + { + touched = true; + egf->setRef(externalGeoRef[egf->getRefIndex()]); + } + egf->setRefIndex(-1); + } + }else{ + for(auto &v : refMap) { + auto it = externalGeoRefMap.find(v.first); + if(it == externalGeoRefMap.end()) + continue; + for(long id : it->second) { + auto iter = externalGeoMap.find(id); + if(iter!=externalGeoMap.end()) { + auto &geo = geos[iter->second]; + geo = geo->clone(); + auto egf = ExternalGeometryFacade::getFacade(geo); + FC_LOG(getFullName() << " ref change on ExternalEdge" + << iter->second-1 << ' ' << egf->getRef() << " -> " << v.second); + egf->setRef(v.second); + touched = true; + } + } + } + } + if(touched) + ExternalGeo.setValues(std::move(geos)); +} + void SketchObject::onUndoRedoFinished() { // upon undo/redo, PropertyConstraintList does not have updated valid geometry keys, which @@ -9108,6 +10736,36 @@ void SketchObject::onDocumentRestored() try { restoreFinished(); Part::Part2DObject::onDocumentRestored(); + + if (getDocument()->testStatus(App::Document::Importing)) { + App::GeoFeatureGroupExtension *grp = nullptr; + auto grpObj = App::GeoFeatureGroupExtension::getGroupOfObject(this); + if (grpObj) + grp = grpObj->getExtensionByType(true); + + auto exports = Exports.getValues(); + bool touched = false; + for (auto &obj : exports) { + auto exp = Base::freecad_dynamic_cast(obj); + if (!exp || exp->BaseRefs.getValue() == this) + continue; + auto newexp = Base::freecad_dynamic_cast( + getDocument()->addObject("Sketcher::SketchExport", "Export")); + if (grp) + grp->addObject(newexp); + if (exp->Label.getStrValue() != exp->getNameInDocument()) + newexp->Label.setValue(exp->Label.getValue()); + else + newexp->Label.setValue(newexp->getNameInDocument()); + newexp->BaseRefs.setValue(this, exp->BaseRefs.getSubValues(false)); + newexp->Visibility.setValue(exp->Visibility.getValue()); + obj = newexp; + touched = true; + } + if (touched) + Exports.setValues(exports); + } + } catch (...) { } @@ -9118,9 +10776,28 @@ void SketchObject::restoreFinished() try { migrateSketch(); +#ifdef FC_USE_TNP_FIX + updateGeometryRefs(); + if(ExternalGeo.getSize()<=2) { + if (ExternalGeo.getSize() < 2) + initExternalGeo(); + for(auto &key : externalGeoRef) { + long id = getDocument()->getStringHasher()->getID(key.c_str()).value(); + if(geoLastId < id) + geoLastId = id; + externalGeoRefMap[key].push_back(id); + } + rebuildExternalGeometry(); + if(ExternalGeometry.getSize()+2!=ExternalGeo.getSize()) + FC_WARN("Failed to restore some external geometry in " << getFullName()); + }else + acceptGeometry(); + +#else validateExternalLinks(); rebuildExternalGeometry(); Constraints.acceptGeometry(getCompleteGeometry()); +#endif synchroniseGeometryState(); // this may happen when saving a sketch directly in edit mode // but never performed a recompute before @@ -9128,8 +10805,32 @@ void SketchObject::restoreFinished() if (this->solve(true) == 0) Shape.setValue(solvedSketch.toShape()); } - } - catch (...) { + + // Sanity check on constraints with expression. It is added because the + // way SketchObject syncs expression and constraints heavily relies on + // proper setup of undo/redo transactions. The missing transaction in + // EditDatumDialog may cause stray or worse wrongly bound expressions. + for (auto &v : ExpressionEngine.getExpressions()) { + if (v.first.getProperty() != &Constraints) + continue; + const Constraint * cstr = nullptr; + try { + cstr = Constraints.getConstraint(v.first); + } catch (Base::Exception &) { + } + if (!cstr || !cstr->isDimensional()) { + FC_WARN((cstr ? "Invalid" : "Orphan") + << " constraint expression in " + << getFullName() << "." + << v.first.toString() + << ": " << v.second->toString()); + ExpressionEngine.setValue(v.first, nullptr); + } + } + } catch (Base::Exception &e) { + e.ReportException(); + FC_ERR("Error while restoring " << getFullName()); + } catch (...) { } } @@ -9431,6 +11132,7 @@ int SketchObject::port_reversedExternalArcs(bool justAnalyze) if (geoId <= GeoEnum::RefExt && (posId == Sketcher::PointPos::start || posId == Sketcher::PointPos::end)) { // we are dealing with a link to an endpoint of external geom +// Part::Geometry* g = this->ExternalGeo[-geoId - 1]; Part::Geometry* g = this->ExternalGeo[-geoId - 1]; if (g->is()) { const Part::GeomArcOfCircle* segm = @@ -9596,6 +11298,17 @@ App::DocumentObject *SketchObject::getSubObject( const char *mapped = Data::isMappedElement(subname); if(!subname || !subname[0]) return Part2DObject::getSubObject(subname,pyObj,pmat,transform,depth); + const char *element = Data::findElementName(subname); + if(element != subname) { + const char *dot = strchr(subname,'.'); + if(!dot) + return 0; + std::string name(subname,dot-subname); + auto child = Exports.find(name.c_str()); + if(!child) + return 0; + return child->getSubObject(dot+1,pyObj,pmat,true,depth+1); + } Data::IndexedName indexedName = checkSubName(subname); int index = indexedName.getIndex(); @@ -9685,8 +11398,14 @@ App::DocumentObject *SketchObject::getSubObject( if(pmat) point = (*pmat)*point; shape = BRepBuilderAPI_MakeVertex(gp_Pnt(point.x,point.y,point.z)).Vertex(); + // Originally in ComplexGeoData::setElementName + // LinkStable/src/App/ComplexGeoData.cpp#L1631 + // No longer possible after map separated in ElementMap.cpp + if ( !shape.hasElementMap() ) { + shape.resetElementMap(std::make_shared()); + } shape.setElementName(Data::IndexedName::fromConst("Vertex", 1), - Data::MappedName::fromRawData(name.c_str()), 0); + Data::MappedName::fromRawData(name.c_str()),0); } shape.Tag = getID(); *pyObj = Py::new_reference_to(Part::shape2pyshape(shape)); @@ -9796,7 +11515,7 @@ App::ElementNamePair SketchObject::getElementName( // Todo: Toponaming Project March 2024: This method override breaks the sketcher - selection and deletion // of constraints ceases to work. See #13169. We need to prove that this works before // enabling it. - return Part2DObject::getElementName(name,type); +// return Part2DObject::getElementName(name,type); #ifndef FC_USE_TNP_FIX return Part2DObject::getElementName(name,type); #endif @@ -9949,6 +11668,31 @@ Data::IndexedName SketchObject::checkSubName(const char *subname) const{ return Data::IndexedName(); } +Data::IndexedName SketchObject::shapeTypeFromGeoId(int geoId, PointPos posId) const { + if(geoId == GeoEnum::HAxis) { + if(posId == PointPos::start) + return Data::IndexedName::fromConst("RootPoint", 0); + return Data::IndexedName::fromConst("H_Axis", 0); + }else if(geoId == GeoEnum::VAxis) + return Data::IndexedName::fromConst("V_Axis", 0); + + if (posId == PointPos::none) { + auto geo = getGeometry(geoId); + if (geo && geo->isDerivedFrom(Part::GeomPoint::getClassTypeId())) + posId = PointPos::start; + } + if(posId != PointPos::none) { + int idx = getVertexIndexGeoPos(geoId, posId); + if(idx < 0) + return Data::IndexedName(); + return Data::IndexedName::fromConst("Vertex", idx+1); + } + if(geoId >= 0) + return Data::IndexedName::fromConst("Edge", geoId+1); + else + return Data::IndexedName::fromConst("ExternalEdge", -geoId-2); +} + bool SketchObject::geoIdFromShapeType(const Data::IndexedName & indexedName, int &geoId, PointPos &posId) const @@ -10041,6 +11785,34 @@ std::string SketchObject::convertSubName(const Data::IndexedName &indexedName, b return ss.str(); } +std::string SketchObject::getGeometryReference(int GeoId) const { + auto geo = getGeometry(GeoId); + if (!geo) + return std::string(); + auto egf = ExternalGeometryFacade::getFacade(geo); + if (egf->getRef().empty()) + return std::string(); + + const std::string &ref = egf->getRef(); + + if(egf->testFlag(ExternalGeometryExtension::Missing)) + return std::string("? ") + ref; + + auto pos = ref.find('.'); + if(pos == std::string::npos) + return ref; + std::string objName = ref.substr(0,pos); + auto obj = getDocument()->getObject(objName.c_str()); + if(!obj) + return ref; + + std::pair elementName; + App::GeoFeature::resolveElement(obj,ref.c_str()+pos+1,elementName); + if(elementName.second.size()) + return objName + "." + elementName.second; + return ref; +} + int SketchObject::autoConstraint(double precision, double angleprecision, bool includeconstruction) { return analyser->autoconstraint(precision, angleprecision, includeconstruction); @@ -10269,4 +12041,164 @@ PyObject* Sketcher::SketchObjectPython::getPyObject() // explicit template instantiation template class SketcherExport FeaturePythonT; }// namespace App + +// --------------------------------------------------------- + +PROPERTY_SOURCE(Sketcher::SketchExport, Part::Part2DObject) + +SketchExport::SketchExport() { + ADD_PROPERTY_TYPE(Base,(0),"", + (App::PropertyType)(App::Prop_Hidden|App::Prop_ReadOnly), + "(Deprecated) Base sketch object name"); + + ADD_PROPERTY_TYPE(Refs,(),"",App::Prop_Hidden, + "(Deprecated) Sketch geometry references"); + + ADD_PROPERTY_TYPE(BaseRefs,(0),"",App::Prop_None,"Base sketch references"); + + ADD_PROPERTY_TYPE(SyncPlacement,(true),"", + App::Prop_None,"Synchronize placement with parent sketch if not attached"); + + ADD_PROPERTY_TYPE(ScaleVector, (1, 1, 1) ,"", App::Prop_None, + "Scale vector intended for mirror. Note that using the scale vector for\n" + "non-uniform scaling transformed the geometries into BSpline curves"); +} + +SketchExport::~SketchExport() +{} + +App::DocumentObject *SketchExport::getBase() const { + return BaseRefs.getValue(); +} + +void SketchExport::onDocumentRestored() +{ + if (!BaseRefs.getValue() && Base.getValue()) + BaseRefs.setValue(Base.getValue(), Refs.getValues()); + if (Shape.getShape().getPlacement() != Placement.getValue()) + Placement.touch(); + Part::Part2DObject::onDocumentRestored(); +} + +App::DocumentObjectExecReturn *SketchExport::execute(void) { + try { + App::DocumentObjectExecReturn* rtn = Part2DObject::execute();//to positionBySupport + if(rtn!=App::DocumentObject::StdReturn) + //error + return rtn; + } + catch (const Base::Exception& e) { + return new App::DocumentObjectExecReturn(e.what()); + } + + auto base = Base::freecad_dynamic_cast(getBase()); + if(!base) + return new App::DocumentObjectExecReturn("Missing parent sketch"); + if(update() && SyncPlacement.getValue() && !positionBySupport()) + Placement.setValue(base->Placement.getValue()); + return App::DocumentObject::StdReturn; +} + +void SketchExport::onChanged(const App::Property* prop) { + auto doc = getDocument(); + if(prop == &BaseRefs) { + if(!isRestoring() && doc && !doc->isPerformingTransaction()) + update(); + Base.setValue(BaseRefs.getValue()); + Refs.setValue(BaseRefs.getSubValues(true)); + } else if(prop == &Shape) { + // We used to bypass Part::Feature handling of shape change, which sets + // the placement the same as the shape's. But this is causes bad thing + // to happen. We'll fix that in onDocumentRestored(). + if (getDocument() && getDocument()->testStatus(App::Document::Restoring)) { + DocumentObject::onChanged(prop); + return; + } + } + Part2DObject::onChanged(prop); +} + +std::set SketchExport::getRefs() const { + std::set refSet; + const auto &refs = BaseRefs.getSubValues(); + refSet.insert(refs.begin(),refs.end()); + if(refSet.size()>1) + refSet.erase(""); + return refSet; +} + +bool SketchExport::update() { + auto base = getBase(); + if(!base) + return false; + std::vector points; + std::vector shapes; + for(const auto &ref : getRefs()) { + // Obtain the shape without feature's placement transformation, because + // we may have our own support. + auto shape = Part::Feature::getTopoShape(base,ref.c_str(),true,0,0,false,false); + if(shape.isNull()) { + FC_ERR("Invalid element reference: " << ref); + throw Base::RuntimeError("Invalid element reference"); + } + if(!shape.hasSubShape(TopAbs_EDGE)) + points.push_back(shape.makeElementCopy(Part::OpCodes::SketchExport)); + else + shapes.push_back(shape.makeElementCopy()); + } + Part::TopoShape res; + if(shapes.size()) { + res.makeElementWires(shapes,Part::OpCodes::SketchExport); + shapes.clear(); + if(points.size()) { + // Check if the vertex is already included in the wires + for(auto &point : points) { + auto name = point.getMappedName( + Data::IndexedName::fromConst("Vertex", 1)); + if(name && res.getIndexedName(name)) + continue; + shapes.push_back(point); + } + if(shapes.size()) { + shapes.push_back(res); + res.makeElementCompound(shapes); + } + } + }else if(points.empty()) + return false; + else + res.makeElementCompound(points,0,Part::TopoShape::SingleShapeCompoundCreationPolicy::returnShape); + + const auto &scale = ScaleVector.getValue(); + if ((fabs(scale.x - 1.0) > Precision::Confusion() + || fabs(scale.y - 1.0) > Precision::Confusion() + || fabs(scale.z - 1.0) > Precision::Confusion()) + && fabs(scale.x) > Precision::Confusion() + && fabs(scale.y) > Precision::Confusion() + && fabs(scale.z) > Precision::Confusion()) + { + Base::Matrix4D mat; + mat.scale(scale); + res.transformShape(mat, false, true); + } + + Shape.setValue(res); + return true; +} + +void SketchExport::handleChangedPropertyType(Base::XMLReader &reader, + const char *TypeName, App::Property *prop) +{ + if (prop == &Base && strcmp(TypeName, "App::PropertyString") == 0) { + App::PropertyString p; + p.Restore(reader); + auto obj = getDocument()->getObject(p.getValue()); + if(!obj) { + FC_ERR("Cannot find parent sketch '" << p.getValue() << "' of " << getFullName()); + return; + } + Base.setValue(obj); + } +} + // clang-format on diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 54921dfdc8c8..cc1a781fc4e2 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -66,8 +66,14 @@ class SketcherExport SketchObject: public Part::Part2DObject Part ::PropertyGeometryList Geometry; Sketcher::PropertyConstraintList Constraints; App ::PropertyLinkSubList ExternalGeometry; + App ::PropertyLinkListHidden Exports; + Part ::PropertyGeometryList ExternalGeo; App ::PropertyBool FullyConstrained; + App ::PropertyPrecision ArcFitTolerance; + // App ::PropertyInteger ExternalBSplineMaxDegree; + // App ::PropertyPrecision ExternalBSplineTolerance; Part ::PropertyPartShape InternalShape; + App ::PropertyPrecision InternalTolerance; App ::PropertyBool MakeInternals; /** @name methods override Feature */ //@{ @@ -172,16 +178,31 @@ class SketcherExport SketchObject: public Part::Part2DObject /// Carbon copy another sketch geometry and constraints int carbonCopy(App::DocumentObject* pObj, bool construction = true); /// add an external geometry reference - int addExternal(App::DocumentObject* Obj, const char* SubName); + int addExternal(App::DocumentObject* Obj, + const char* SubName, + bool defining = false, + bool intersection = false); /** delete external * ExtGeoId >= 0 with 0 corresponding to the first user defined * external geometry */ int delExternal(int ExtGeoId); + int delExternal(const std::vector& ExtGeoIds); + /// attach a link reference to an external geometry + int + attachExternal(const std::vector& geoIds, App::DocumentObject* Obj, const char* SubName); + int detachExternal(const std::vector& geoIds); /** deletes all external geometry */ int delAllExternal(); + const Part::Geometry* _getGeometry(int GeoId) const; + int setGeometry(int GeoId, const Part::Geometry*); + /// returns GeoId of all geometries projected from the same external geometry reference + std::vector getRelatedGeometry(int GeoId) const; + /// Sync frozen external geometries + int syncGeometry(const std::vector& geoIds); + /** returns a pointer to a given Geometry index, possible indexes are: * id>=0 for user defined geometries, * id==-1 for the horizontal sketch axis, @@ -192,7 +213,11 @@ class SketcherExport SketchObject: public Part::Part2DObject typename GeometryT = Part::Geometry, typename = typename std::enable_if< std::is_base_of::type>::value>::type> - const GeometryT* getGeometry(int GeoId) const; + // const GeometryT* getGeometry(int GeoId) const; + const GeometryT* getGeometry(int GeoId) const + { + return static_cast(_getGeometry(GeoId)); + } std::unique_ptr getGeometryFacade(int GeoId) const; @@ -204,15 +229,19 @@ class SketcherExport SketchObject: public Part::Part2DObject /// returns a list of projected external geometries const std::vector& getExternalGeometry() const { - return ExternalGeo; + // return ExternalGeo; + return ExternalGeo.getValues(); } /// rebuilds external geometry (projection onto the sketch plane) - void rebuildExternalGeometry(); + void rebuildExternalGeometry(bool defining = false, bool intersection = false); /// returns the number of external Geometry entities int getExternalGeometryCount() const { - return ExternalGeo.size(); + // return ExternalGeo.size(); + return ExternalGeo.getSize(); } + /// auto fix external geometry references + void fixExternalGeometry(const std::vector& geoIds = {}); /// retrieves a vector containing both normal and external Geometry (including the sketch axes) std::vector getCompleteGeometry() const; @@ -516,6 +545,9 @@ class SketcherExport SketchObject: public Part::Part2DObject unsigned int getMemSize() const override; void Save(Base::Writer& /*writer*/) const override; void Restore(Base::XMLReader& /*reader*/) override; + void handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, + App::Property* prop) override; /// returns the number of construction lines (to be used as axes) int getAxisCount() const override; @@ -704,6 +736,9 @@ class SketcherExport SketchObject: public Part::Part2DObject return geoIdFromShapeType(shapetype, geoId, posId); } + /// Return a human friendly element reference of an external geometry + std::string getGeometryReference(int GeoId) const; + std::string convertSubName(const char* subname, bool postfix = true) const; std::string convertSubName(const std::string& subname, bool postfix = true) const @@ -809,6 +844,8 @@ class SketcherExport SketchObject: public Part::Part2DObject std::vector supportedGeometry(const std::vector& geoList) const; + void updateGeoHistory(); + void generateId(Part::Geometry* geo); /*! \brief Transfer constraints on lines being filleted. @@ -827,6 +864,14 @@ class SketcherExport SketchObject: public Part::Part2DObject // check whether constraint may be changed driving status int testDrivingChange(int ConstrId, bool isdriving); + void initExternalGeo(); + + void onUpdateElementReference(const App::Property*) override; + + void delExternalPrivate(const std::set& ids, bool removeReference); + + void updateGeometryRefs(); + void onUndoRedoFinished() override; // migration functions @@ -879,7 +924,7 @@ class SketcherExport SketchObject: public Part::Part2DObject /// Flag to allow carbon copy from misaligned geometry bool allowUnaligned; - std::vector ExternalGeo; + // std::vector ExternalGeo; std::vector VertexId2GeoId; std::vector VertexId2PosId; @@ -932,9 +977,30 @@ class SketcherExport SketchObject: public Part::Part2DObject bool internaltransaction; - // indicates whether changes to properties are the deed of SketchObject or not (for input - // validation) - bool managedoperation; + bool managedoperation; // indicates whether changes to properties are the deed of SketchObject + // or not (for input validation) + + // mapping from ExternalGeometry[*] to ExternalGeo[*].Id + // Some external geometry may generate more than one projection + std::map> externalGeoRefMap; + bool updateGeoRef = false; + + // backup of ExternalGeometry in case of element reference change + std::vector externalGeoRef; + + // mapping from ExternalGeo[*].Id to index of ExternalGeo + std::map externalGeoMap; + + // mapping from Geometry[*].Id to index of Geometry + std::map geoMap; + + int geoHistoryLevel; + std::vector geoIdHistory; + long geoLastId; + + class GeoHistory; + std::unique_ptr geoHistory; + mutable std::map internalElementMap; }; @@ -973,23 +1039,58 @@ inline int SketchObject::moveTemporaryPoint(int geoId, return solvedSketch.movePoint(geoId, pos, toPoint, relative); } -template -const GeometryT* SketchObject::getGeometry(int GeoId) const +// template +// const GeometryT* SketchObject::getGeometry(int GeoId) const +//{ +// if (GeoId >= 0) { +// const std::vector& geomlist = getInternalGeometry(); +// if (GeoId < int(geomlist.size())) { +// return static_cast(geomlist[GeoId]); +// } +// } +//// else if (-GeoId <= int(ExternalGeo.size())) { +// else if (-GeoId <= int(ExternalGeo.getSize())) { +// return static_cast(ExternalGeo[-GeoId - 1]); +// } +// +// return nullptr; +//} + +using SketchObjectPython = App::FeaturePythonT; + +// --------------------------------------------------------- + +class SketcherExport SketchExport: public Part::Part2DObject { - if (GeoId >= 0) { - const std::vector& geomlist = getInternalGeometry(); - if (GeoId < int(geomlist.size())) { - return static_cast(geomlist[GeoId]); - } - } - else if (-GeoId <= int(ExternalGeo.size())) { - return static_cast(ExternalGeo[-GeoId - 1]); + PROPERTY_HEADER(Sketcher::SketchObject); + +public: + SketchExport(); + ~SketchExport() override; + + App::PropertyStringList Refs; + App::PropertyLink Base; + App::PropertyLinkSub BaseRefs; + App::PropertyBool SyncPlacement; + App::PropertyVector ScaleVector; + + App::DocumentObjectExecReturn* execute(void) override; + void onChanged(const App::Property* /*prop*/) override; + const char* getViewProviderName(void) const override + { + return "SketcherGui::ViewProviderSketchExport"; } - return nullptr; -} + bool update(); -using SketchObjectPython = App::FeaturePythonT; + App::DocumentObject* getBase() const; + std::set getRefs() const; + + void handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, + App::Property* prop) override; + void onDocumentRestored() override; +}; } // namespace Sketcher From e15646d158ea0520abe6d70ca1ddc891bf28c4f4 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 18 Jul 2024 21:13:14 -0400 Subject: [PATCH 2/5] Toponaming: Tests, cleanup, and fix axis drawing --- src/Mod/Part/App/TopoShapeExpansion.cpp | 10 +- src/Mod/Sketcher/App/SketchObject.cpp | 237 +++++++++--------- src/Mod/Sketcher/App/SketchObject.h | 35 +-- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 76 +++--- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 2 + .../SketcherTests/TestSketcherSolver.py | 107 ++++++++ tests/src/Mod/Sketcher/App/SketchObject.cpp | 14 +- 7 files changed, 288 insertions(+), 193 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 426ed4361072..d1819cb5514b 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -4783,12 +4783,10 @@ TopoShape& TopoShape::makeElementRefine(const TopoShape& shape, const char* op, return {}; std::vector res; - int type = shape.shapeType(); - for(;;) { - if(--type < 0) - break; - const char *shapetype = shapeName((TopAbs_ShapeEnum)type).c_str(); - for(int idx : findAncestors(shape.getShape(), (TopAbs_ShapeEnum)type)) + + for (int type = shape.shapeType() - 1; type >= 0; type--) { + const char* shapetype = shapeName((TopAbs_ShapeEnum)type).c_str(); + for (int idx : findAncestors(shape.getShape(), (TopAbs_ShapeEnum)type)) res.emplace_back(shapetype, idx); } return res; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index ff8705c54b2b..399150743494 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -161,16 +161,6 @@ SketchObject::SketchObject() "Sketch", (App::PropertyType)(App::Prop_None), "Tolerance for fitting arcs of projected external geometry"); - // ADD_PROPERTY_TYPE(ExternalBSplineMaxDegree, - // (0), - // "Sketch", - // (App::Prop_None), - // "Maximum degree of imported external BSpline. Zero to disable simplification"); - // ADD_PROPERTY_TYPE(ExternalBSplineTolerance, - // (0.0), - // "Sketch", - // (App::Prop_None), - // "Tolerance for simplifying imported external BSpline"); geoLastId = 0; ADD_PROPERTY(InternalShape, @@ -186,22 +176,6 @@ SketchObject::SketchObject() allowOtherBody = true; allowUnaligned = true; -// for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); -// ++it) -// if (*it) -// delete *it; -// ExternalGeo.clear(); -// auto HLine = GeometryTypedFacade::getTypedFacade(); -// auto VLine = GeometryTypedFacade::getTypedFacade(); -// HLine->getTypedGeometry()->setPoints(Base::Vector3d(0, 0, 0), Base::Vector3d(1, 0, 0)); -// VLine->getTypedGeometry()->setPoints(Base::Vector3d(0, 0, 0), Base::Vector3d(0, 1, 0)); -// HLine->setConstruction(true); -// VLine->setConstruction(true); -// ExternalGeo.push_back(HLine->getGeometry()); -// ExternalGeo.push_back(VLine->getGeometry()); -// HLine->setOwner(false);// we have transferred the ownership to ExternalGeo -// VLine->setOwner(false);// we have transferred the ownership to ExternalGeo - initExternalGeo(); rebuildVertexIndex(); @@ -236,14 +210,7 @@ SketchObject::SketchObject() registerElementCache(internalPrefix(), &InternalShape); } -SketchObject::~SketchObject() -{ -// for (std::vector::iterator it = ExternalGeo.begin(); it != ExternalGeo.end(); -// ++it) -// if (*it) -// delete *it; -// ExternalGeo.clear(); - +SketchObject::~SketchObject() { delete analyser; } @@ -252,8 +219,6 @@ void SketchObject::setupObject() ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher"); ArcFitTolerance.setValue(hGrpp->GetFloat("ArcFitTolerance", Precision::Confusion()*10.0)); -// ExternalBSplineMaxDegree.setValue(hGrpp->GetInt("ExternalBSplineMaxDegree", 5)); -// ExternalBSplineTolerance.setValue(hGrpp->GetFloat("ExternalBSplineTolerance", 1e-4)); MakeInternals.setValue(hGrpp->GetBool("MakeInternals", false)); inherited::setupObject(); } @@ -346,8 +311,7 @@ App::DocumentObjectExecReturn* SketchObject::execute() return App::DocumentObject::StdReturn; } -static bool inline checkSmallEdge(const Part::TopoShape &s) -{ +static bool inline checkSmallEdge(const Part::TopoShape &s) { if (s.shapeType() != TopAbs_EDGE) return false; BRepAdaptor_Curve adapt(TopoDS::Edge(s.getShape())); @@ -401,7 +365,6 @@ void SketchObject::buildShape() { #endif } - // FIXME: Commented since ExternalGeometryFacade is not added for(size_t i=2;i geomlist = solvedSketch.extractGeometry(); -// Geometry.setValues(geomlist); -// for (std::vector::iterator it = geomlist.begin(); it != geomlist.end(); -// ++it) -// if (*it) -// delete *it; Part::PropertyGeometryList tmp; tmp.setValues(std::move(geomlist)); // Only set values if there is actual changes @@ -1349,6 +1307,10 @@ int SketchObject::toggleVirtualSpace(int ConstrId) this->Constraints.setValues(std::move(newVals)); + // Solver didn't actually update, but we need this to inform view provider + // to redraw + signalSolverUpdate(); + return 0; } @@ -1925,26 +1887,109 @@ int SketchObject::setConstruction(int GeoId, bool on) // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); +#ifdef FC_USE_TNP_FIX + Part::PropertyGeometryList *prop; + int idx; + if (GeoId >= 0) { + prop = &Geometry; + if (GeoId < Geometry.getSize()) + idx = GeoId; + else + return -1; + }else if (GeoId <= GeoEnum::RefExt && -GeoId-1 < ExternalGeo.getSize()) { + prop = &ExternalGeo; + idx = -GeoId-1; + }else + return -1; +#else const std::vector& vals = getInternalGeometry(); if (GeoId < 0 || GeoId >= int(vals.size())) return -1; if (getGeometryFacade(GeoId)->isInternalAligned()) return -1; +#endif // While it may seem that there is not a need to trigger an update at this time, because the // solver has its own copy of the geometry, and updateColors of the viewprovider may be // triggered by the clearselection of the UI command, this won't update the elements widget, in // the accumulative of actions it is judged that it is worth to trigger an update here. +#ifdef FC_USE_TNP_FIX + std::unique_ptr geo(prop->getValues()[idx]->clone()); + if(prop == &Geometry) + GeometryFacade::setConstruction(geo.get(), on); + else { + auto egf = ExternalGeometryFacade::getFacade(geo.get()); + egf->setFlag(ExternalGeometryExtension::Defining, on); + } + + prop->set1Value(idx,std::move(geo)); + +#else std::unique_ptr geo(vals[GeoId]->clone()); GeometryFacade::setConstruction(geo.get(), on); this->Geometry.set1Value(GeoId, std::move(geo)); - +#endif solverNeedsUpdate = true; return 0; } +int SketchObject::toggleExternalGeometryFlag(const std::vector &geoIds, + const std::vector &flags) +{ + if (flags.empty()) + return 0; + auto flag = flags.front(); + + Base::StateLocker lock(managedoperation, true); // no need to check input data validity as this is an sketchobject managed operation. + + bool update = false; + bool touched = false; + auto geos = ExternalGeo.getValues(); + std::set idSet(geoIds.begin(),geoIds.end()); + for(auto geoId : geoIds) { + if(geoId > GeoEnum::RefExt || -geoId-1>=ExternalGeo.getSize()) + continue; + if(!idSet.count(geoId)) + continue; + idSet.erase(geoId); + int idx = -geoId-1; + auto &geo = geos[idx]; + auto egf = ExternalGeometryFacade::getFacade(geo); + bool value = !egf->testFlag(flag); + if(egf->getRef().size()) { + for(auto gid : getRelatedGeometry(geoId)) { + if(gid == geoId) + continue; + int idx = -gid-1; + auto &g = geos[idx]; + g = g->clone(); + auto egf = ExternalGeometryFacade::getFacade(g); + egf->setFlag(flag, value); + for (size_t i=1; isetFlag(flags[i], value); + idSet.erase(gid); + } + } + geo = geo->clone(); + egf->setGeometry(geo); + egf->setFlag(flag, value); + for (size_t i=1; isetFlag(flags[i], value); + if (value || flag != ExternalGeometryExtension::Frozen) + update = true; + touched = true; + } + + if(!touched) + return -1; + ExternalGeo.setValues(geos); + if (update) + rebuildExternalGeometry(); + return 0; +} + void SketchObject::addGeometryState(const Constraint* cstr) const { const std::vector& vals = getInternalGeometry(); @@ -4780,9 +4825,6 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, } // add the geometry -// std::map geoIdMap; -// std::map isStartEndInverted; -// std::vector newgeoVals(getInternalGeometry()); std::vector symmetricVals = getSymmetric(geoIdList, geoIdMap, isStartEndInverted, refGeoId, refPosId); newgeoVals.insert(newgeoVals.end(), symmetricVals.begin(), symmetricVals.end()); @@ -5147,7 +5189,6 @@ std::vector SketchObject::getSymmetric(const std::vector& auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); - // double df= geosymaoe->getFocal(); Base::Vector3d f1 = geosymaoe->getFocus(); Base::Vector3d sf1 = @@ -5342,7 +5383,6 @@ std::vector SketchObject::getSymmetric(const std::vector& geosymbsp->setPoles(poles); - // isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { auto* geosympoint = static_cast(geosym); @@ -6878,7 +6918,6 @@ bool SketchObject::convertToNURBS(int GeoId) Base::StateLocker lock(managedoperation, true); if (GeoId > getHighestCurveIndex() -// || (GeoId < 0 && -GeoId > static_cast(ExternalGeo.size())) || GeoId == -1 || (GeoId < 0 && -GeoId > static_cast(ExternalGeo.getSize())) || GeoId == -1 || GeoId == -2) return false; @@ -7715,7 +7754,6 @@ int SketchObject::addExternal(App::DocumentObject *Obj, const char* SubName, boo // set the Link list. ExternalGeometry.setValues(Objects, SubElements); try { -// rebuildExternalGeometry(); rebuildExternalGeometry(defining, intersection); } catch (const Base::Exception& e) { @@ -8014,7 +8052,6 @@ int SketchObject::attachExternal( } auto geos = ExternalGeo.getValues(); -// auto geos = ExternalGeo; std::vector Objects = ExternalGeometry.getValues(); auto itObj = Objects.begin(); @@ -8026,7 +8063,7 @@ int SketchObject::attachExternal( for(auto &key : externalGeoRef) { if (*itObj == Obj && *itSub == SubName){ - FC_ERR("Duplicdate external element reference in " << getFullName() << ": " << key); + FC_ERR("Duplicate external element reference in " << getFullName() << ": " << key); return -1; } // detach old reference @@ -8062,7 +8099,6 @@ int SketchObject::attachExternal( std::vector SketchObject::getRelatedGeometry(int GeoId) const { std::vector res; if(GeoId>GeoEnum::RefExt || -GeoId-1>=ExternalGeo.getSize()) -// if(GeoId>GeoEnum::RefExt || -GeoId-1>=ExternalGeo.size()) return res; auto geo = getGeometry(GeoId); if(!geo) @@ -8084,7 +8120,6 @@ std::vector SketchObject::getRelatedGeometry(int GeoId) const { int SketchObject::syncGeometry(const std::vector &geoIds) { bool touched = false; auto geos = ExternalGeo.getValues(); -// auto geos = ExternalGeo; std::set idSet; for(int geoId : geoIds) { auto geo = getGeometry(geoId); @@ -8095,7 +8130,6 @@ int SketchObject::syncGeometry(const std::vector &geoIds) { } for(int geoId : idSet) { if(geoId <= GeoEnum::RefExt && -geoId-1 < ExternalGeo.getSize()) { -// if(geoId <= GeoEnum::RefExt && -geoId-1 < ExternalGeo.size()) { auto &geo = geos[-geoId-1]; geo = geo->clone(); ExternalGeometryFacade::getFacade(geo)->setFlag(ExternalGeometryExtension::Sync); @@ -8115,7 +8149,6 @@ const Part::Geometry* SketchObject::_getGeometry(int GeoId) const return geomlist[GeoId]; } else if (GeoId < 0 && -GeoId-1 < ExternalGeo.getSize()) -// else if (GeoId < 0 && -GeoId-1 < ExternalGeo.size()) return ExternalGeo[-GeoId-1]; return nullptr; @@ -8127,7 +8160,6 @@ int SketchObject::getCompleteGeometryIndex(int GeoId) const if (GeoId < int(Geometry.getSize())) return GeoId; } -// else if (-GeoId <= int(ExternalGeo.size())) else if (-GeoId <= int(ExternalGeo.getSize())) return -GeoId - 1; @@ -8136,7 +8168,6 @@ int SketchObject::getCompleteGeometryIndex(int GeoId) const int SketchObject::getGeoIdFromCompleteGeometryIndex(int completeGeometryIndex) const { -// int completeGeometryCount = int(Geometry.getSize() + ExternalGeo.size()); int completeGeometryCount = int(Geometry.getSize() + ExternalGeo.getSize()); if (completeGeometryIndex < 0 || completeGeometryIndex >= completeGeometryCount) @@ -8258,14 +8289,14 @@ Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Pl // Project an edge to a line. Only works if the edge is planar and its plane is // perpendicular to the projection plane. This function is to work around OCC -// normal projection bug which seems to only repsect the start and ending points +// normal projection bug which seems to only respect the start and ending points // of an arc but disregarding any extreme points. OCC also has trouble handling // BSpline projection to a straight line. Although it does correctly projects // the line including extreme bounds, it will produce a BSpline with degree // more than one. // // The work around here is to use an aligned bounding box of the edge to get -// the projection of the extremem points to construct the projected line. +// the projection of the extreme points to construct the projected line. Part::Geometry* projectEdgeToLine(const TopoDS_Edge &edge, const Base::Placement& invPlm) { @@ -8342,7 +8373,7 @@ void getParameterRange(Handle(Geom_Curve) curve, // The reason of this function is because the first/last parameter reported // from some curve does not really corresponds to the first/last vertex of // the edge. I can only guess this is because the curve (in some cases) is - // actually computed on demaond from surface (in BRepAdaptor_Curve maybe). + // actually computed on demand from surface (in BRepAdaptor_Curve maybe). // And in the process, there is something off in tolerance causing the // derived parameter not matching the value corresponding to the position of // the actual vertex. @@ -8364,7 +8395,7 @@ void adjustParameterRange(const TopoDS_Edge &edge, // lower arc. Because projection orientation may swap the first and last // parameter of the original curve. // - // We project the middel point of the original curve to the projected curve + // We project the middle point of the original curve to the projected curve // to decide whether to flip the parameters. Handle(Geom_Curve) origCurve = BRepAdaptor_Curve(edge).Curve().Curve(); @@ -8589,13 +8620,13 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) auto obj = getDocument()->getObject(objName.c_str()); if(!obj) continue; - std::pair elementName; + App::ElementNamePair elementName; App::GeoFeature::resolveElement(obj,ref.c_str()+pos+1,elementName); - if(elementName.second.size() - && !App::GeoFeature::hasMissingElement(elementName.second.c_str())) + if(elementName.oldName.size() + && !App::GeoFeature::hasMissingElement(elementName.oldName.c_str())) { Objects.push_back(obj); - SubElements.push_back(elementName.second); + SubElements.push_back(elementName.oldName); keys.push_back(ref); } } @@ -8656,8 +8687,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) frozen = true; if(egf->testFlag(ExternalGeometryExtension::Sync)) sync = true; -// if (egf->testFlag(ExternalGeometryExtension::Intersection)) -// intersection = true; } } if(frozen && !sync) { @@ -8745,7 +8774,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) BRepAdaptor_Curve curve(edge); if (curve.GetType() == GeomAbs_Line) { geos.emplace_back(projectLine(curve, gPlane, invPlm)); -// ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); } } } @@ -8763,7 +8791,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) const TopoDS_Edge& edge = TopoDS::Edge(refSubShape); BRepAdaptor_Curve curve(edge); if (curve.GetType() == GeomAbs_Line) { -// ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); geos.emplace_back(projectLine(curve, gPlane, invPlm)); } else if (curve.GetType() == GeomAbs_Circle) { @@ -8787,7 +8814,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) gCircle->setCenter(Base::Vector3d(cnt.X(), cnt.Y(), cnt.Z())); GeometryFacade::setConstruction(gCircle, true); -// ExternalGeo.push_back(gCircle); geos.emplace_back(gCircle); } else { @@ -8797,7 +8823,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) hCircle, curve.FirstParameter(), curve.LastParameter()); gArc->setHandle(tCurve); GeometryFacade::setConstruction(gArc, true); -// ExternalGeo.push_back(gArc); geos.emplace_back(gArc); } } @@ -8917,7 +8942,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) projectedSegment->setPoints(p1, p2); GeometryFacade::setConstruction(projectedSegment, true); -// ExternalGeo.push_back(projectedSegment); geos.emplace_back(projectedSegment); } else {// general case, full circle @@ -8957,9 +8981,7 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Part::GeomEllipse* ellipse = new Part::GeomEllipse(); ellipse->setHandle(curve); GeometryFacade::setConstruction(ellipse, true); - -// ExternalGeo.push_back(ellipse); - geos.emplace_back(ellipse); + geos.emplace_back(ellipse); } } } @@ -9031,8 +9053,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Part::GeomCircle* circle = new Part::GeomCircle(); circle->setHandle(curve); GeometryFacade::setConstruction(circle, true); - -// ExternalGeo.push_back(circle); geos.emplace_back(circle); } else { @@ -9046,7 +9066,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Base::Vector3d(start.X(), start.Y(), start.Z()), Base::Vector3d(end.X(), end.Y(), end.Z())); GeometryFacade::setConstruction(projectedSegment, true); -// ExternalGeo.push_back(projectedSegment); geos.emplace_back(projectedSegment); } else { @@ -9060,8 +9079,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Part::GeomEllipse* ellipse = new Part::GeomEllipse(); ellipse->setHandle(curve); GeometryFacade::setConstruction(ellipse, true); - -// ExternalGeo.push_back(ellipse); geos.emplace_back(ellipse); } } @@ -9089,14 +9106,12 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Base::Vector3d p = (p1 + p2) / 2; Part::GeomPoint* point = new Part::GeomPoint(p); GeometryFacade::setConstruction(point, true); -// ExternalGeo.push_back(point); geos.emplace_back(point); } else { Part::GeomLineSegment* line = new Part::GeomLineSegment(); line->setPoints(p1, p2); GeometryFacade::setConstruction(line, true); -// ExternalGeo.push_back(line); geos.emplace_back(line); } } @@ -9112,7 +9127,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) circle->setCenter(Base::Vector3d(p.X(), p.Y(), p.Z())); GeometryFacade::setConstruction(circle, true); -// ExternalGeo.push_back(circle); geos.emplace_back(circle); } else { @@ -9124,7 +9138,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) projCurve.LastParameter()); arc->setHandle(tCurve); GeometryFacade::setConstruction(arc, true); -// ExternalGeo.push_back(arc); geos.emplace_back(arc); } } @@ -9151,14 +9164,12 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Base::Vector3d(center.X(), center.Y(), center.Z())); GeometryFacade::setConstruction(circle, true); -// ExternalGeo.push_back(circle); geos.emplace_back(circle); } else { Part::GeomBSplineCurve* bspline = new Part::GeomBSplineCurve(projCurve.BSpline()); GeometryFacade::setConstruction(bspline, true); -// ExternalGeo.push_back(bspline); geos.emplace_back(bspline); } } @@ -9180,7 +9191,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) hyperbola->setAngleXU( -xdir.AngleWithRef(xdirref.XDirection(), normal)); GeometryFacade::setConstruction(hyperbola, true); -// ExternalGeo.push_back(hyperbola); geos.emplace_back(hyperbola); } else { @@ -9193,7 +9203,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) projCurve.LastParameter()); aoh->setHandle(tCurve); GeometryFacade::setConstruction(aoh, true); -// ExternalGeo.push_back(aoh); geos.emplace_back(aoh); } } @@ -9214,7 +9223,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) parabola->setAngleXU( -xdir.AngleWithRef(xdirref.XDirection(), normal)); GeometryFacade::setConstruction(parabola, true); -// ExternalGeo.push_back(parabola); geos.emplace_back(parabola); } else { @@ -9227,7 +9235,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) projCurve.LastParameter()); aop->setHandle(tCurve); GeometryFacade::setConstruction(aop, true); -// ExternalGeo.push_back(aop); geos.emplace_back(aop); } } @@ -9246,7 +9253,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Handle(Geom_Ellipse) curve = new Geom_Ellipse(e); ellipse->setHandle(curve); GeometryFacade::setConstruction(ellipse, true); -// ExternalGeo.push_back(ellipse); geos.emplace_back(ellipse); } else { @@ -9258,7 +9264,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) projCurve.LastParameter()); aoe->setHandle(tCurve); GeometryFacade::setConstruction(aoe, true); -// ExternalGeo.push_back(aoe); geos.emplace_back(aoe); } } @@ -9283,7 +9288,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) Part::GeomPoint* point = new Part::GeomPoint(p); GeometryFacade::setConstruction(point, true); -// ExternalGeo.push_back(point); geos.emplace_back(point); } break; default: @@ -9300,8 +9304,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) FC_THROWM(Base::CADKernelError,"Failed to get intersection"); Part::TopoShape intersectionShape(maker.Shape()); auto edges = intersectionShape.getSubTopoShapes(TopAbs_EDGE); -// for (const auto &s : edges) -// importEdge(s.getShape()); // Section of some face (e.g. sphere) produce more than one arcs // from the same circle. So we try to fit the arcs with a single // circle/arc. @@ -9322,8 +9324,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) } } } -// for (const auto &s : intersectionShape.getSubShapes(TopAbs_VERTEX, TopAbs_EDGE)) -// importVertex(s); } } catch (Base::Exception &e) { @@ -9353,7 +9353,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) if (intersection) { for(auto &geo : geos) { auto egf = ExternalGeometryFacade::getFacade(geo.get()); -// egf->setFlag(ExternalGeometryExtension::Intersection); egf->setFlag(ExternalGeometryExtension::Defining, defining); } } else if (defining && i+1==(int)Objects.size()) { @@ -9450,7 +9449,6 @@ void SketchObject::rebuildExternalGeometry(bool defining, bool addIntersection) void SketchObject::fixExternalGeometry(const std::vector &geoIds) { std::set idSet(geoIds.begin(),geoIds.end()); auto geos = ExternalGeo.getValues(); -// auto geos = ExternalGeo; auto objs = ExternalGeometry.getValues(); auto subs = ExternalGeometry.getSubValues(); bool touched = false; @@ -9503,7 +9501,6 @@ void SketchObject::fixExternalGeometry(const std::vector &geoIds) { std::vector SketchObject::getCompleteGeometry() const { std::vector vals = getInternalGeometry(); -// vals.insert(vals.end(), ExternalGeo.rbegin(), ExternalGeo.rend());// in reverse order const auto &geos = getExternalGeometry(); vals.insert(vals.end(), geos.rbegin(), geos.rend()); // in reverse order return vals; @@ -9512,13 +9509,11 @@ std::vector SketchObject::getCompleteGeometry() const GeoListFacade SketchObject::getGeoListFacade() const { std::vector facade; -// facade.reserve(Geometry.getSize() + ExternalGeo.size()); facade.reserve(Geometry.getSize() + ExternalGeo.getSize()); for (auto geo : Geometry.getValues()) facade.push_back(GeometryFacade::getFacade(geo)); -// for (auto rit = ExternalGeo.rbegin(); rit != ExternalGeo.rend(); rit++) const auto &externalGeos = ExternalGeo.getValues(); for(auto rit = externalGeos.rbegin(); rit != externalGeos.rend(); rit++) facade.push_back(GeometryFacade::getFacade(*rit)); @@ -10279,14 +10274,7 @@ static inline bool checkMigration(Part::PropertyGeometryList &prop) void SketchObject::onChanged(const App::Property* prop) { -// if (isRestoring() && prop == &Geometry) { -// std::vector geom = Geometry.getValues(); -// std::vector supportedGeom = supportedGeometry(geom); -// // To keep upward compatibility ignore unsupported geometry types -// if (supportedGeom.size() != geom.size()) { -// Geometry.setValues(supportedGeom); -// return; -// } + if (prop == &Geometry) { if (isRestoring() && checkMigration(Geometry)) { // Construction migration to extension @@ -10308,6 +10296,21 @@ void SketchObject::onChanged(const App::Property* prop) } } } + geoMap.clear(); + const auto &vals = getInternalGeometry(); + for(long i=0;i<(long)vals.size();++i) { + auto geo = vals[i]; + auto gf = GeometryFacade::getFacade(geo); + if(!gf->getId()) + gf->setId(++geoLastId); + else if(gf->getId() > geoLastId) + geoLastId = gf->getId(); + while(!geoMap.insert(std::make_pair(gf->getId(),i)).second) { + FC_WARN("duplicate geometry id " << gf->getId() << " -> " << geoLastId+1); + gf->setId(++geoLastId); + } + } + updateGeoHistory(); } auto doc = getDocument(); @@ -10546,7 +10549,7 @@ void SketchObject::updateGeometryRefs() { std::unordered_map legacyMap; for(int i=0;i<(int)objs.size();++i) { auto obj = objs[i]; - const std::string &sub=shadows[i].first.size()?shadows[i].first:subs[i]; + const std::string &sub=shadows[i].newName.size()?shadows[i].newName:subs[i]; externalGeoRef.emplace_back(obj->getNameInDocument()); auto &key = externalGeoRef.back(); key += '.'; @@ -11806,10 +11809,10 @@ std::string SketchObject::getGeometryReference(int GeoId) const { if(!obj) return ref; - std::pair elementName; + App::ElementNamePair elementName; App::GeoFeature::resolveElement(obj,ref.c_str()+pos+1,elementName); - if(elementName.second.size()) - return objName + "." + elementName.second; + if(elementName.oldName.size()) + return objName + "." + elementName.oldName; return ref; } diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index cc1a781fc4e2..83f2aabeae98 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -38,6 +38,8 @@ #include "GeometryFacade.h" #include "Sketch.h" +#include "SketchGeometryExtension.h" +#include "ExternalGeometryExtension.h" namespace Sketcher { @@ -70,8 +72,6 @@ class SketcherExport SketchObject: public Part::Part2DObject Part ::PropertyGeometryList ExternalGeo; App ::PropertyBool FullyConstrained; App ::PropertyPrecision ArcFitTolerance; - // App ::PropertyInteger ExternalBSplineMaxDegree; - // App ::PropertyPrecision ExternalBSplineTolerance; Part ::PropertyPartShape InternalShape; App ::PropertyPrecision InternalTolerance; App ::PropertyBool MakeInternals; @@ -213,7 +213,6 @@ class SketcherExport SketchObject: public Part::Part2DObject typename GeometryT = Part::Geometry, typename = typename std::enable_if< std::is_base_of::type>::value>::type> - // const GeometryT* getGeometry(int GeoId) const; const GeometryT* getGeometry(int GeoId) const { return static_cast(_getGeometry(GeoId)); @@ -229,7 +228,6 @@ class SketcherExport SketchObject: public Part::Part2DObject /// returns a list of projected external geometries const std::vector& getExternalGeometry() const { - // return ExternalGeo; return ExternalGeo.getValues(); } /// rebuilds external geometry (projection onto the sketch plane) @@ -237,7 +235,6 @@ class SketcherExport SketchObject: public Part::Part2DObject /// returns the number of external Geometry entities int getExternalGeometryCount() const { - // return ExternalGeo.size(); return ExternalGeo.getSize(); } /// auto fix external geometry references @@ -751,6 +748,8 @@ class SketcherExport SketchObject: public Part::Part2DObject std::string convertSubName(const Data::IndexedName&, bool postfix = true) const; + Data::IndexedName shapeTypeFromGeoId(int GeoId, PointPos pos = Sketcher::PointPos::none) const; + App::ElementNamePair getElementName(const char* name, ElementNameType type) const override; bool isPerformingInternalTransaction() const @@ -824,13 +823,17 @@ class SketcherExport SketchObject: public Part::Part2DObject int getGeometryId(int GeoId, long& id) const; protected: + // Only the first flag is toggled, the rest of the flags is set or cleared following the first + // flag. + int toggleExternalGeometryFlag(const std::vector& geoIds, + const std::vector& flags); + + void buildShape(); /// get called by the container when a property has changed void onChanged(const App::Property* /*prop*/) override; void onDocumentRestored() override; void restoreFinished() override; - void buildShape(); - std::string validateExpression(const App::ObjectIdentifier& path, std::shared_ptr expr); @@ -924,8 +927,6 @@ class SketcherExport SketchObject: public Part::Part2DObject /// Flag to allow carbon copy from misaligned geometry bool allowUnaligned; - // std::vector ExternalGeo; - std::vector VertexId2GeoId; std::vector VertexId2PosId; @@ -1039,22 +1040,6 @@ inline int SketchObject::moveTemporaryPoint(int geoId, return solvedSketch.movePoint(geoId, pos, toPoint, relative); } -// template -// const GeometryT* SketchObject::getGeometry(int GeoId) const -//{ -// if (GeoId >= 0) { -// const std::vector& geomlist = getInternalGeometry(); -// if (GeoId < int(geomlist.size())) { -// return static_cast(geomlist[GeoId]); -// } -// } -//// else if (-GeoId <= int(ExternalGeo.size())) { -// else if (-GeoId <= int(ExternalGeo.getSize())) { -// return static_cast(ExternalGeo[-GeoId - 1]); -// } -// -// return nullptr; -//} using SketchObjectPython = App::FeaturePythonT; diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index d5d158388709..a3f2d7170110 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -2884,41 +2884,38 @@ void ViewProviderSketch::drawEditMarkers(const std::vector& Edit editCoinManager->drawEditMarkers(EditMarkers, augmentationlevel); } -void ViewProviderSketch::updateData(const App::Property* prop) -{ +void ViewProviderSketch::updateData(const App::Property* prop) { ViewProvider2DObject::updateData(prop); - // In the case of an undo/redo transaction, updateData is triggered by - // SketchObject::onUndoRedoFinished() in the solve() In the case of an internal transaction, - // touching the geometry results in a call to updateData. - if (isInEditMode() && !getSketchObject()->getDocument()->isPerformingTransaction() - && !getSketchObject()->isPerformingInternalTransaction() - && (prop == &(getSketchObject()->Geometry) || prop == &(getSketchObject()->Constraints))) { - - // At this point, we do not need to solve the Sketch - // If we are adding geometry an update can be triggered before the sketch is actually - // solved. Because a solve is mandatory to any addition (at least to update the DoF of the - // solver), only when the solver geometry is the same in number than the sketch geometry an - // update should trigger a redraw. This reduces even more the number of redraws per - // insertion of geometry - - // solver information is also updated when no matching geometry, so that if a solving fails - // this failed solving info is presented to the user - UpdateSolverInformation();// just update the solver window with the last SketchObject - // solving information - - if (getSketchObject()->getExternalGeometryCount() - + getSketchObject()->getHighestCurveIndex() + 1 - == getSolvedSketch().getGeometrySize()) { - Gui::MDIView* mdi = Gui::Application::Instance->editDocument()->getActiveView(); - if (mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) - draw(false, true); + if (prop != &getSketchObject()->Constraints) + signalElementsChanged(); +} - signalConstraintsChanged(); - } +void ViewProviderSketch::slotSolverUpdate() +{ + if (!isInEditMode() ) + return; - if (prop != &getSketchObject()->Constraints) - signalElementsChanged(); + // At this point, we do not need to solve the Sketch + // If we are adding geometry an update can be triggered before the sketch is actually + // solved. Because a solve is mandatory to any addition (at least to update the DoF of the + // solver), only when the solver geometry is the same in number than the sketch geometry an + // update should trigger a redraw. This reduces even more the number of redraws per + // insertion of geometry + + // solver information is also updated when no matching geometry, so that if a solving fails + // this failed solving info is presented to the user + UpdateSolverInformation();// just update the solver window with the last SketchObject + // solving information + + if (getSketchObject()->getExternalGeometryCount() + + getSketchObject()->getHighestCurveIndex() + 1 + == getSolvedSketch().getGeometrySize()) { + Gui::MDIView* mdi = Gui::Application::Instance->editDocument()->getActiveView(); + if (mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) + draw(false, true); + + signalConstraintsChanged(); } } @@ -3124,6 +3121,15 @@ bool ViewProviderSketch::setEdit(int ModNum) getSketchObject()->validateExternalLinks(); } + //NOLINTBEGIN + connectUndoDocument = getDocument()->signalUndoDocument.connect( + std::bind(&ViewProviderSketch::slotUndoDocument, this, sp::_1)); + connectRedoDocument = getDocument()->signalRedoDocument.connect( + std::bind(&ViewProviderSketch::slotRedoDocument, this, sp::_1)); + connectSolverUpdate = getSketchObject() + ->signalSolverUpdate.connect(boost::bind(&ViewProviderSketch::slotSolverUpdate, this)); + //NOLINTEND + // There are geometry extensions introduced by the solver and geometry extensions introduced by // the viewprovider. // 1. It is important that the solver has geometry with updated extensions. @@ -3139,13 +3145,6 @@ bool ViewProviderSketch::setEdit(int ModNum) // a draw(true) via ViewProvider::UpdateData. getSketchObject()->solve(true); - //NOLINTBEGIN - connectUndoDocument = getDocument()->signalUndoDocument.connect( - std::bind(&ViewProviderSketch::slotUndoDocument, this, sp::_1)); - connectRedoDocument = getDocument()->signalRedoDocument.connect( - std::bind(&ViewProviderSketch::slotRedoDocument, this, sp::_1)); - //NOLINTEND - // Enable solver initial solution update while dragging. getSketchObject()->setRecalculateInitialSolutionWhileMovingPoint( viewProviderParameters.recalculateInitialSolutionWhileDragging); @@ -3355,6 +3354,7 @@ void ViewProviderSketch::unsetEdit(int ModNum) connectUndoDocument.disconnect(); connectRedoDocument.disconnect(); + connectSolverUpdate.disconnect(); // when pressing ESC make sure to close the dialog Gui::Control().closeDialog(); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index 90d8372cd21d..61837ec680ca 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -745,6 +745,7 @@ class SketcherGuiExport ViewProviderSketch: public PartGui::ViewProvider2DObject //@{ void slotUndoDocument(const Gui::Document&); void slotRedoDocument(const Gui::Document&); + void slotSolverUpdate(); void forceUpdateData(); //@} @@ -924,6 +925,7 @@ class SketcherGuiExport ViewProviderSketch: public PartGui::ViewProvider2DObject private: boost::signals2::connection connectUndoDocument; boost::signals2::connection connectRedoDocument; + boost::signals2::connection connectSolverUpdate; // modes while sketching SketchMode Mode; diff --git a/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py b/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py index e83e9093e495..b2733ad31c0e 100644 --- a/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py +++ b/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py @@ -537,6 +537,113 @@ def testRemovedExternalGeometryReference(self): self.assertEqual(len(hole.Shape.Edges), 32) self.assertEqual(len(sketch2.ExternalGeometry), 1) + def testSaveLoadWithExternalGeometryReference(self): + body = self.Doc.addObject("PartDesign::Body", "Body") + sketch = self.Doc.addObject("Sketcher::SketchObject", "Sketch") + CreateRectangleSketch(sketch, (0, 0), (30, 30)) + pad = self.Doc.addObject("PartDesign::Pad", "Pad") + pad.Profile = sketch + sketch1 = self.Doc.addObject("Sketcher::SketchObject", "Sketch1") + body.addObject(sketch) + body.addObject(pad) + body.addObject(sketch1) + self.Doc.recompute() + sketch1.addExternal("Pad", "Edge12") + self.Doc.recompute() + + # Try changing it before the save + sketch = self.Doc.getObject("Sketch") + g1 = sketch.Constraints[11].First + d1 = sketch.Constraints[11].Value + sketch.delConstraint(11) + sketch.addConstraint(Sketcher.Constraint("Distance", g1, d1 - 1.0)) + self.Doc.recompute() + + filename = self.Doc.Name + ".FCStd" + self.Doc.saveAs(filename) + FreeCAD.closeDocument(self.Doc.Name) + self.Doc = FreeCAD.openDocument(filename) + pad = self.Doc.getObject("Pad") + self.Doc.recompute() + pad = self.Doc.getObject("Pad") + + sketch = self.Doc.getObject("Sketch") + g1 = sketch.Constraints[11].First + d1 = sketch.Constraints[11].Value + sketch.delConstraint(11) + sketch.addConstraint(Sketcher.Constraint("Distance", g1, d1 - 1.0)) + self.Doc.recompute() + + pad = self.Doc.getObject("Pad") + # TODO: Assert some stuff when the bug is fixed + # self.assertEqual(pad.Shape.ElementMapSize,0) + # self.assertNotNull(pad.Shape.ElementReverseMap["Edge12"]) + + def testTNPExternalGeometryStored(self): + # Arrange + import xml.etree.ElementTree as ET + + sketch = self.Doc.addObject("Sketcher::SketchObject", "Sketch") + CreateRectangleSketch(sketch, (0, 0), (30, 30)) + sketch1 = self.Doc.addObject("Sketcher::SketchObject", "Sketch1") + pad = self.Doc.addObject("PartDesign::Pad", "Pad") + pad.Profile = sketch + self.Doc.recompute() + # sketch1.addExternal("Sketch", "Edge3") + sketch1.addExternal("Pad", "Edge12") + self.Doc.recompute() + # Act + root = ET.fromstring("" + sketch1.Content + "") + # Can't use != in an xpath because it wasn't added until python 3.10. + # "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref!='']" + extRefs = root.findall( + "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref='']" + ) + extRefsAll = root.findall( + "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref]" + ) + # Assert + self.assertEqual(len(extRefs), 2) + self.assertEqual(len(extRefsAll), 3) + self.assertEqual(root.tag, "all") + # Act + filename = self.Doc.Name + ".FCStd" + self.Doc.saveAs(filename) + FreeCAD.closeDocument(self.Doc.Name) + self.Doc = FreeCAD.openDocument(filename) + # Assert + root = ET.fromstring("" + self.Doc.getObject("Sketch1").Content + "") + extRefs = root.findall( + "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref='']" + ) + extRefsAll = root.findall( + "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref]" + ) + self.assertEqual(len(extRefs), 2) + self.assertEqual(len(extRefsAll), 3) + self.assertEqual(root.tag, "all") + # Act + sketch = self.Doc.getObject("Sketch") + g1 = sketch.Constraints[11].First + d1 = sketch.Constraints[11].Value + sketch.delConstraint(11) + sketch.addConstraint(Sketcher.Constraint("Distance", g1, d1 - 1.0)) + self.Doc.recompute() + # Assert + root = ET.fromstring("" + self.Doc.getObject("Sketch1").Content + "") + extRefs = root.findall( + "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref='']" + ) + extRefsAll = root.findall( + "*/*[@name='ExternalGeo']//*/[@type='Sketcher::ExternalGeometryExtension']/[@Ref]" + ) + self.assertEqual(len(extRefs), 2) + self.assertEqual(len(extRefsAll), 3) + self.assertEqual(root.tag, "all") + + # TODO other tests: + # getHigherElement + def assertSuccessfulSolve(self, sketch, msg=None): status = sketch.solve() # TODO: can we get the solver's messages somehow to improve the message? diff --git a/tests/src/Mod/Sketcher/App/SketchObject.cpp b/tests/src/Mod/Sketcher/App/SketchObject.cpp index 761ebf97ff55..989a03f7c6c3 100644 --- a/tests/src/Mod/Sketcher/App/SketchObject.cpp +++ b/tests/src/Mod/Sketcher/App/SketchObject.cpp @@ -266,29 +266,29 @@ TEST_F(SketchObjectTest, testGetElementName) // unless it's Export, we are really just testing the superclass App::GeoFeature::getElementName // call. auto forward_normal_name = - getObject()->getElementName("g39;SKT", App::GeoFeature::ElementNameType::Normal); + getObject()->getElementName("g1;SKT", App::GeoFeature::ElementNameType::Normal); auto reverse_normal_name = getObject()->getElementName("Vertex2", App::GeoFeature::ElementNameType::Normal); auto reverse_export_name = getObject()->getElementName("Vertex1", App::GeoFeature::ElementNameType::Export); auto map = getObject()->Shape.getShape().getElementMap(); ASSERT_EQ(map.size(), 3); - EXPECT_STREQ(map[0].name.toString().c_str(), "g39;SKT"); + EXPECT_STREQ(map[0].name.toString().c_str(), "g1;SKT"); EXPECT_EQ(map[0].index.toString(), "Edge1"); // Assert #ifndef FC_USE_TNP_FIX EXPECT_STREQ(forward_normal_name.newName.c_str(), ""); - EXPECT_STREQ(forward_normal_name.oldName.c_str(), "g39;SKT"); + EXPECT_STREQ(forward_normal_name.oldName.c_str(), "g1;SKT"); EXPECT_STREQ(reverse_normal_name.newName.c_str(), ""); EXPECT_STREQ(reverse_normal_name.oldName.c_str(), "Vertex2"); - EXPECT_STREQ(reverse_export_name.newName.c_str(), ";g39v1;SKT.Vertex1"); + EXPECT_STREQ(reverse_export_name.newName.c_str(), ";g1v1;SKT.Vertex1"); EXPECT_STREQ(reverse_export_name.oldName.c_str(), "Vertex1"); #else - EXPECT_STREQ(forward_normal_name.newName.c_str(), ";g39;SKT.Edge1"); + EXPECT_STREQ(forward_normal_name.newName.c_str(), ";g1;SKT.Edge1"); EXPECT_STREQ(forward_normal_name.oldName.c_str(), "Edge1"); - EXPECT_STREQ(reverse_normal_name.newName.c_str(), ";g39v2;SKT.Vertex2"); + EXPECT_STREQ(reverse_normal_name.newName.c_str(), ";g1v2;SKT.Vertex2"); EXPECT_STREQ(reverse_normal_name.oldName.c_str(), "Vertex2"); - EXPECT_STREQ(reverse_export_name.newName.c_str(), ";g39v1;SKT.Vertex1"); + EXPECT_STREQ(reverse_export_name.newName.c_str(), ";g1v1;SKT.Vertex1"); EXPECT_STREQ(reverse_export_name.oldName.c_str(), "Vertex1"); #endif } From 71870bd6f3fd8fbeab6a0f71113c889219456fd2 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 26 Jul 2024 09:35:15 -0400 Subject: [PATCH 3/5] Toponaming: Touchup cleanup Includes some header re-ordering from chennes. --- src/Mod/Sketcher/App/PreCompiled.h | 16 + src/Mod/Sketcher/App/SketchObject.cpp | 448 ++------------------ src/Mod/Sketcher/App/SketchObject.h | 33 -- tests/src/Mod/Sketcher/App/SketchObject.cpp | 22 +- 4 files changed, 74 insertions(+), 445 deletions(-) diff --git a/src/Mod/Sketcher/App/PreCompiled.h b/src/Mod/Sketcher/App/PreCompiled.h index 36c1a184c080..bdb7f8929b7c 100644 --- a/src/Mod/Sketcher/App/PreCompiled.h +++ b/src/Mod/Sketcher/App/PreCompiled.h @@ -43,23 +43,38 @@ #include // Boost +#include +#include #include #include +#include +#include +#include +#include #include +#include // OpenCasCade #include #include +#include #include #include +#include #include +#include #include +#include #include #include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -74,6 +89,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 399150743494..e898f21569a0 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -31,20 +31,22 @@ #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 @@ -53,35 +55,31 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include #include -#include #include #include #include #include #include #include -#endif - - -#include - -#include -#include #include #include +#include #include #include -#include +#include +#include + +#endif #include #include @@ -110,10 +108,7 @@ #include "SketchObjectPy.h" #include "SolverGeometryExtension.h" -#include -#include -#include -#include +#include "ExternalGeometryFacade.h" #undef DEBUG @@ -685,19 +680,18 @@ class SketchObject::GeoHistory { oldset.clear(); for(long _id : idset) { long id = abs(_id); - auto &v = adjmap[id]; - auto &adj = _id>0?v.first:v.second; - for(auto it=adj.begin(),itNext=it;it!=adj.end();it=itNext) { - ++itNext; + auto& v = adjmap[id]; + auto& adj = _id > 0 ? v.first : v.second; + for (auto it = adj.begin(); it != adj.end(); /* don't advance here */) { long other = *it; - if(geomap.find(other) == geomap.end()) { - // remember those deleted id's + auto removeId = it++; // grab ID we might erase, and advance + if (geomap.find(other) == geomap.end()) { + // remember those deleted IDs to swap in below oldset.insert(other); - FC_TRACE("insert old " << id << ", " << other); - } else if(idset.find(other)==idset.end()) { - // delete any existing id's that are no longer in the adj list - FC_TRACE("erase " << id << ", " << other); - adj.erase(it); + } + else if (idset.find(other) == idset.end()) { + // delete any existing IDs that are no longer in the adj list + adj.erase(removeId); } } // now merge the current ones @@ -705,7 +699,6 @@ class SketchObject::GeoHistory { long id2 = abs(_id2); if(id!=id2) { adj.insert(id2); - FC_TRACE("insert new " << id << ", " << id2); } } } @@ -1954,22 +1947,24 @@ int SketchObject::toggleExternalGeometryFlag(const std::vector &geoIds, if(!idSet.count(geoId)) continue; idSet.erase(geoId); - int idx = -geoId-1; - auto &geo = geos[idx]; - auto egf = ExternalGeometryFacade::getFacade(geo); - bool value = !egf->testFlag(flag); - if(egf->getRef().size()) { - for(auto gid : getRelatedGeometry(geoId)) { - if(gid == geoId) + const int idx = -geoId - 1; + auto& geo = geos[idx]; + const auto egf = ExternalGeometryFacade::getFacade(geo); + const bool value = !egf->testFlag(flag); + if (!egf->getRef().empty()) { + for (auto relatedGeoId : getRelatedGeometry(geoId)) { + if (relatedGeoId == geoId) { continue; - int idx = -gid-1; - auto &g = geos[idx]; - g = g->clone(); - auto egf = ExternalGeometryFacade::getFacade(g); - egf->setFlag(flag, value); - for (size_t i=1; isetFlag(flags[i], value); - idSet.erase(gid); + } + int relatedIndex = -relatedGeoId - 1; + auto& relatedGeometry = geos[relatedIndex]; + relatedGeometry = relatedGeometry->clone(); + auto relatedFacade = ExternalGeometryFacade::getFacade(relatedGeometry); + relatedFacade->setFlag(flag, value); + for (size_t i = 1; i < flags.size(); ++i) { + relatedFacade->setFlag(flags[i], value); + } + idSet.erase(relatedGeoId); } } geo = geo->clone(); @@ -3002,7 +2997,7 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, oc2pf.x, oc2pf.y, oc2pf.z); - + // To enable detailed Log of ten intermediate points along the curves uncomment this /*auto printoffsetcurve = [](Part::GeomOffsetCurve *c) { for(double param = c->getFirstParameter(); param < c->getLastParameter(); param = param @@ -8037,9 +8032,8 @@ int SketchObject::attachExternal( std::set detached; std::set idSet; - for(int geoId : geoIds) { - if(geoId > GeoEnum::RefExt || -geoId-1 >= ExternalGeo.getSize()) -// if(geoId > GeoEnum::RefExt || -geoId-1 >= ExternalGeo.size()) + for (int geoId : geoIds) { + if (geoId > GeoEnum::RefExt || -geoId - 1 >= ExternalGeo.getSize()) continue; auto geo = getGeometry(geoId); if(!geo) @@ -8287,183 +8281,6 @@ Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Pl } } -// Project an edge to a line. Only works if the edge is planar and its plane is -// perpendicular to the projection plane. This function is to work around OCC -// normal projection bug which seems to only respect the start and ending points -// of an arc but disregarding any extreme points. OCC also has trouble handling -// BSpline projection to a straight line. Although it does correctly projects -// the line including extreme bounds, it will produce a BSpline with degree -// more than one. -// -// The work around here is to use an aligned bounding box of the edge to get -// the projection of the extreme points to construct the projected line. -Part::Geometry* projectEdgeToLine(const TopoDS_Edge &edge, - const Base::Placement& invPlm) -{ - auto shape = Part::TopoShape(edge); - // First, transform the shape to the projection plane local coordinates. - // TODO: Both of these methods are heavily deprecated. Is this really required? - // shape.setShapePlacement(invPlm * shape.getShapePlacement()); - - gp_Pln plane; - // Check if the edge is planar - if (!shape.findPlane(plane)) - return nullptr; - - // Check if the edge plane is perpendicular to the projection plane - if (!plane.Axis().IsNormal(gp_Ax1(), Precision::Angular())) - return nullptr; - - // Align the z axis of the edge plane to the y axis of the projection - // plane, so that the extreme bound will be a line in the x axis direction - // of the projection plane. - double angle = plane.Axis().Direction().Angle(gp_Dir(0, 1, 0)); - gp_Trsf trsf; - if (fabs(angle) > Precision::Angular()) { - trsf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(0, 0, 1)), angle); - shape.move(trsf); - } - - // Make a copy to work around OCC circular edge transformation bug - shape = shape.makeElementCopy(); - - // Explicitly make the mesh, or else getBoundBox() will be very loosely - // bound. - // - // Use very small deflection to make more accurate measurement. Could be slow! - BRepMesh_IncrementalMesh aMesh(shape.getShape(), 0.005, false, 0.1, true); - - // Obtain the bounding box and move the extreme points back to its original - // location - auto bbox = shape.getBoundBox(); - if (!bbox.IsValid()) - return nullptr; - - gp_Pnt p1(bbox.MinX, bbox.MinY, 0); - gp_Pnt p2(bbox.MaxX, bbox.MaxY, 0); - if (fabs(angle) > Precision::Angular()) { - trsf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(0, 0, 1)), -angle); - p1.Transform(trsf); - p2.Transform(trsf); - } - - Base::Vector3d P1(p1.X(), p1.Y(), 0); - Base::Vector3d P2(p2.X(), p2.Y(), 0); - - // check for degenerated case when the line is collapsed to a point - if (p1.SquareDistance(p2) < Precision::SquareConfusion()) { - Part::GeomPoint* point = new Part::GeomPoint((P1 + P2) / 2); - GeometryFacade::setConstruction(point, true); - return point; - } - else { - Part::GeomLineSegment* line = new Part::GeomLineSegment(); - line->setPoints(P1, P2); - GeometryFacade::setConstruction(line, true); - return line; - } -} - -void getParameterRange(Handle(Geom_Curve) curve, - const gp_Pnt &firstPoint, - const gp_Pnt &lastPoint, - double &firstParameter, - double &lastParameter) -{ - // The reason of this function is because the first/last parameter reported - // from some curve does not really corresponds to the first/last vertex of - // the edge. I can only guess this is because the curve (in some cases) is - // actually computed on demand from surface (in BRepAdaptor_Curve maybe). - // And in the process, there is something off in tolerance causing the - // derived parameter not matching the value corresponding to the position of - // the actual vertex. - GeomAPI_ProjectPointOnCurve pfirst(firstPoint, curve); - GeomAPI_ProjectPointOnCurve plast(lastPoint, curve); - firstParameter = pfirst.LowerDistanceParameter(); - lastParameter = plast.LowerDistanceParameter(); -} - -void adjustParameterRange(const TopoDS_Edge &edge, - Handle(Geom_Plane) gPlane, - const gp_Trsf &mov, - Handle(Geom_Curve) curve, - double &firstParameter, - double &lastParameter) -{ - // This function is to deal with the ambiguity of trimming a periodic - // curve, e.g. given two points on a circle, whether to get the upper or - // lower arc. Because projection orientation may swap the first and last - // parameter of the original curve. - // - // We project the middle point of the original curve to the projected curve - // to decide whether to flip the parameters. - - Handle(Geom_Curve) origCurve = BRepAdaptor_Curve(edge).Curve().Curve(); - - // GeomAPI_ProjectPointOnCurve will project a point to an untransformed - // curve, so make sure to obtain the point on an untransformed edge. - auto e = edge.Located(TopLoc_Location()); - - gp_Pnt firstPoint = BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(e))); - double f = GeomAPI_ProjectPointOnCurve(firstPoint, origCurve).LowerDistanceParameter(); - - gp_Pnt lastPoint = BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(e))); - double l = GeomAPI_ProjectPointOnCurve(lastPoint, origCurve).LowerDistanceParameter(); - - auto adjustPeriodic = [](Handle(Geom_Curve) curve, double &f, double &l) { - // Copied from Geom_TrimmedCurve::setTrim() - if (curve->IsPeriodic()) { - Standard_Real Udeb = curve->FirstParameter(); - Standard_Real Ufin = curve->LastParameter(); - // set f in the range Udeb , Ufin - // set l in the range f , f + Period() - ElCLib::AdjustPeriodic(Udeb, Ufin, - std::min(std::abs(f-l)/2,Precision::PConfusion()), - f, l); - } - }; - - // Adjust for periodic curve to deal with orientation - adjustPeriodic(origCurve, f, l); - - // Obtain the middle parameter in order to get the mid point of the arc - double m = (l - f) * 0.5 + f; - GeomLProp_CLProps prop(origCurve,m,0,Precision::Confusion()); - gp_Pnt midPoint = prop.Value(); - - // Transform all three points to the world coordinate - auto trsf = edge.Location().Transformation(); - midPoint.Transform(trsf); - firstPoint.Transform(trsf); - lastPoint.Transform(trsf); - - // Project the points to the sketch plane. Note the coordinates are still - // in world coordinate system. - gp_Pnt pm = GeomAPI_ProjectPointOnSurf(midPoint, gPlane).NearestPoint(); - gp_Pnt pf = GeomAPI_ProjectPointOnSurf(firstPoint, gPlane).NearestPoint(); - gp_Pnt pl = GeomAPI_ProjectPointOnSurf(lastPoint, gPlane).NearestPoint(); - - // Transform the projected points to sketch plane local coordinates - pm.Transform(mov); - pf.Transform(mov); - pl.Transform(mov); - - // Obtain the corresponding parameters for those points in the projected curve - double f2 = GeomAPI_ProjectPointOnCurve(pf, curve).LowerDistanceParameter(); - double l2 = GeomAPI_ProjectPointOnCurve(pl, curve).LowerDistanceParameter(); - double m2 = GeomAPI_ProjectPointOnCurve(pm, curve).LowerDistanceParameter(); - - firstParameter = f2; - lastParameter = l2; - - adjustPeriodic(curve, f2, l2); - adjustPeriodic(curve, f2, m2); - // If the middle point is out of range, it means we need to choose the - // other half of the arc. - if (m2 > l2) - std::swap(firstParameter, lastParameter); -} - } // anonymous namespace bool SketchObject::evaluateSupport() @@ -10745,28 +10562,6 @@ void SketchObject::onDocumentRestored() auto grpObj = App::GeoFeatureGroupExtension::getGroupOfObject(this); if (grpObj) grp = grpObj->getExtensionByType(true); - - auto exports = Exports.getValues(); - bool touched = false; - for (auto &obj : exports) { - auto exp = Base::freecad_dynamic_cast(obj); - if (!exp || exp->BaseRefs.getValue() == this) - continue; - auto newexp = Base::freecad_dynamic_cast( - getDocument()->addObject("Sketcher::SketchExport", "Export")); - if (grp) - grp->addObject(newexp); - if (exp->Label.getStrValue() != exp->getNameInDocument()) - newexp->Label.setValue(exp->Label.getValue()); - else - newexp->Label.setValue(newexp->getNameInDocument()); - newexp->BaseRefs.setValue(this, exp->BaseRefs.getSubValues(false)); - newexp->Visibility.setValue(exp->Visibility.getValue()); - obj = newexp; - touched = true; - } - if (touched) - Exports.setValues(exports); } } @@ -12045,163 +11840,4 @@ PyObject* Sketcher::SketchObjectPython::getPyObject() template class SketcherExport FeaturePythonT; }// namespace App -// --------------------------------------------------------- - -PROPERTY_SOURCE(Sketcher::SketchExport, Part::Part2DObject) - -SketchExport::SketchExport() { - ADD_PROPERTY_TYPE(Base,(0),"", - (App::PropertyType)(App::Prop_Hidden|App::Prop_ReadOnly), - "(Deprecated) Base sketch object name"); - - ADD_PROPERTY_TYPE(Refs,(),"",App::Prop_Hidden, - "(Deprecated) Sketch geometry references"); - - ADD_PROPERTY_TYPE(BaseRefs,(0),"",App::Prop_None,"Base sketch references"); - - ADD_PROPERTY_TYPE(SyncPlacement,(true),"", - App::Prop_None,"Synchronize placement with parent sketch if not attached"); - - ADD_PROPERTY_TYPE(ScaleVector, (1, 1, 1) ,"", App::Prop_None, - "Scale vector intended for mirror. Note that using the scale vector for\n" - "non-uniform scaling transformed the geometries into BSpline curves"); -} - -SketchExport::~SketchExport() -{} - -App::DocumentObject *SketchExport::getBase() const { - return BaseRefs.getValue(); -} - -void SketchExport::onDocumentRestored() -{ - if (!BaseRefs.getValue() && Base.getValue()) - BaseRefs.setValue(Base.getValue(), Refs.getValues()); - if (Shape.getShape().getPlacement() != Placement.getValue()) - Placement.touch(); - Part::Part2DObject::onDocumentRestored(); -} - -App::DocumentObjectExecReturn *SketchExport::execute(void) { - try { - App::DocumentObjectExecReturn* rtn = Part2DObject::execute();//to positionBySupport - if(rtn!=App::DocumentObject::StdReturn) - //error - return rtn; - } - catch (const Base::Exception& e) { - return new App::DocumentObjectExecReturn(e.what()); - } - - auto base = Base::freecad_dynamic_cast(getBase()); - if(!base) - return new App::DocumentObjectExecReturn("Missing parent sketch"); - if(update() && SyncPlacement.getValue() && !positionBySupport()) - Placement.setValue(base->Placement.getValue()); - return App::DocumentObject::StdReturn; -} - -void SketchExport::onChanged(const App::Property* prop) { - auto doc = getDocument(); - if(prop == &BaseRefs) { - if(!isRestoring() && doc && !doc->isPerformingTransaction()) - update(); - Base.setValue(BaseRefs.getValue()); - Refs.setValue(BaseRefs.getSubValues(true)); - } else if(prop == &Shape) { - // We used to bypass Part::Feature handling of shape change, which sets - // the placement the same as the shape's. But this is causes bad thing - // to happen. We'll fix that in onDocumentRestored(). - if (getDocument() && getDocument()->testStatus(App::Document::Restoring)) { - DocumentObject::onChanged(prop); - return; - } - } - Part2DObject::onChanged(prop); -} - -std::set SketchExport::getRefs() const { - std::set refSet; - const auto &refs = BaseRefs.getSubValues(); - refSet.insert(refs.begin(),refs.end()); - if(refSet.size()>1) - refSet.erase(""); - return refSet; -} - -bool SketchExport::update() { - auto base = getBase(); - if(!base) - return false; - std::vector points; - std::vector shapes; - for(const auto &ref : getRefs()) { - // Obtain the shape without feature's placement transformation, because - // we may have our own support. - auto shape = Part::Feature::getTopoShape(base,ref.c_str(),true,0,0,false,false); - if(shape.isNull()) { - FC_ERR("Invalid element reference: " << ref); - throw Base::RuntimeError("Invalid element reference"); - } - if(!shape.hasSubShape(TopAbs_EDGE)) - points.push_back(shape.makeElementCopy(Part::OpCodes::SketchExport)); - else - shapes.push_back(shape.makeElementCopy()); - } - Part::TopoShape res; - if(shapes.size()) { - res.makeElementWires(shapes,Part::OpCodes::SketchExport); - shapes.clear(); - if(points.size()) { - // Check if the vertex is already included in the wires - for(auto &point : points) { - auto name = point.getMappedName( - Data::IndexedName::fromConst("Vertex", 1)); - if(name && res.getIndexedName(name)) - continue; - shapes.push_back(point); - } - if(shapes.size()) { - shapes.push_back(res); - res.makeElementCompound(shapes); - } - } - }else if(points.empty()) - return false; - else - res.makeElementCompound(points,0,Part::TopoShape::SingleShapeCompoundCreationPolicy::returnShape); - - const auto &scale = ScaleVector.getValue(); - if ((fabs(scale.x - 1.0) > Precision::Confusion() - || fabs(scale.y - 1.0) > Precision::Confusion() - || fabs(scale.z - 1.0) > Precision::Confusion()) - && fabs(scale.x) > Precision::Confusion() - && fabs(scale.y) > Precision::Confusion() - && fabs(scale.z) > Precision::Confusion()) - { - Base::Matrix4D mat; - mat.scale(scale); - res.transformShape(mat, false, true); - } - - Shape.setValue(res); - return true; -} - -void SketchExport::handleChangedPropertyType(Base::XMLReader &reader, - const char *TypeName, App::Property *prop) -{ - if (prop == &Base && strcmp(TypeName, "App::PropertyString") == 0) { - App::PropertyString p; - p.Restore(reader); - auto obj = getDocument()->getObject(p.getValue()); - if(!obj) { - FC_ERR("Cannot find parent sketch '" << p.getValue() << "' of " << getFullName()); - return; - } - Base.setValue(obj); - } -} - // clang-format on diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 83f2aabeae98..dd32a9a9364e 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -1044,39 +1044,6 @@ inline int SketchObject::moveTemporaryPoint(int geoId, using SketchObjectPython = App::FeaturePythonT; // --------------------------------------------------------- - -class SketcherExport SketchExport: public Part::Part2DObject -{ - PROPERTY_HEADER(Sketcher::SketchObject); - -public: - SketchExport(); - ~SketchExport() override; - - App::PropertyStringList Refs; - App::PropertyLink Base; - App::PropertyLinkSub BaseRefs; - App::PropertyBool SyncPlacement; - App::PropertyVector ScaleVector; - - App::DocumentObjectExecReturn* execute(void) override; - void onChanged(const App::Property* /*prop*/) override; - const char* getViewProviderName(void) const override - { - return "SketcherGui::ViewProviderSketchExport"; - } - - bool update(); - - App::DocumentObject* getBase() const; - std::set getRefs() const; - - void handleChangedPropertyType(Base::XMLReader& reader, - const char* TypeName, - App::Property* prop) override; - void onDocumentRestored() override; -}; - } // namespace Sketcher diff --git a/tests/src/Mod/Sketcher/App/SketchObject.cpp b/tests/src/Mod/Sketcher/App/SketchObject.cpp index 989a03f7c6c3..4311649badce 100644 --- a/tests/src/Mod/Sketcher/App/SketchObject.cpp +++ b/tests/src/Mod/Sketcher/App/SketchObject.cpp @@ -260,21 +260,31 @@ TEST_F(SketchObjectTest, testGetElementName) Base::Vector3d p1(0.0, 0.0, 0.0), p2(1.0, 0.0, 0.0); std::unique_ptr geoline(new Part::GeomLineSegment()); static_cast(geoline.get())->setPoints(p1, p2); - getObject()->addGeometry(geoline.get()); + auto id = getObject()->addGeometry(geoline.get()); + long tag; + getObject()->getGeometryId(id, tag); // We need to look up the tag that got assigned + std::ostringstream oss; + oss << "g" << tag; + auto tagName = oss.str(); getObject()->recomputeFeature(); // or ->execute() // Act // unless it's Export, we are really just testing the superclass App::GeoFeature::getElementName // call. auto forward_normal_name = - getObject()->getElementName("g1;SKT", App::GeoFeature::ElementNameType::Normal); + getObject()->getElementName((tagName + ";SKT").c_str(), + App::GeoFeature::ElementNameType::Normal); auto reverse_normal_name = getObject()->getElementName("Vertex2", App::GeoFeature::ElementNameType::Normal); auto reverse_export_name = getObject()->getElementName("Vertex1", App::GeoFeature::ElementNameType::Export); auto map = getObject()->Shape.getShape().getElementMap(); ASSERT_EQ(map.size(), 3); - EXPECT_STREQ(map[0].name.toString().c_str(), "g1;SKT"); + EXPECT_STREQ(map[0].name.toString().c_str(), (tagName + ";SKT").c_str()); EXPECT_EQ(map[0].index.toString(), "Edge1"); + EXPECT_STREQ(map[1].name.toString().c_str(), (tagName + "v1;SKT").c_str()); + EXPECT_EQ(map[1].index.toString(), "Vertex1"); + EXPECT_STREQ(map[2].name.toString().c_str(), (tagName + "v2;SKT").c_str()); + EXPECT_EQ(map[2].index.toString(), "Vertex2"); // Assert #ifndef FC_USE_TNP_FIX EXPECT_STREQ(forward_normal_name.newName.c_str(), ""); @@ -284,11 +294,11 @@ TEST_F(SketchObjectTest, testGetElementName) EXPECT_STREQ(reverse_export_name.newName.c_str(), ";g1v1;SKT.Vertex1"); EXPECT_STREQ(reverse_export_name.oldName.c_str(), "Vertex1"); #else - EXPECT_STREQ(forward_normal_name.newName.c_str(), ";g1;SKT.Edge1"); + EXPECT_STREQ(forward_normal_name.newName.c_str(), (";" + tagName + ";SKT.Edge1").c_str()); EXPECT_STREQ(forward_normal_name.oldName.c_str(), "Edge1"); - EXPECT_STREQ(reverse_normal_name.newName.c_str(), ";g1v2;SKT.Vertex2"); + EXPECT_STREQ(reverse_normal_name.newName.c_str(), (";" + tagName + "v2;SKT.Vertex2").c_str()); EXPECT_STREQ(reverse_normal_name.oldName.c_str(), "Vertex2"); - EXPECT_STREQ(reverse_export_name.newName.c_str(), ";g1v1;SKT.Vertex1"); + EXPECT_STREQ(reverse_export_name.newName.c_str(), (";" + tagName + "v1;SKT.Vertex1").c_str()); EXPECT_STREQ(reverse_export_name.oldName.c_str(), "Vertex1"); #endif } From 037c05a92780d27d6a11fd938bb641548b22c3dc Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Fri, 26 Jul 2024 21:48:33 -0500 Subject: [PATCH 4/5] Sketcher/Toponaming: Minor code cleanup during review --- src/Mod/Sketcher/App/SketchObject.cpp | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index e898f21569a0..a9582df27619 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -104,6 +104,8 @@ #include #include +#include + #include "SketchObject.h" #include "SketchObjectPy.h" #include "SolverGeometryExtension.h" @@ -612,22 +614,25 @@ int SketchObject::solve(bool updateGeoAfterSolving /*=true*/) namespace bg = boost::geometry; namespace bgi = boost::geometry::index; +// NOLINTNEXTLINE BOOST_GEOMETRY_REGISTER_POINT_3D( Base::Vector3d,double,bg::cs::cartesian,x,y,z) class SketchObject::GeoHistory { +private: + static constexpr int bgiMaxElements = 16; public: - typedef bgi::linear<16> Parameters; + using Parameters = bgi::linear; - typedef std::set IdSet; - typedef std::pair IdSets; - typedef std::list AdjList; + using IdSet = std::set; + using IdSets = std::pair; + using AdjList = std::list; //associate a geo with connected ones on both points - typedef std::map AdjMap; + using AdjMap = std::map; // maps start/end points to all existing geo to query and update adjacencies - typedef std::pair Value; + using Value = std::pair; AdjList adjlist; AdjMap adjmap; @@ -636,7 +641,7 @@ class SketchObject::GeoHistory { AdjList::iterator find(const Base::Vector3d &pt,bool strict=true){ std::vector ret; rtree.query(bgi::nearest(pt,1),std::back_inserter(ret)); - if(ret.size()) { + if(!ret.empty()) { // NOTE: we are using square distance here, the 1e-6 threshold is // very forgiving. We should have used Precision::SquareConfisuion(), // which is 1e-14. However, there is a problem with current @@ -647,13 +652,9 @@ class SketchObject::GeoHistory { double tol = strict?Precision::SquareConfusion()*10:1e-6; double d = Base::DistanceP2(ret[0].first,pt); if(d(); FC_TIME_INIT(t); const auto &geos = getInternalGeometry(); geoHistory->clear(); - for(int i=0;i<(int)geos.size();++i) { - auto geo = geos[i]; + for(auto geo : geos) { auto pstart = getPoint(geo,PointPos::start); auto pend = getPoint(geo,PointPos::end); int id = GeometryFacade::getId(geo); @@ -7098,7 +7098,7 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m if (GeoId < 0 || GeoId > getHighestCurveIndex()) THROWMT(Base::ValueError, - QT_TRANSLATE_NOOP("Exceptions", "BSpline Geometry Index (GeoID) is out of bounds.")) + QT_TRANSLATE_NOOP("Exceptions", "B-spline Geometry Index (GeoID) is out of bounds.")) if (multiplicityincr == 0)// no change in multiplicity THROWMT( @@ -7110,7 +7110,7 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m if (geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) THROWMT(Base::TypeError, QT_TRANSLATE_NOOP("Exceptions", - "The Geometry Index (GeoId) provided is not a B-spline curve.")) + "The Geometry Index (GeoId) provided is not a B-spline.")) const Part::GeomBSplineCurve* bsp = static_cast(geo); @@ -7283,7 +7283,7 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity) if (GeoId < 0 || GeoId > getHighestCurveIndex()) THROWMT( Base::ValueError, - QT_TRANSLATE_NOOP("Exceptions", "BSpline Geometry Index (GeoID) is out of bounds.")); + QT_TRANSLATE_NOOP("Exceptions", "B-spline Geometry Index (GeoID) is out of bounds.")); if (multiplicity == 0) THROWMT(Base::ValueError, @@ -7294,7 +7294,7 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity) if (geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) THROWMT(Base::TypeError, QT_TRANSLATE_NOOP("Exceptions", - "The Geometry Index (GeoId) provided is not a B-spline curve.")); + "The Geometry Index (GeoId) provided is not a B-spline.")); const Part::GeomBSplineCurve* bsp = static_cast(geo); @@ -7306,12 +7306,12 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity) THROWMT(Base::ValueError, QT_TRANSLATE_NOOP( "Exceptions", - "Knot multiplicity cannot be higher than the degree of the BSpline.")); + "Knot multiplicity cannot be higher than the degree of the B-spline.")); if (param > lastParam || param < firstParam) THROWMT(Base::ValueError, QT_TRANSLATE_NOOP("Exceptions", - "Knot cannot be inserted outside the BSpline parameter range.")); + "Knot cannot be inserted outside the B-spline parameter range.")); std::unique_ptr bspline; @@ -8267,7 +8267,7 @@ Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Pl invPlm.multVec(p1, p1); invPlm.multVec(p2, p2); - if (Base::Distance(p1, p2) < Precision::Confusion()) { + if (Base::DistanceP2(p1, p2) < Precision::SquareConfusion()) { Base::Vector3d p = (p1 + p2) / 2; Part::GeomPoint* point = new Part::GeomPoint(p); GeometryFacade::setConstruction(point, true); From 8948505b4537bc3aec9eab41e24812e66498c9ec Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Fri, 26 Jul 2024 22:10:44 -0500 Subject: [PATCH 5/5] Sketcher/Toponaming: Further linter cleanup --- src/Mod/Sketcher/App/SketchObject.cpp | 141 +++++++++++++++----------- 1 file changed, 84 insertions(+), 57 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index a9582df27619..8aeaa2a2447f 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -362,7 +362,7 @@ void SketchObject::buildShape() { #endif } - for(size_t i=2;itestFlag(ExternalGeometryExtension::Defining)) @@ -4798,8 +4798,6 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, newgeoVals.reserve(geovals.size() + geoIdList.size()); - int cgeoid = getHighestCurveIndex() + 1; - std::map geoIdMap; std::map isStartEndInverted; @@ -10091,25 +10089,26 @@ static inline bool checkMigration(Part::PropertyGeometryList &prop) void SketchObject::onChanged(const App::Property* prop) { - if (prop == &Geometry) { if (isRestoring() && checkMigration(Geometry)) { // Construction migration to extension - for( auto g : Geometry.getValues()) { - if(g->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) { + for( auto geometryValue : Geometry.getValues()) { + if(geometryValue->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) { auto ext = std::static_pointer_cast( - g->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock()); + geometryValue->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock()); - auto gf = GeometryFacade::getFacade(g); // at this point IA geometry is already migrated + auto gf = GeometryFacade::getFacade(geometryValue); // at this point IA geometry is already migrated if(ext->testMigrationType(Part::GeometryMigrationExtension::Construction)) { bool oldconstr = ext->getConstruction(); - if( g->getTypeId() == Part::GeomPoint::getClassTypeId() && !gf->isInternalAligned()) + if( geometryValue->getTypeId() == Part::GeomPoint::getClassTypeId() && !gf->isInternalAligned()){ oldconstr = true; + } gf->setConstruction(oldconstr); } - if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) + if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) { gf->setId(ext->getId()); + } } } } @@ -10118,12 +10117,13 @@ void SketchObject::onChanged(const App::Property* prop) for(long i=0;i<(long)vals.size();++i) { auto geo = vals[i]; auto gf = GeometryFacade::getFacade(geo); - if(!gf->getId()) + if(gf->getId() == 0) { gf->setId(++geoLastId); - else if(gf->getId() > geoLastId) + } else if(gf->getId() > geoLastId) { geoLastId = gf->getId(); + } while(!geoMap.insert(std::make_pair(gf->getId(),i)).second) { - FC_WARN("duplicate geometry id " << gf->getId() << " -> " << geoLastId+1); + FC_WARN("duplicate geometry id " << gf->getId() << " -> " << geoLastId+1); // NOLINT gf->setId(++geoLastId); } } @@ -10199,23 +10199,25 @@ void SketchObject::onChanged(const App::Property* prop) } } } else if ( prop == &ExternalGeo && !prop->testStatus(App::Property::User3) ) { - if(doc && doc->isPerformingTransaction()) + if(doc && doc->isPerformingTransaction()) { setStatus(App::PendingTransactionUpdate, true); +} if (isRestoring() && checkMigration(ExternalGeo)) { - for( auto g : ExternalGeo.getValues()) { - if(g->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) { + for( auto geometryValue : ExternalGeo.getValues()) { + if(geometryValue->hasExtension(Part::GeometryMigrationExtension::getClassTypeId())) { auto ext = std::static_pointer_cast( - g->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock()); + geometryValue->getExtension(Part::GeometryMigrationExtension::getClassTypeId()).lock()); std::unique_ptr egf; if(ext->testMigrationType(Part::GeometryMigrationExtension::GeometryId)) { - egf = ExternalGeometryFacade::getFacade(g); + egf = ExternalGeometryFacade::getFacade(geometryValue); egf->setId(ext->getId()); } if(ext->testMigrationType(Part::GeometryMigrationExtension::ExternalReference)) { - if (!egf) - egf = ExternalGeometryFacade::getFacade(g); + if (!egf) { + egf = ExternalGeometryFacade::getFacade(geometryValue); + } egf->setRef(ext->getRef()); egf->setRefIndex(ext->getRefIndex()); egf->setFlags(ext->getFlags()); @@ -10230,34 +10232,36 @@ void SketchObject::onChanged(const App::Property* prop) auto geo = ExternalGeo[i]; auto egf = ExternalGeometryFacade::getFacade(geo); if(egf->testFlag(ExternalGeometryExtension::Detached)) { - if(egf->getRef().size()) { + if(!egf->getRef().empty()) { detached.insert(egf->getRef()); egf->setRef(std::string()); } egf->setFlag(ExternalGeometryExtension::Detached,false); egf->setFlag(ExternalGeometryExtension::Missing,false); } - if(egf->getId() > geoLastId) + if(egf->getId() > geoLastId) { geoLastId = egf->getId(); + } if(!externalGeoMap.emplace(egf->getId(),i).second) { - FC_WARN("duplicate geometry id " << egf->getId() << " -> " << geoLastId+1); + FC_WARN("duplicate geometry id " << egf->getId() << " -> " << geoLastId+1); // NOLINT egf->setId(++geoLastId); externalGeoMap[egf->getId()] = i; } - if(egf->getRef().size()) + if(!egf->getRef().empty()) { externalGeoRefMap[egf->getRef()].push_back(egf->getId()); + } } - if(detached.size()) { + if(!detached.empty()) { auto objs = ExternalGeometry.getValues(); assert(externalGeoRef.size() == objs.size()); auto itObj = objs.begin(); auto subs = ExternalGeometry.getSubValues(); auto itSub = subs.begin(); - for(size_t i=0;iisPerformingTransaction()) + if(doc && doc->isPerformingTransaction()) { setStatus(App::PendingTransactionUpdate, true); + } if(!isRestoring()) { // must wait till onDocumentRestored() when shadow references are @@ -10287,10 +10294,10 @@ void SketchObject::onChanged(const App::Property* prop) signalElementsChanged(); } } else if (prop == &Placement) { - if (ExternalGeometry.getSize() > 0) + if (ExternalGeometry.getSize() > 0) { touch(); + } } else if (prop == &ExpressionEngine) { - auto doc = getDocument(); if(!isRestoring() && doc && !doc->isPerformingTransaction() && noRecomputes @@ -10301,12 +10308,12 @@ void SketchObject::onChanged(const App::Property* prop) try { auto res = ExpressionEngine.execute(); if(res) { - FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << res->Why); + FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << res->Why); // NOLINT delete res; } } catch (Base::Exception &e) { e.ReportException(); - FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << e.what()); + FC_ERR("Failed to recompute " << ExpressionEngine.getFullName() << ": " << e.what()); // NOLINT } solve(); } @@ -10366,17 +10373,19 @@ void SketchObject::updateGeometryRefs() { std::unordered_map legacyMap; for(int i=0;i<(int)objs.size();++i) { auto obj = objs[i]; - const std::string &sub=shadows[i].newName.size()?shadows[i].newName:subs[i]; + const std::string &sub = shadows[i].newName.empty() ? subs[i] : shadows[i].newName; externalGeoRef.emplace_back(obj->getNameInDocument()); auto &key = externalGeoRef.back(); key += '.'; legacyMap[key + Data::oldElementName(sub.c_str())] = i; - if (!obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) + if (!obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { key += Data::newElementName(sub.c_str()); - if (originalRefs.size() && originalRefs[i] != key) + } + if (!originalRefs.empty() && originalRefs[i] != key) { refMap[originalRefs[i]] = key; + } } bool touched = false; auto geos = ExternalGeo.getValues(); @@ -10384,7 +10393,8 @@ void SketchObject::updateGeometryRefs() { for(auto geo : geos) { auto egf = ExternalGeometryFacade::getFacade(geo); if(egf->getRefIndex()<0) { - if (egf->getId() < 0 && egf->getRef().size()) { + if (egf->getId() < 0 && !egf->getRef().empty()) { + // NOLINTNEXTLINE FC_ERR("External geometry reference corrupted in " << getFullName() << " Please check."); // This could happen if someone saved the sketch containing @@ -10405,17 +10415,20 @@ void SketchObject::updateGeometryRefs() { // which should be considered as normal. So warning only // if not undo/redo. // + // NOLINTNEXTLINE FC_WARN("Update legacy external reference " << egf->getRef() << " -> " << externalGeoRef[it->second] << " in " << getFullName()); - } else + } else { + // NOLINTNEXTLINE FC_LOG("Update undo/redo external reference " << egf->getRef() << " -> " << externalGeoRef[it->second] << " in " << getFullName()); + } touched = true; egf->setRef(externalGeoRef[it->second]); } continue; } - else if(egf->getRefIndex() < (int)externalGeoRef.size() + if(egf->getRefIndex() < (int)externalGeoRef.size() && egf->getRef() != externalGeoRef[egf->getRefIndex()]) { touched = true; @@ -10426,14 +10439,16 @@ void SketchObject::updateGeometryRefs() { }else{ for(auto &v : refMap) { auto it = externalGeoRefMap.find(v.first); - if(it == externalGeoRefMap.end()) + if (it == externalGeoRefMap.end()) { continue; + } for(long id : it->second) { auto iter = externalGeoMap.find(id); if(iter!=externalGeoMap.end()) { auto &geo = geos[iter->second]; geo = geo->clone(); auto egf = ExternalGeometryFacade::getFacade(geo); + // NOLINTNEXTLINE FC_LOG(getFullName() << " ref change on ExternalEdge" << iter->second-1 << ' ' << egf->getRef() << " -> " << v.second); egf->setRef(v.second); @@ -10442,8 +10457,9 @@ void SketchObject::updateGeometryRefs() { } } } - if(touched) + if(touched) { ExternalGeo.setValues(std::move(geos)); + } } void SketchObject::onUndoRedoFinished() @@ -11468,27 +11484,32 @@ Data::IndexedName SketchObject::checkSubName(const char *subname) const{ Data::IndexedName SketchObject::shapeTypeFromGeoId(int geoId, PointPos posId) const { if(geoId == GeoEnum::HAxis) { - if(posId == PointPos::start) + if(posId == PointPos::start) { return Data::IndexedName::fromConst("RootPoint", 0); + } return Data::IndexedName::fromConst("H_Axis", 0); - }else if(geoId == GeoEnum::VAxis) + } + if(geoId == GeoEnum::VAxis) { return Data::IndexedName::fromConst("V_Axis", 0); + } if (posId == PointPos::none) { auto geo = getGeometry(geoId); - if (geo && geo->isDerivedFrom(Part::GeomPoint::getClassTypeId())) + if (geo && geo->isDerivedFrom(Part::GeomPoint::getClassTypeId())) { posId = PointPos::start; + } } if(posId != PointPos::none) { int idx = getVertexIndexGeoPos(geoId, posId); - if(idx < 0) + if(idx < 0){ return Data::IndexedName(); + } return Data::IndexedName::fromConst("Vertex", idx+1); } - if(geoId >= 0) + if (geoId >= 0) { return Data::IndexedName::fromConst("Edge", geoId+1); - else - return Data::IndexedName::fromConst("ExternalEdge", -geoId-2); + } + return Data::IndexedName::fromConst("ExternalEdge", -geoId-2); } bool SketchObject::geoIdFromShapeType(const Data::IndexedName & indexedName, @@ -11585,29 +11606,35 @@ std::string SketchObject::convertSubName(const Data::IndexedName &indexedName, b std::string SketchObject::getGeometryReference(int GeoId) const { auto geo = getGeometry(GeoId); - if (!geo) - return std::string(); + if (!geo) { + return {}; + } auto egf = ExternalGeometryFacade::getFacade(geo); - if (egf->getRef().empty()) - return std::string(); + if (egf->getRef().empty()) { + return {}; + } const std::string &ref = egf->getRef(); - if(egf->testFlag(ExternalGeometryExtension::Missing)) + if(egf->testFlag(ExternalGeometryExtension::Missing)) { return std::string("? ") + ref; + } auto pos = ref.find('.'); - if(pos == std::string::npos) + if(pos == std::string::npos) { return ref; + } std::string objName = ref.substr(0,pos); auto obj = getDocument()->getObject(objName.c_str()); - if(!obj) + if(!obj) { return ref; + } App::ElementNamePair elementName; App::GeoFeature::resolveElement(obj,ref.c_str()+pos+1,elementName); - if(elementName.oldName.size()) + if (!elementName.oldName.empty()) { return objName + "." + elementName.oldName; + } return ref; }