| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <cmath> |
| |
|
| | #include <BRep_Tool.hxx> |
| | #include <Precision.hxx> |
| | #include <TopExp.hxx> |
| | #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx> |
| | #include <TopoDS.hxx> |
| | #include <TopoDS_Shape.hxx> |
| | #include <TopoDS_Vertex.hxx> |
| | #include <gp_Pnt.hxx> |
| |
|
| | #include <App/Document.h> |
| | #include <Base/Console.h> |
| |
|
| | #include "GeometryFacade.h" |
| | #include "SketchAnalysis.h" |
| | #include "SketchObject.h" |
| |
|
| |
|
| | using namespace Sketcher; |
| |
|
| | SketchAnalysis::SketchAnalysis(Sketcher::SketchObject* Obj) |
| | : sketch(Obj) |
| | {} |
| |
|
| | SketchAnalysis::~SketchAnalysis() = default; |
| |
|
| | namespace |
| | { |
| | struct VertexIds |
| | { |
| | Base::Vector3d v; |
| | int GeoId {}; |
| | Sketcher::PointPos PosId {}; |
| | }; |
| |
|
| | struct Vertex_Less |
| | { |
| | explicit Vertex_Less(double tolerance) |
| | : tolerance(tolerance) |
| | {} |
| | bool operator()(const VertexIds& x, const VertexIds& y) const |
| | { |
| | if (fabs(x.v.x - y.v.x) > tolerance) { |
| | return x.v.x < y.v.x; |
| | } |
| | if (fabs(x.v.y - y.v.y) > tolerance) { |
| | return x.v.y < y.v.y; |
| | } |
| | if (fabs(x.v.z - y.v.z) > tolerance) { |
| | return x.v.z < y.v.z; |
| | } |
| | return false; |
| | } |
| |
|
| | private: |
| | double tolerance; |
| | }; |
| |
|
| | struct VertexID_Less |
| | { |
| | bool operator()(const VertexIds& x, const VertexIds& y) const |
| | { |
| | return (x.GeoId < y.GeoId || ((x.GeoId == y.GeoId) && (x.PosId < y.PosId))); |
| | } |
| | }; |
| |
|
| | struct Vertex_EqualTo |
| | { |
| | explicit Vertex_EqualTo(double tolerance) |
| | : tolerance(tolerance) |
| | {} |
| | bool operator()(const VertexIds& x, const VertexIds& y) const |
| | { |
| | if (fabs(x.v.x - y.v.x) <= tolerance) { |
| | if (fabs(x.v.y - y.v.y) <= tolerance) { |
| | if (fabs(x.v.z - y.v.z) <= tolerance) { |
| | return true; |
| | } |
| | } |
| | } |
| | return false; |
| | } |
| |
|
| | private: |
| | double tolerance; |
| | }; |
| |
|
| | struct EdgeIds |
| | { |
| | double l {}; |
| | int GeoId {}; |
| | }; |
| |
|
| | struct Edge_Less |
| | { |
| | explicit Edge_Less(double tolerance) |
| | : tolerance(tolerance) |
| | {} |
| | bool operator()(const EdgeIds& x, const EdgeIds& y) const |
| | { |
| | if (fabs(x.l - y.l) > tolerance) { |
| | return x.l < y.l; |
| | } |
| | return false; |
| | } |
| |
|
| | private: |
| | double tolerance; |
| | }; |
| |
|
| | struct Edge_EqualTo |
| | { |
| | explicit Edge_EqualTo(double tolerance) |
| | : tolerance(tolerance) |
| | {} |
| | bool operator()(const EdgeIds& x, const EdgeIds& y) const |
| | { |
| | return (fabs(x.l - y.l) <= tolerance); |
| | } |
| |
|
| | private: |
| | double tolerance; |
| | }; |
| |
|
| | struct PointConstraints |
| | { |
| | void addGeometry(const Part::Geometry* geo, int index) |
| | { |
| | if (const auto* segm = dynamic_cast<const Part::GeomLineSegment*>(geo)) { |
| | addLineSegment(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomArcOfCircle*>(geo)) { |
| | addArcOfCircle(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomArcOfEllipse*>(geo)) { |
| | addArcOfEllipse(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomArcOfHyperbola*>(geo)) { |
| | addArcOfHyperbola(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomArcOfParabola*>(geo)) { |
| | addArcOfParabola(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomBSplineCurve*>(geo)) { |
| | addBSplineCurve(segm, index); |
| | } |
| | } |
| |
|
| | void addLineSegment(const Part::GeomLineSegment* segm, int index) |
| | { |
| | VertexIds id; |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::start; |
| | id.v = segm->getStartPoint(); |
| | vertexIds.push_back(id); |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::end; |
| | id.v = segm->getEndPoint(); |
| | vertexIds.push_back(id); |
| | } |
| |
|
| | void addArcOfCircle(const Part::GeomArcOfCircle* segm, int index) |
| | { |
| | VertexIds id; |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::start; |
| | id.v = segm->getStartPoint(true); |
| | vertexIds.push_back(id); |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::end; |
| | id.v = segm->getEndPoint(true); |
| | vertexIds.push_back(id); |
| | } |
| |
|
| | void addArcOfEllipse(const Part::GeomArcOfEllipse* segm, int index) |
| | { |
| | VertexIds id; |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::start; |
| | id.v = segm->getStartPoint(true); |
| | vertexIds.push_back(id); |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::end; |
| | id.v = segm->getEndPoint(true); |
| | vertexIds.push_back(id); |
| | } |
| |
|
| | void addArcOfHyperbola(const Part::GeomArcOfHyperbola* segm, int index) |
| | { |
| | VertexIds id; |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::start; |
| | id.v = segm->getStartPoint(); |
| | vertexIds.push_back(id); |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::end; |
| | id.v = segm->getEndPoint(); |
| | vertexIds.push_back(id); |
| | } |
| |
|
| | void addArcOfParabola(const Part::GeomArcOfParabola* segm, int index) |
| | { |
| | VertexIds id; |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::start; |
| | id.v = segm->getStartPoint(); |
| | vertexIds.push_back(id); |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::end; |
| | id.v = segm->getEndPoint(); |
| | vertexIds.push_back(id); |
| | } |
| |
|
| | void addBSplineCurve(const Part::GeomBSplineCurve* segm, int index) |
| | { |
| | VertexIds id; |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::start; |
| | id.v = segm->getStartPoint(); |
| | vertexIds.push_back(id); |
| | id.GeoId = index; |
| | id.PosId = Sketcher::PointPos::end; |
| | id.v = segm->getEndPoint(); |
| | vertexIds.push_back(id); |
| | } |
| |
|
| | std::list<ConstraintIds> getMissingCoincidences( |
| | std::vector<Sketcher::Constraint*>& allcoincid, |
| | double precision |
| | ) |
| | { |
| | std::list<ConstraintIds> missingCoincidences; |
| |
|
| | |
| | std::sort(vertexIds.begin(), vertexIds.end(), Vertex_Less(precision)); |
| |
|
| | auto vt = vertexIds.begin(); |
| | Vertex_EqualTo pred(precision); |
| |
|
| | |
| |
|
| | while (vt < vertexIds.end()) { |
| | |
| | vt = std::adjacent_find(vt, vertexIds.end(), pred); |
| | if (vt < vertexIds.end()) { |
| | std::vector<VertexIds>::iterator vn; |
| | |
| | std::set<VertexIds, VertexID_Less> vertexGrp; |
| | |
| | vertexGrp.insert(*vt); |
| | for (vn = vt + 1; vn < vertexIds.end(); ++vn) { |
| | if (pred(*vt, *vn)) { |
| | vertexGrp.insert(*vn); |
| | } |
| | else { |
| | break; |
| | } |
| | } |
| |
|
| | |
| | std::vector<std::set<VertexIds, VertexID_Less>> coincVertexGrps; |
| |
|
| | |
| | |
| | for (auto& coincidence : allcoincid) { |
| | VertexIds v1; |
| | VertexIds v2; |
| | v1.GeoId = coincidence->First; |
| | v1.PosId = coincidence->FirstPos; |
| | v2.GeoId = coincidence->Second; |
| | v2.PosId = coincidence->SecondPos; |
| |
|
| | |
| | |
| | auto nv1 = vertexGrp.extract(v1); |
| | auto nv2 = vertexGrp.extract(v2); |
| |
|
| | |
| | |
| | if (nv1.empty() && nv2.empty()) { |
| | std::set<VertexIds, VertexID_Less>* tempGrp = nullptr; |
| | for (auto it = coincVertexGrps.begin(); it < coincVertexGrps.end(); ++it) { |
| | if ((it->find(v1) != it->end()) || (it->find(v2) != it->end())) { |
| | if (!tempGrp) { |
| | tempGrp = &*it; |
| | } |
| | else { |
| | tempGrp->insert(it->begin(), it->end()); |
| | coincVertexGrps.erase(it); |
| | break; |
| | } |
| | } |
| | } |
| | continue; |
| | } |
| |
|
| | |
| | |
| | for (std::set<VertexIds, VertexID_Less>& grp : coincVertexGrps) { |
| | if ((grp.find(v1) != grp.end()) || (grp.find(v2) != grp.end())) { |
| | |
| | if (!nv1.empty()) { |
| | grp.insert(nv1.value()); |
| | } |
| | if (!nv2.empty()) { |
| | grp.insert(nv2.value()); |
| | } |
| | continue; |
| | } |
| | } |
| |
|
| | if (nv1.empty() || nv2.empty()) { |
| | continue; |
| | } |
| |
|
| | |
| | std::set<VertexIds, VertexID_Less> newGrp; |
| | newGrp.insert(nv1.value()); |
| | newGrp.insert(nv2.value()); |
| | coincVertexGrps.push_back(newGrp); |
| | } |
| |
|
| | |
| | |
| | for (auto& lonept : vertexGrp) { |
| | std::set<VertexIds, VertexID_Less> newGrp; |
| | newGrp.insert(lonept); |
| | coincVertexGrps.push_back(newGrp); |
| | } |
| |
|
| | |
| | |
| | if (coincVertexGrps.size() > 1) { |
| | std::vector<std::set<VertexIds, VertexID_Less>>::iterator vn; |
| | |
| | |
| | for (vn = coincVertexGrps.begin() + 1; vn < coincVertexGrps.end(); ++vn) { |
| | ConstraintIds id; |
| | id.Type = Coincident; |
| | id.v = (vn - 1)->begin()->v; |
| | id.First = (vn - 1)->begin()->GeoId; |
| | id.FirstPos = (vn - 1)->begin()->PosId; |
| | id.Second = vn->begin()->GeoId; |
| | id.SecondPos = vn->begin()->PosId; |
| | missingCoincidences.push_back(id); |
| | } |
| | } |
| |
|
| | vt = vn; |
| | } |
| | } |
| |
|
| | return missingCoincidences; |
| | } |
| |
|
| | private: |
| | |
| | std::vector<VertexIds> vertexIds; |
| | }; |
| |
|
| | struct EqualityConstraints |
| | { |
| | void addGeometry(const Part::Geometry* geo, int index) |
| | { |
| | if (const auto* segm = dynamic_cast<const Part::GeomLineSegment*>(geo)) { |
| | addLineSegment(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomArcOfCircle*>(geo)) { |
| | addArcOfCircle(segm, index); |
| | } |
| | else if (const auto* segm = dynamic_cast<const Part::GeomCircle*>(geo)) { |
| | addCircle(segm, index); |
| | } |
| | } |
| |
|
| | void addLineSegment(const Part::GeomLineSegment* segm, int index) |
| | { |
| | EdgeIds id; |
| | id.GeoId = index; |
| | id.l = (segm->getEndPoint() - segm->getStartPoint()).Length(); |
| | lineedgeIds.push_back(id); |
| | } |
| |
|
| | void addArcOfCircle(const Part::GeomArcOfCircle* segm, int index) |
| | { |
| | EdgeIds id; |
| | id.GeoId = index; |
| | id.l = segm->getRadius(); |
| | radiusedgeIds.push_back(id); |
| | } |
| |
|
| | void addCircle(const Part::GeomCircle* segm, int index) |
| | { |
| | EdgeIds id; |
| | id.GeoId = index; |
| | id.l = segm->getRadius(); |
| | radiusedgeIds.push_back(id); |
| | } |
| |
|
| | std::list<ConstraintIds> getEqualLines(double precision) |
| | { |
| | std::sort(lineedgeIds.begin(), lineedgeIds.end(), Edge_Less(precision)); |
| | auto vt = lineedgeIds.begin(); |
| | Edge_EqualTo pred(precision); |
| |
|
| | std::list<ConstraintIds> equallines; |
| | |
| | while (vt < lineedgeIds.end()) { |
| | |
| | vt = std::adjacent_find(vt, lineedgeIds.end(), pred); |
| | if (vt < lineedgeIds.end()) { |
| | std::vector<EdgeIds>::iterator vn; |
| | for (vn = vt + 1; vn != lineedgeIds.end(); ++vn) { |
| | if (pred(*vt, *vn)) { |
| | ConstraintIds id; |
| | id.Type = Equal; |
| | id.v.x = vt->l; |
| | id.First = vt->GeoId; |
| | id.FirstPos = Sketcher::PointPos::none; |
| | id.Second = vn->GeoId; |
| | id.SecondPos = Sketcher::PointPos::none; |
| | equallines.push_back(id); |
| | } |
| | else { |
| | break; |
| | } |
| | } |
| |
|
| | vt = vn; |
| | } |
| | } |
| |
|
| | return equallines; |
| | } |
| |
|
| | std::list<ConstraintIds> getEqualRadius(double precision) |
| | { |
| | std::sort(radiusedgeIds.begin(), radiusedgeIds.end(), Edge_Less(precision)); |
| | auto vt = radiusedgeIds.begin(); |
| | Edge_EqualTo pred(precision); |
| |
|
| | std::list<ConstraintIds> equalradius; |
| | |
| | while (vt < radiusedgeIds.end()) { |
| | |
| | vt = std::adjacent_find(vt, radiusedgeIds.end(), pred); |
| | if (vt < radiusedgeIds.end()) { |
| | std::vector<EdgeIds>::iterator vn; |
| | for (vn = vt + 1; vn != radiusedgeIds.end(); ++vn) { |
| | if (pred(*vt, *vn)) { |
| | ConstraintIds id; |
| | id.Type = Equal; |
| | id.v.x = vt->l; |
| | id.First = vt->GeoId; |
| | id.FirstPos = Sketcher::PointPos::none; |
| | id.Second = vn->GeoId; |
| | id.SecondPos = Sketcher::PointPos::none; |
| | equalradius.push_back(id); |
| | } |
| | else { |
| | break; |
| | } |
| | } |
| |
|
| | vt = vn; |
| | } |
| | } |
| |
|
| | return equalradius; |
| | } |
| |
|
| | private: |
| | std::vector<EdgeIds> lineedgeIds; |
| | std::vector<EdgeIds> radiusedgeIds; |
| | }; |
| |
|
| | } |
| |
|
| | int SketchAnalysis::detectMissingPointOnPointConstraints(double precision, bool includeconstruction ) |
| | { |
| | PointConstraints pointConstr; |
| |
|
| | |
| | const std::vector<Part::Geometry*>& geom = sketch->getInternalGeometry(); |
| | for (std::size_t i = 0; i < geom.size(); i++) { |
| | auto gf = GeometryFacade::getFacade(geom[i]); |
| |
|
| | if (gf->getConstruction() && !includeconstruction) { |
| | continue; |
| | } |
| |
|
| | pointConstr.addGeometry(gf->getGeometry(), int(i)); |
| | |
| | } |
| |
|
| | |
| |
|
| | std::vector<Sketcher::Constraint*> coincidences = sketch->Constraints.getValues(); |
| | for (auto& constraint : sketch->Constraints.getValues()) { |
| | |
| | if (constraint->Type == Sketcher::Coincident || |
| | constraint->Type == Sketcher::Tangent || |
| | constraint->Type == Sketcher::Perpendicular) { |
| | coincidences.push_back(constraint); |
| | } |
| | |
| | |
| | } |
| |
|
| | |
| | std::list<ConstraintIds> missingCoincidences |
| | = pointConstr.getMissingCoincidences(coincidences, precision); |
| |
|
| | |
| | this->vertexConstraints.clear(); |
| | this->vertexConstraints.reserve(missingCoincidences.size()); |
| |
|
| | for (auto& coincidence : missingCoincidences) { |
| | this->vertexConstraints.push_back(coincidence); |
| | } |
| |
|
| | |
| | return int(this->vertexConstraints.size()); |
| | } |
| |
|
| | void SketchAnalysis::analyseMissingPointOnPointCoincident(double angleprecision) |
| | { |
| | for (auto& vc : vertexConstraints) { |
| |
|
| | auto geo1 = sketch->getGeometry(vc.First); |
| | auto geo2 = sketch->getGeometry(vc.Second); |
| |
|
| | |
| | const auto* curve1 = dynamic_cast<const Part::GeomCurve*>(geo1); |
| | const auto* curve2 = dynamic_cast<const Part::GeomCurve*>(geo2); |
| |
|
| | if (curve1 && curve2) { |
| |
|
| | const auto* segm1 = dynamic_cast<const Part::GeomLineSegment*>(geo1); |
| | const auto* segm2 = dynamic_cast<const Part::GeomLineSegment*>(geo2); |
| |
|
| | if (segm1 && segm2) { |
| |
|
| | Base::Vector3d dir1 = segm1->getEndPoint() - segm1->getStartPoint(); |
| | Base::Vector3d dir2 = segm2->getEndPoint() - segm2->getStartPoint(); |
| |
|
| | if ((checkVertical(dir1, angleprecision) || checkHorizontal(dir1, angleprecision)) |
| | && (checkVertical(dir2, angleprecision) || checkHorizontal(dir2, angleprecision))) { |
| | |
| | continue; |
| | } |
| | } |
| |
|
| | try { |
| | double u1 {}; |
| | double u2 {}; |
| |
|
| | curve1->closestParameter(vc.v, u1); |
| | curve2->closestParameter(vc.v, u2); |
| |
|
| | Base::Vector3d tgv1 = curve1->firstDerivativeAtParameter(u1).Normalize(); |
| | Base::Vector3d tgv2 = curve2->firstDerivativeAtParameter(u2).Normalize(); |
| |
|
| | if (fabs(tgv1 * tgv2) > fabs(cos(angleprecision))) { |
| | vc.Type = Sketcher::Tangent; |
| | } |
| | else if (fabs(tgv1 * tgv2) < fabs(cos(std::numbers::pi / 2 - angleprecision))) { |
| | vc.Type = Sketcher::Perpendicular; |
| | } |
| | } |
| | catch (Base::Exception&) { |
| | Base::Console().warning( |
| | "Point-On-Point Coincidence analysis: unable to obtain " |
| | "derivative. Detection ignored.\n" |
| | ); |
| | continue; |
| | } |
| | } |
| | } |
| | } |
| |
|
| | Sketcher::Constraint* SketchAnalysis::create(const ConstraintIds& id) |
| | { |
| | auto c = new Sketcher::Constraint(); |
| | c->Type = id.Type; |
| | c->First = id.First; |
| | c->Second = id.Second; |
| | c->FirstPos = id.FirstPos; |
| | c->SecondPos = id.SecondPos; |
| | return c; |
| | } |
| |
|
| | void SketchAnalysis::solveSketch(const char* errorText) |
| | { |
| | int status {}; |
| | int dofs {}; |
| | solvesketch(status, dofs, true); |
| |
|
| | if (status == int(Solver::RedundantConstraints)) { |
| | sketch->autoRemoveRedundants(DeleteOption::NoFlag); |
| |
|
| | solvesketch(status, dofs, false); |
| | } |
| |
|
| | if (status) { |
| | THROWMT(Base::RuntimeError, errorText); |
| | } |
| | } |
| |
|
| | void SketchAnalysis::makeConstraints(std::vector<ConstraintIds>& ids) |
| | { |
| | std::vector<Sketcher::Constraint*> constr; |
| | constr.reserve(ids.size()); |
| | for (const auto& it : ids) { |
| | auto c = create(it); |
| | constr.push_back(c); |
| | } |
| |
|
| | sketch->addConstraints(constr); |
| | ids.clear(); |
| |
|
| | for (auto it : constr) { |
| | delete it; |
| | } |
| | } |
| |
|
| | void SketchAnalysis::makeConstraintsOneByOne(std::vector<ConstraintIds>& ids, const char* errorText) |
| | { |
| | for (const auto& it : ids) { |
| | auto c = create(it); |
| |
|
| | |
| | sketch->addConstraint(c); |
| | delete c; |
| |
|
| | solveSketch(errorText); |
| | } |
| |
|
| | ids.clear(); |
| | } |
| |
|
| | void SketchAnalysis::makeMissingPointOnPointCoincident() |
| | { |
| | makeConstraints(vertexConstraints); |
| | } |
| |
|
| | void SketchAnalysis::makeMissingPointOnPointCoincidentOneByOne() |
| | { |
| | makeConstraintsOneByOne( |
| | vertexConstraints, |
| | QT_TRANSLATE_NOOP( |
| | "Exceptions", |
| | "Autoconstraint error: Unsolvable sketch while " |
| | "applying coincident constraints." |
| | ) |
| | ); |
| | } |
| |
|
| | int SketchAnalysis::detectMissingVerticalHorizontalConstraints(double angleprecision) |
| | { |
| | const std::vector<Part::Geometry*>& geom = sketch->getInternalGeometry(); |
| |
|
| | verthorizConstraints.clear(); |
| |
|
| | for (std::size_t i = 0; i < geom.size(); i++) { |
| | Part::Geometry* g = geom[i]; |
| |
|
| | if (const auto* segm = dynamic_cast<const Part::GeomLineSegment*>(g)) { |
| | Base::Vector3d dir = segm->getEndPoint() - segm->getStartPoint(); |
| |
|
| | ConstraintIds id; |
| |
|
| | id.v = dir; |
| | id.First = (int)i; |
| | id.FirstPos = Sketcher::PointPos::none; |
| | id.Second = GeoEnum::GeoUndef; |
| | id.SecondPos = Sketcher::PointPos::none; |
| |
|
| | if (checkVertical(dir, angleprecision)) { |
| | id.Type = Sketcher::Vertical; |
| | verthorizConstraints.push_back(id); |
| | } |
| | else if (checkHorizontal(dir, angleprecision)) { |
| | id.Type = Sketcher::Horizontal; |
| | verthorizConstraints.push_back(id); |
| | } |
| | } |
| | } |
| |
|
| | return int(verthorizConstraints.size()); |
| | } |
| |
|
| | void SketchAnalysis::makeMissingVerticalHorizontal() |
| | { |
| | makeConstraints(verthorizConstraints); |
| | } |
| |
|
| | void SketchAnalysis::makeMissingVerticalHorizontalOneByOne() |
| | { |
| | makeConstraintsOneByOne( |
| | verthorizConstraints, |
| | QT_TRANSLATE_NOOP( |
| | "Exceptions", |
| | "Autoconstraint error: Unsolvable sketch while " |
| | "applying vertical/horizontal constraints." |
| | ) |
| | ); |
| | } |
| |
|
| | bool SketchAnalysis::checkVertical(Base::Vector3d dir, double angleprecision) |
| | { |
| | return (dir.x == 0. && dir.y != 0.) |
| | || (fabs(dir.y / dir.x) > tan(std::numbers::pi / 2 - angleprecision)); |
| | } |
| |
|
| | bool SketchAnalysis::checkHorizontal(Base::Vector3d dir, double angleprecision) |
| | { |
| | return (dir.y == 0. && dir.x != 0.) || (fabs(dir.x / dir.y) > (1 / tan(angleprecision))); |
| | } |
| |
|
| | int SketchAnalysis::detectMissingEqualityConstraints(double precision) |
| | { |
| | EqualityConstraints equalConstr; |
| |
|
| | const std::vector<Part::Geometry*>& geom = sketch->getInternalGeometry(); |
| | for (std::size_t i = 0; i < geom.size(); i++) { |
| | Part::Geometry* g = geom[i]; |
| | equalConstr.addGeometry(g, int(i)); |
| | } |
| |
|
| | std::list<ConstraintIds> equallines = equalConstr.getEqualLines(precision); |
| | std::list<ConstraintIds> equalradius = equalConstr.getEqualRadius(precision); |
| |
|
| | |
| | |
| | |
| | std::vector<Sketcher::Constraint*> constraint = sketch->Constraints.getValues(); |
| | for (auto it : constraint) { |
| | if (it->Type == Sketcher::Equal) { |
| | ConstraintIds |
| | id {Base::Vector3d {}, it->First, it->Second, it->FirstPos, it->SecondPos, it->Type}; |
| |
|
| | auto pos = std::find_if(equallines.begin(), equallines.end(), Constraint_Equal(id)); |
| |
|
| | if (pos != equallines.end()) { |
| | equallines.erase(pos); |
| | } |
| |
|
| | pos = std::find_if(equalradius.begin(), equalradius.end(), Constraint_Equal(id)); |
| |
|
| | if (pos != equalradius.end()) { |
| | equalradius.erase(pos); |
| | } |
| | } |
| | } |
| |
|
| | this->lineequalityConstraints.clear(); |
| | this->lineequalityConstraints.reserve(equallines.size()); |
| |
|
| | for (const auto& it : equallines) { |
| | this->lineequalityConstraints.push_back(it); |
| | } |
| |
|
| | this->radiusequalityConstraints.clear(); |
| | this->radiusequalityConstraints.reserve(equalradius.size()); |
| |
|
| | for (const auto& it : equalradius) { |
| | this->radiusequalityConstraints.push_back(it); |
| | } |
| |
|
| | return int(this->lineequalityConstraints.size() + this->radiusequalityConstraints.size()); |
| | } |
| |
|
| | void SketchAnalysis::makeMissingEquality() |
| | { |
| | std::vector<Sketcher::ConstraintIds> equalities(lineequalityConstraints); |
| | equalities.insert( |
| | equalities.end(), |
| | radiusequalityConstraints.begin(), |
| | radiusequalityConstraints.end() |
| | ); |
| | makeConstraints(equalities); |
| |
|
| | lineequalityConstraints.clear(); |
| | radiusequalityConstraints.clear(); |
| | } |
| |
|
| | void SketchAnalysis::makeMissingEqualityOneByOne() |
| | { |
| | std::vector<Sketcher::ConstraintIds> equalities(lineequalityConstraints); |
| | equalities.insert( |
| | equalities.end(), |
| | radiusequalityConstraints.begin(), |
| | radiusequalityConstraints.end() |
| | ); |
| |
|
| | makeConstraintsOneByOne( |
| | equalities, |
| | QT_TRANSLATE_NOOP( |
| | "Exceptions", |
| | "Autoconstraint error: Unsolvable sketch while " |
| | "applying equality constraints." |
| | ) |
| | ); |
| | lineequalityConstraints.clear(); |
| | radiusequalityConstraints.clear(); |
| | } |
| |
|
| | void SketchAnalysis::solvesketch(int& status, int& dofs, bool updategeo) |
| | { |
| | status = sketch->solve(updategeo); |
| |
|
| | if (updategeo) { |
| | dofs = sketch->setUpSketch(); |
| | } |
| | else { |
| | dofs = sketch->getLastDoF(); |
| | } |
| |
|
| | if (sketch->getLastHasRedundancies()) { |
| | status = int(Solver::RedundantConstraints); |
| | } |
| |
|
| | if (dofs < 0) { |
| | status = int(Solver::OverConstrained); |
| | } |
| | else if (sketch->getLastHasConflicts()) { |
| | status = int(Solver::ConflictingConstraints); |
| | } |
| | } |
| |
|
| | void SketchAnalysis::autoDeleteAllConstraints() |
| | { |
| | App::Document* doc = sketch->getDocument(); |
| | doc->openTransaction("delete all constraints"); |
| | |
| | sketch->deleteAllConstraints(); |
| |
|
| | doc->commitTransaction(); |
| |
|
| | |
| | solveSketch( |
| | QT_TRANSLATE_NOOP("Exceptions", "Autoconstraint error: Unsolvable sketch without constraints.") |
| | ); |
| | } |
| |
|
| | void SketchAnalysis::autoHorizontalVerticalConstraints() |
| | { |
| | App::Document* doc = sketch->getDocument(); |
| | doc->openTransaction("add vertical/horizontal constraints"); |
| |
|
| | makeMissingVerticalHorizontal(); |
| |
|
| | |
| | doc->commitTransaction(); |
| |
|
| | solveSketch(QT_TRANSLATE_NOOP( |
| | "Exceptions", |
| | "Autoconstraint error: Unsolvable sketch after applying " |
| | "horizontal and vertical constraints." |
| | )); |
| | } |
| |
|
| | void SketchAnalysis::autoPointOnPointCoincident() |
| | { |
| | App::Document* doc = sketch->getDocument(); |
| | doc->openTransaction("add coincident constraint"); |
| |
|
| | makeMissingPointOnPointCoincident(); |
| |
|
| | |
| | doc->commitTransaction(); |
| |
|
| | solveSketch(QT_TRANSLATE_NOOP( |
| | "Exceptions", |
| | "Autoconstraint error: Unsolvable sketch after applying " |
| | "point-on-point constraints." |
| | )); |
| | } |
| |
|
| | void SketchAnalysis::autoMissingEquality() |
| | { |
| | App::Document* doc = sketch->getDocument(); |
| | doc->openTransaction("add equality constraints"); |
| |
|
| | try { |
| | makeMissingEquality(); |
| | } |
| | catch (Base::RuntimeError&) { |
| | doc->abortTransaction(); |
| | throw; |
| | } |
| |
|
| | |
| | doc->commitTransaction(); |
| |
|
| | solveSketch(QT_TRANSLATE_NOOP( |
| | "Exceptions", |
| | "Autoconstraint error: Unsolvable sketch after " |
| | "applying equality constraints." |
| | )); |
| | } |
| |
|
| | int SketchAnalysis::autoconstraint(double precision, double angleprecision, bool includeconstruction) |
| | { |
| | autoDeleteAllConstraints(); |
| |
|
| | |
| | int nhv = detectMissingVerticalHorizontalConstraints(angleprecision); |
| |
|
| | |
| | |
| | |
| | |
| | int nc = detectMissingPointOnPointConstraints(precision, includeconstruction); |
| |
|
| | if (nc > 0) { |
| | |
| | analyseMissingPointOnPointCoincident(angleprecision); |
| | } |
| |
|
| | |
| | int ne = detectMissingEqualityConstraints(precision); |
| |
|
| | Base::Console().log( |
| | "Constraints: Vertical/Horizontal: %d found. " |
| | "Point-on-point: %d. Equality: %d\n", |
| | nhv, |
| | nc, |
| | ne |
| | ); |
| |
|
| | |
| | if (nhv > 0) { |
| | autoHorizontalVerticalConstraints(); |
| | } |
| |
|
| | |
| | if (nc > 0) { |
| | autoPointOnPointCoincident(); |
| | } |
| |
|
| | |
| | if (ne > 0) { |
| | autoMissingEquality(); |
| | } |
| |
|
| | return 0; |
| | } |
| |
|
| |
|
| | std::vector<Base::Vector3d> SketchAnalysis::getOpenVertices() const |
| | { |
| | std::vector<Base::Vector3d> points; |
| | TopoDS_Shape shape = sketch->Shape.getValue(); |
| |
|
| | Base::Placement Plm = sketch->Placement.getValue(); |
| |
|
| | Base::Placement invPlm = Plm.inverse(); |
| |
|
| | |
| | TopTools_IndexedDataMapOfShapeListOfShape vertex2Edge; |
| | TopExp::MapShapesAndAncestors(shape, TopAbs_VERTEX, TopAbs_EDGE, vertex2Edge); |
| | for (int i = 1; i <= vertex2Edge.Extent(); ++i) { |
| | const TopTools_ListOfShape& los = vertex2Edge.FindFromIndex(i); |
| | if (los.Extent() != 2) { |
| | const TopoDS_Vertex& vertex = TopoDS::Vertex(vertex2Edge.FindKey(i)); |
| | gp_Pnt pnt = BRep_Tool::Pnt(vertex); |
| | Base::Vector3d pos; |
| | invPlm.multVec(Base::Vector3d(pnt.X(), pnt.Y(), pnt.Z()), pos); |
| | points.push_back(pos); |
| | } |
| | } |
| |
|
| | return points; |
| | } |
| |
|
| | std::set<int> SketchAnalysis::getDegeneratedGeometries(double tolerance) const |
| | { |
| | std::set<int> delInternalGeometries; |
| | const std::vector<Part::Geometry*>& geom = sketch->getInternalGeometry(); |
| | for (std::size_t i = 0; i < geom.size(); i++) { |
| | auto gf = GeometryFacade::getFacade(geom[i]); |
| |
|
| | if (gf->getConstruction()) { |
| | continue; |
| | } |
| |
|
| | if (auto curve = dynamic_cast<Part::GeomCurve*>(gf->getGeometry())) { |
| | double len = curve->length(curve->getFirstParameter(), curve->getLastParameter()); |
| | if (len < tolerance) { |
| | delInternalGeometries.insert(static_cast<int>(i)); |
| | } |
| | } |
| | } |
| |
|
| | return delInternalGeometries; |
| | } |
| |
|
| | int SketchAnalysis::detectDegeneratedGeometries(double tolerance) const |
| | { |
| | std::set<int> delInternalGeometries = getDegeneratedGeometries(tolerance); |
| | return static_cast<int>(delInternalGeometries.size()); |
| | } |
| |
|
| | int SketchAnalysis::removeDegeneratedGeometries(double tolerance) |
| | { |
| | std::set<int> delInternalGeometries = getDegeneratedGeometries(tolerance); |
| | for (auto it = delInternalGeometries.rbegin(); it != delInternalGeometries.rend(); ++it) { |
| | sketch->delGeometry(*it); |
| | } |
| | return static_cast<int>(delInternalGeometries.size()); |
| | } |
| |
|