Download src/Mod/Sketcher/App/Sketch.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 199 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Sketcher/App/Sketch.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Sketcher/App/Sketch.cpp
-
curl -L -o Sketch.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Sketcher/App/Sketch.cpp
199 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2010 Jürgen Riegel <juergen.riegel@web.de> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library is distributed in the hope that it will be useful, * | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| * GNU Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| ***************************************************************************/ | |
| // #define DEBUG_BLOCK_CONSTRAINT | |
| using namespace Sketcher; | |
| using namespace Base; | |
| using namespace Part; | |
| TYPESYSTEM_SOURCE(Sketcher::Sketch, Base::Persistence) | |
| Sketch::Sketch() | |
| : SolveTime(0) | |
| , RecalculateInitialSolutionWhileMovingPoint(false) | |
| , resolveAfterGeometryUpdated(false) | |
| , GCSsys() | |
| , ConstraintsCounter(0) | |
| , isInitMove(false) | |
| , isFine(true) | |
| , moveStep(0) | |
| , defaultSolver(GCS::DogLeg) | |
| , defaultSolverRedundant(GCS::DogLeg) | |
| , debugMode(GCS::Minimal) | |
| {} | |
| Sketch::~Sketch() | |
| { | |
| clear(); | |
| } | |
| void Sketch::clear() | |
| { | |
| // clear all internal data sets | |
| Points.clear(); | |
| Lines.clear(); | |
| Arcs.clear(); | |
| Circles.clear(); | |
| Ellipses.clear(); | |
| ArcsOfEllipse.clear(); | |
| ArcsOfHyperbola.clear(); | |
| ArcsOfParabola.clear(); | |
| BSplines.clear(); | |
| resolveAfterGeometryUpdated = false; | |
| // deleting the doubles allocated with new | |
| for (auto param : Parameters) { | |
| delete param; | |
| } | |
| Parameters.clear(); | |
| DrivenParameters.clear(); | |
| for (auto fixParam : FixParameters) { | |
| delete fixParam; | |
| } | |
| FixParameters.clear(); | |
| param2geoelement.clear(); | |
| pDependencyGroups.clear(); | |
| solverExtensions.clear(); | |
| internalAlignmentGeometryMap.clear(); | |
| // deleting the geometry copied into this sketch | |
| for (auto geom : Geoms) { | |
| delete geom.geo; | |
| } | |
| Geoms.clear(); | |
| // deleting the non-Driving constraints copied into this sketch | |
| // for (auto* constr : NonDrivingConstraints) { | |
| // delete constr; | |
| // } | |
| Constrs.clear(); | |
| GCSsys.clear(); | |
| isInitMove = false; | |
| ConstraintsCounter = 0; | |
| Conflicting.clear(); | |
| Redundant.clear(); | |
| PartiallyRedundant.clear(); | |
| MalformedConstraints.clear(); | |
| } | |
| bool Sketch::analyseBlockedGeometry( | |
| const std::vector<Part::Geometry*>& internalGeoList, | |
| const std::vector<Constraint*>& constraintList, | |
| std::vector<bool>& onlyblockedGeometry, | |
| std::vector<int>& blockedGeoIds | |
| ) const | |
| { | |
| // To understand this function read the documentation in Sketch.h | |
| // It is important that "onlyblockedGeometry" ONLY identifies blocked geometry | |
| // that is not affected by any other driving constraint | |
| bool doesBlockAffectOtherConstraints = false; | |
| int geoindex = 0; | |
| for (auto g : internalGeoList) { | |
| if (GeometryFacade::getBlocked(g)) { | |
| // is it only affected by one constraint, the block constraint (and this is driving), or | |
| // by any other driving constraint ? | |
| bool blockOnly = true; | |
| bool blockisDriving = false; | |
| for (auto c : constraintList) { | |
| // is block driving | |
| if (c->Type == Sketcher::Block && c->isActive && c->First == geoindex) { | |
| blockisDriving = true; | |
| } | |
| // We have another driving constraint (which may be InternalAlignment) | |
| if (c->Type != Sketcher::Block && c->isDriving | |
| && (c->First == geoindex || c->Second == geoindex || c->Third == geoindex)) { | |
| blockOnly = false; | |
| } | |
| } | |
| if (blockisDriving) { | |
| if (blockOnly) { | |
| onlyblockedGeometry[geoindex] = true; // we pre-fix this geometry | |
| } | |
| else { | |
| // we will have to pos-analyse the first diagnose result for these geometries | |
| // in order to avoid redundant constraints | |
| doesBlockAffectOtherConstraints = true; | |
| blockedGeoIds.push_back(geoindex); | |
| } | |
| } | |
| } | |
| geoindex++; | |
| } | |
| return doesBlockAffectOtherConstraints; | |
| } | |
| int Sketch::setUpSketch( | |
| const std::vector<Part::Geometry*>& GeoList, | |
| const std::vector<Constraint*>& ConstraintList, | |
| int extGeoCount | |
| ) | |
| { | |
| Base::TimeElapsed start_time; | |
| clear(); | |
| std::vector<Part::Geometry*> intGeoList, extGeoList; | |
| std::copy(GeoList.begin(), GeoList.end() - extGeoCount, std::back_inserter(intGeoList)); | |
| std::copy(GeoList.end() - extGeoCount, GeoList.end(), std::back_inserter(extGeoList)); | |
| // these geometries are blocked, frozen and sent as fixed parameters to the solver | |
| std::vector<bool> onlyBlockedGeometry(intGeoList.size(), false); | |
| // these constraints are unenforceable due to a Blocked constraint | |
| std::vector<bool> unenforceableConstraints(ConstraintList.size(), false); | |
| /* This implements the old block constraint. I have decided not to remove it at this time while | |
| * the new is tested, just in case the change needs to be reverted */ | |
| /*if(!intGeoList.empty()) | |
| getBlockedGeometry(blockedGeometry, unenforceableConstraints, ConstraintList);*/ | |
| // Pre-analysis of blocked geometry (new block constraint) to fix geometry only affected by a | |
| // block constraint (see comment in Sketch.h) | |
| std::vector<int> blockedGeoIds; | |
| bool doesBlockAffectOtherConstraints | |
| = analyseBlockedGeometry(intGeoList, ConstraintList, onlyBlockedGeometry, blockedGeoIds); | |
| if (doesBlockAffectOtherConstraints) { | |
| Base::Console().log("\n Block interferes with other constraints: Post-analysis required"); | |
| } | |
| Base::Console().log("\nOnlyBlocked GeoIds:"); | |
| size_t i = 0; | |
| bool found = false; | |
| for (; i < onlyBlockedGeometry.size(); i++) { | |
| if (onlyBlockedGeometry[i]) { | |
| Base::Console().log("\n GeoId=%d", i); | |
| found = true; | |
| } | |
| } | |
| if (found) { | |
| Base::Console().log("\n None"); | |
| } | |
| Base::Console().log("\nNotOnlyBlocked GeoIds:"); | |
| i = 0; | |
| for (; i < blockedGeoIds.size(); i++) { | |
| Base::Console().log("\n GeoId=%d", blockedGeoIds[i]); | |
| } | |
| if (i == 0) { | |
| Base::Console().log("\n None"); | |
| } | |
| Base::Console().log("\n"); | |
| buildInternalAlignmentGeometryMap(ConstraintList); | |
| addGeometry(intGeoList, onlyBlockedGeometry); | |
| int extStart = Geoms.size(); | |
| addGeometry(extGeoList, true); | |
| int extEnd = Geoms.size() - 1; | |
| for (int i = extStart; i <= extEnd; i++) { | |
| Geoms[i].external = true; | |
| } | |
| // The Geoms list might be empty after an undo/redo | |
| if (!Geoms.empty()) { | |
| addConstraints(ConstraintList, unenforceableConstraints); | |
| } | |
| clearTemporaryConstraints(); | |
| GCSsys.declareUnknowns(Parameters); | |
| GCSsys.declareDrivenParams(DrivenParameters); | |
| GCSsys.initSolution(defaultSolverRedundant); | |
| // Post-analysis | |
| // Now that we have all the parameters information, we deal properly with the block constraints | |
| // if necessary | |
| if (doesBlockAffectOtherConstraints) { | |
| std::vector<double*> params_to_block; | |
| bool unsatisfied_groups | |
| = analyseBlockedConstraintDependentParameters(blockedGeoIds, params_to_block); | |
| // I am unsure if more than one QR iterations are needed with the current implementation. | |
| // | |
| // With previous implementations mostly one QR iteration was enough, but if block constraint | |
| // is abused, more iterations were needed. | |
| int index = 0; | |
| while (unsatisfied_groups) { | |
| // We tried hard not to arrive to an unsatisfied group, so we try harder | |
| // This loop has the advantage that the user will notice increased effort to solve, | |
| // so they may understand that they are abusing the block constraint, while guaranteeing | |
| // that wrong behaviour of the block constraint is not undetected. | |
| // Another QR iteration | |
| fixParametersAndDiagnose(params_to_block); | |
| unsatisfied_groups | |
| = analyseBlockedConstraintDependentParameters(blockedGeoIds, params_to_block); | |
| if (debugMode == GCS::IterationLevel) { | |
| Base::Console().log("Sketcher::setUpSketch()-BlockConstraint-PostAnalysis:%d\n", index); | |
| } | |
| index++; | |
| } | |
| // 2. If something needs blocking, block-it | |
| fixParametersAndDiagnose(params_to_block); | |
| if (params_to_block.size() > 0) { | |
| std::vector<std::vector<double*>> groups; | |
| GCSsys.getDependentParamsGroups(groups); | |
| // Debug code block | |
| for (size_t i = 0; i < groups.size(); i++) { | |
| Base::Console().log("\nDepParams: Group %d:", i); | |
| for (size_t j = 0; j < groups[i].size(); j++) { | |
| Base::Console().log( | |
| "\n Param=%x ,GeoId=%d, GeoPos=%d", | |
| param2geoelement.find(*std::next(groups[i].begin(), j))->first, | |
| param2geoelement.find(*std::next(groups[i].begin(), j))->second.first, | |
| param2geoelement.find(*std::next(groups[i].begin(), j))->second.second | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| // Now we set the Sketch status with the latest solver information | |
| GCSsys.getConflicting(Conflicting); | |
| GCSsys.getRedundant(Redundant); | |
| GCSsys.getPartiallyRedundant(PartiallyRedundant); | |
| GCSsys.getDependentParams(pDependentParametersList); | |
| calculateDependentParametersElements(); | |
| if (debugMode == GCS::Minimal || debugMode == GCS::IterationLevel) { | |
| Base::TimeElapsed end_time; | |
| Base::Console().log( | |
| "Sketcher::setUpSketch()-T:%s\n", | |
| Base::TimeElapsed::diffTime(start_time, end_time).c_str() | |
| ); | |
| } | |
| return GCSsys.dofsNumber(); | |
| } | |
| void Sketch::buildInternalAlignmentGeometryMap(const std::vector<Constraint*>& constraintList) | |
| { | |
| for (auto* c : constraintList) { | |
| if (c->Type == InternalAlignment) { | |
| internalAlignmentGeometryMap[c->First] = c->Second; | |
| } | |
| } | |
| } | |
| void Sketch::fixParametersAndDiagnose(std::vector<double*>& params_to_block) | |
| { | |
| if (!params_to_block.empty()) { // only there are parameters to fix | |
| for (auto p : params_to_block) { | |
| if (auto findparam = std::ranges::find(Parameters, p); findparam != Parameters.end()) { | |
| FixParameters.push_back(*findparam); | |
| Parameters.erase(findparam); | |
| } | |
| } | |
| pDependencyGroups.clear(); | |
| clearTemporaryConstraints(); | |
| GCSsys.invalidatedDiagnosis(); | |
| GCSsys.declareUnknowns(Parameters); | |
| GCSsys.declareDrivenParams(DrivenParameters); | |
| GCSsys.initSolution(defaultSolverRedundant); | |
| /*GCSsys.getConflicting(Conflicting); | |
| GCSsys.getRedundant(Redundant); | |
| GCSsys.getPartlyRedundant(PartiallyRedundant); | |
| GCSsys.getDependentParams(pDependentParametersList); | |
| calculateDependentParametersElements();*/ | |
| } | |
| } | |
| bool Sketch::analyseBlockedConstraintDependentParameters( | |
| std::vector<int>& blockedGeoIds, | |
| std::vector<double*>& params_to_block | |
| ) const | |
| { | |
| // 1. Retrieve solver information | |
| std::vector<std::vector<double*>> groups; | |
| GCSsys.getDependentParamsGroups(groups); | |
| // 2. Determine blockable parameters for each group (see documentation in header file). | |
| struct group | |
| { | |
| std::vector<double*> blockable_params_in_group; | |
| double* blocking_param_in_group = nullptr; | |
| }; | |
| std::vector<group> prop_groups(groups.size()); | |
| for (size_t i = 0; i < groups.size(); i++) { | |
| Base::Console().log("\nDepParams: Group %d:", i); | |
| for (size_t j = 0; j < groups[i].size(); j++) { | |
| Base::Console().log( | |
| "\n Param=%x ,GeoId=%d, GeoPos=%d", | |
| param2geoelement.find(*std::next(groups[i].begin(), j))->first, | |
| param2geoelement.find(*std::next(groups[i].begin(), j))->second.first, | |
| param2geoelement.find(*std::next(groups[i].begin(), j))->second.second | |
| ); | |
| } | |
| } | |
| for (size_t i = 0; i < groups.size(); i++) { | |
| for (size_t j = 0; j < groups[i].size(); j++) { | |
| double* thisparam = *std::next(groups[i].begin(), j); | |
| if (auto element = param2geoelement.find(thisparam); element != param2geoelement.end()) { | |
| if (auto blockable = std::ranges::find(blockedGeoIds, std::get<0>(element->second)); | |
| blockable != blockedGeoIds.end()) { | |
| // This dependent parameter group contains at least one parameter that should be | |
| // blocked, so added to the blockable list. | |
| prop_groups[i].blockable_params_in_group.push_back(thisparam); | |
| } | |
| } | |
| } | |
| } | |
| // 3. Apply heuristic - pick the last blockable param available to block the group, starting | |
| // from the last group | |
| for (size_t i = prop_groups.size(); i-- > 0;) { | |
| for (size_t j = prop_groups[i].blockable_params_in_group.size(); j-- > 0;) { | |
| // check if parameter is already satisfying one group | |
| double* thisparam = prop_groups[i].blockable_params_in_group[j]; | |
| auto pos = std::ranges::find(params_to_block, thisparam); | |
| if (pos == params_to_block.end()) { // not found, so add | |
| params_to_block.push_back(thisparam); | |
| prop_groups[i].blocking_param_in_group = thisparam; | |
| Base::Console().log("\nTentatively blocking group %d, with param=%x", i, thisparam); | |
| break; | |
| } | |
| } | |
| } | |
| // 4. Check if groups are satisfied or are licitly unsatisfiable and thus deemed as satisfied | |
| bool unsatisfied_groups = false; | |
| for (auto& prop_group : prop_groups) { | |
| // 4.1. unsatisfiable group | |
| if (prop_group.blockable_params_in_group.empty()) { | |
| // this group does not contain any blockable parameter, so it is by definition satisfied | |
| // (or impossible to satisfy by block constraints) | |
| continue; | |
| } | |
| // 4.2. satisfiable and not satisfied | |
| if (!prop_group.blocking_param_in_group) { | |
| unsatisfied_groups = true; | |
| } | |
| } | |
| return unsatisfied_groups; | |
| } | |
| void Sketch::clearTemporaryConstraints() | |
| { | |
| GCSsys.clearByTag(GCS::DefaultTemporaryConstraint); | |
| } | |
| void Sketch::calculateDependentParametersElements() | |
| { | |
| // initialize solve extensions to a know state | |
| solverExtensions.resize(Geoms.size()); | |
| int i = 0; | |
| for (auto geo : Geoms) { | |
| if (!geo.geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) { | |
| geo.geo->setExtension(std::make_unique<Sketcher::SolverGeometryExtension>()); | |
| } | |
| auto solvext = std::static_pointer_cast<Sketcher::SolverGeometryExtension>( | |
| geo.geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock() | |
| ); | |
| if (GCSsys.isEmptyDiagnoseMatrix()) { | |
| solvext->init(SolverGeometryExtension::Dependent); | |
| } | |
| else { | |
| solvext->init(SolverGeometryExtension::Independent); | |
| } | |
| solverExtensions[i] = solvext; | |
| i++; | |
| } | |
| for (auto param : pDependentParametersList) { | |
| // auto element = param2geoelement.at(param); | |
| auto element = param2geoelement.find(param); | |
| if (element != param2geoelement.end()) { | |
| auto geoid = std::get<0>(element->second); | |
| auto geopos = std::get<1>(element->second); | |
| auto solvext = std::static_pointer_cast<Sketcher::SolverGeometryExtension>( | |
| Geoms[geoid].geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock() | |
| ); | |
| auto index = std::get<2>(element->second); | |
| switch (geopos) { | |
| case PointPos::none: | |
| solvext->setEdge(index, SolverGeometryExtension::Dependent); | |
| break; | |
| case PointPos::start: | |
| if (index == 0) { | |
| solvext->setStartx(SolverGeometryExtension::Dependent); | |
| } | |
| else { | |
| solvext->setStarty(SolverGeometryExtension::Dependent); | |
| } | |
| break; | |
| case PointPos::end: | |
| if (index == 0) { | |
| solvext->setEndx(SolverGeometryExtension::Dependent); | |
| } | |
| else { | |
| solvext->setEndy(SolverGeometryExtension::Dependent); | |
| } | |
| break; | |
| case PointPos::mid: | |
| if (index == 0) { | |
| solvext->setMidx(SolverGeometryExtension::Dependent); | |
| } | |
| else { | |
| solvext->setMidy(SolverGeometryExtension::Dependent); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| std::vector<std::vector<double*>> groups; | |
| GCSsys.getDependentParamsGroups(groups); | |
| pDependencyGroups.resize(groups.size()); | |
| // translate parameters into elements (Geoid, PointPos) | |
| for (size_t i = 0; i < groups.size(); i++) { | |
| for (size_t j = 0; j < groups[i].size(); j++) { | |
| auto element = param2geoelement.find(groups[i][j]); | |
| if (element != param2geoelement.end()) { | |
| pDependencyGroups[i].insert( | |
| std::pair(std::get<0>(element->second), std::get<1>(element->second)) | |
| ); | |
| } | |
| } | |
| } | |
| // check if groups have a common element, if yes merge the groups | |
| auto havecommonelement = [](std::set<std::pair<int, Sketcher::PointPos>>::iterator begin1, | |
| std::set<std::pair<int, Sketcher::PointPos>>::iterator end1, | |
| std::set<std::pair<int, Sketcher::PointPos>>::iterator begin2, | |
| std::set<std::pair<int, Sketcher::PointPos>>::iterator end2) { | |
| while (begin1 != end1 && begin2 != end2) { | |
| if (*begin1 < *begin2) { | |
| ++begin1; | |
| } | |
| else if (*begin2 < *begin1) { | |
| ++begin2; | |
| } | |
| else { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }; | |
| if (pDependencyGroups.size() > 1) { // only if there is more than 1 group | |
| size_t endcount = pDependencyGroups.size() - 1; | |
| for (size_t i = 0; i < endcount; i++) { | |
| if (havecommonelement( | |
| pDependencyGroups[i].begin(), | |
| pDependencyGroups[i].end(), | |
| pDependencyGroups[i + 1].begin(), | |
| pDependencyGroups[i + 1].end() | |
| )) { | |
| pDependencyGroups[i].insert( | |
| pDependencyGroups[i + 1].begin(), | |
| pDependencyGroups[i + 1].end() | |
| ); | |
| pDependencyGroups.erase(pDependencyGroups.begin() + i + 1); | |
| endcount--; | |
| } | |
| } | |
| } | |
| } | |
| std::set<std::pair<int, Sketcher::PointPos>> Sketch::getDependencyGroup(int geoId, PointPos pos) const | |
| { | |
| geoId = checkGeoId(geoId); | |
| std::set<std::pair<int, Sketcher::PointPos>> group; | |
| auto key = std::make_pair(geoId, pos); | |
| for (auto& set : pDependencyGroups) { | |
| if (set.find(key) != set.end()) { | |
| group = set; | |
| break; | |
| } | |
| } | |
| return group; | |
| } | |
| std::shared_ptr<SolverGeometryExtension> Sketch::getSolverExtension(int geoId) const | |
| { | |
| if (geoId >= 0 && geoId < int(solverExtensions.size())) { | |
| return solverExtensions[geoId]; | |
| } | |
| return nullptr; | |
| } | |
| int Sketch::resetSolver() | |
| { | |
| clearTemporaryConstraints(); | |
| GCSsys.declareUnknowns(Parameters); | |
| GCSsys.declareDrivenParams(DrivenParameters); | |
| GCSsys.initSolution(defaultSolverRedundant); | |
| GCSsys.getConflicting(Conflicting); | |
| GCSsys.getRedundant(Redundant); | |
| GCSsys.getPartiallyRedundant(PartiallyRedundant); | |
| GCSsys.getDependentParams(pDependentParametersList); | |
| calculateDependentParametersElements(); | |
| return GCSsys.dofsNumber(); | |
| } | |
| const char* nameByType(Sketch::GeoType type) | |
| { | |
| switch (type) { | |
| case Sketch::Point: | |
| return "point"; | |
| case Sketch::Line: | |
| return "line"; | |
| case Sketch::Arc: | |
| return "arc"; | |
| case Sketch::Circle: | |
| return "circle"; | |
| case Sketch::Ellipse: | |
| return "ellipse"; | |
| case Sketch::ArcOfEllipse: | |
| return "arcofellipse"; | |
| case Sketch::ArcOfHyperbola: | |
| return "arcofhyperbola"; | |
| case Sketch::ArcOfParabola: | |
| return "arcofparabola"; | |
| case Sketch::BSpline: | |
| return "bspline"; | |
| case Sketch::None: | |
| default: | |
| return "unknown"; | |
| } | |
| } | |
| // Geometry adding ========================================================== | |
| int Sketch::addGeometry(const Part::Geometry* geo, bool fixed) | |
| { | |
| if (geo->is<GeomPoint>()) { // add a point | |
| const GeomPoint* point = static_cast<const GeomPoint*>(geo); | |
| auto pointf = GeometryFacade::getFacade(point); | |
| // create the definition struct for that geom | |
| return addPoint(*point, fixed); | |
| } | |
| else if (geo->is<GeomLineSegment>()) { // add a line | |
| const GeomLineSegment* lineSeg = static_cast<const GeomLineSegment*>(geo); | |
| // create the definition struct for that geom | |
| return addLineSegment(*lineSeg, fixed); | |
| } | |
| else if (geo->is<GeomCircle>()) { // add a circle | |
| const GeomCircle* circle = static_cast<const GeomCircle*>(geo); | |
| // create the definition struct for that geom | |
| return addCircle(*circle, fixed); | |
| } | |
| else if (geo->is<GeomEllipse>()) { // add a ellipse | |
| const GeomEllipse* ellipse = static_cast<const GeomEllipse*>(geo); | |
| // create the definition struct for that geom | |
| return addEllipse(*ellipse, fixed); | |
| } | |
| else if (geo->is<GeomArcOfCircle>()) { // add an arc | |
| const GeomArcOfCircle* aoc = static_cast<const GeomArcOfCircle*>(geo); | |
| // create the definition struct for that geom | |
| return addArc(*aoc, fixed); | |
| } | |
| else if (geo->is<GeomArcOfEllipse>()) { // add an arc | |
| const GeomArcOfEllipse* aoe = static_cast<const GeomArcOfEllipse*>(geo); | |
| // create the definition struct for that geom | |
| return addArcOfEllipse(*aoe, fixed); | |
| } | |
| else if (geo->is<GeomArcOfHyperbola>()) { // add an arc of hyperbola | |
| const GeomArcOfHyperbola* aoh = static_cast<const GeomArcOfHyperbola*>(geo); | |
| // create the definition struct for that geom | |
| return addArcOfHyperbola(*aoh, fixed); | |
| } | |
| else if (geo->is<GeomArcOfParabola>()) { // add an arc of parabola | |
| const GeomArcOfParabola* aop = static_cast<const GeomArcOfParabola*>(geo); | |
| // create the definition struct for that geom | |
| return addArcOfParabola(*aop, fixed); | |
| } | |
| else if (geo->is<GeomBSplineCurve>()) { // add a bspline | |
| const GeomBSplineCurve* bsp = static_cast<const GeomBSplineCurve*>(geo); | |
| // Current B-Spline implementation relies on OCCT calculations, so a second solve | |
| // is necessary to update actual solver implementation to account for changes in B-Spline | |
| // geometry | |
| resolveAfterGeometryUpdated = true; | |
| return addBSpline(*bsp, fixed); | |
| } | |
| else { | |
| throw Base::TypeError("Sketch::addGeometry(): Unknown or unsupported type added to a sketch"); | |
| } | |
| } | |
| int Sketch::addGeometry(const std::vector<Part::Geometry*>& geos, bool fixed) | |
| { | |
| int ret = -1; | |
| for (const auto& geo : geos) { | |
| ret = addGeometry(geo, fixed); | |
| } | |
| return ret; | |
| } | |
| int Sketch::addGeometry(const std::vector<Part::Geometry*>& geos, const std::vector<bool>& blockedGeometry) | |
| { | |
| assert(geos.size() == blockedGeometry.size()); | |
| int ret = -1; | |
| std::vector<Part::Geometry*>::const_iterator it; | |
| std::vector<bool>::const_iterator bit; | |
| for (it = geos.begin(), bit = blockedGeometry.begin(); | |
| it != geos.end() && bit != blockedGeometry.end(); | |
| ++it, ++bit) { | |
| ret = addGeometry(*it, *bit); | |
| } | |
| return ret; | |
| } | |
| int Sketch::addPoint(const Part::GeomPoint& point, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomPoint* p = static_cast<GeomPoint*>(point.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = p; | |
| def.type = Point; | |
| // set the parameter for the solver | |
| params.push_back(new double(p->getPoint().x)); | |
| params.push_back(new double(p->getPoint().y)); | |
| // set the points for later constraints | |
| GCS::Point p1; | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| def.startPointId = Points.size(); | |
| def.endPointId = Points.size(); | |
| def.midPointId = Points.size(); | |
| Points.push_back(p1); | |
| // store complete set | |
| Geoms.push_back(def); | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addLine(const Part::GeomLineSegment& /*line*/, bool /*fixed*/) | |
| { | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addLineSegment(const Part::GeomLineSegment& lineSegment, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomLineSegment* lineSeg = static_cast<GeomLineSegment*>(lineSegment.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = lineSeg; | |
| def.type = Line; | |
| // get the points from the line | |
| Base::Vector3d start = lineSeg->getStartPoint(); | |
| Base::Vector3d end = lineSeg->getEndPoint(); | |
| // the points for later constraints | |
| GCS::Point p1, p2; | |
| params.push_back(new double(start.x)); | |
| params.push_back(new double(start.y)); | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| params.push_back(new double(end.x)); | |
| params.push_back(new double(end.y)); | |
| p2.x = params[params.size() - 2]; | |
| p2.y = params[params.size() - 1]; | |
| // add the points | |
| def.startPointId = Points.size(); | |
| def.endPointId = Points.size() + 1; | |
| Points.push_back(p1); | |
| Points.push_back(p2); | |
| // set the line for later constraints | |
| GCS::Line l; | |
| l.p1 = p1; | |
| l.p2 = p2; | |
| def.index = Lines.size(); | |
| Lines.push_back(l); | |
| // store complete set | |
| Geoms.push_back(def); | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 1) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addArc(const Part::GeomArcOfCircle& circleSegment, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomArcOfCircle* aoc = static_cast<GeomArcOfCircle*>(circleSegment.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = aoc; | |
| def.type = Arc; | |
| Base::Vector3d center = aoc->getCenter(); | |
| Base::Vector3d startPnt = aoc->getStartPoint(/*emulateCCW=*/true); | |
| Base::Vector3d endPnt = aoc->getEndPoint(/*emulateCCW=*/true); | |
| double radius = aoc->getRadius(); | |
| double startAngle, endAngle; | |
| aoc->getRange(startAngle, endAngle, /*emulateCCW=*/true); | |
| GCS::Point p1, p2, p3; | |
| params.push_back(new double(startPnt.x)); | |
| params.push_back(new double(startPnt.y)); | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| params.push_back(new double(endPnt.x)); | |
| params.push_back(new double(endPnt.y)); | |
| p2.x = params[params.size() - 2]; | |
| p2.y = params[params.size() - 1]; | |
| params.push_back(new double(center.x)); | |
| params.push_back(new double(center.y)); | |
| p3.x = params[params.size() - 2]; | |
| p3.y = params[params.size() - 1]; | |
| def.startPointId = Points.size(); | |
| Points.push_back(p1); | |
| def.endPointId = Points.size(); | |
| Points.push_back(p2); | |
| def.midPointId = Points.size(); | |
| Points.push_back(p3); | |
| params.push_back(new double(radius)); | |
| double* r = params[params.size() - 1]; | |
| params.push_back(new double(startAngle)); | |
| double* a1 = params[params.size() - 1]; | |
| params.push_back(new double(endAngle)); | |
| double* a2 = params[params.size() - 1]; | |
| // set the arc for later constraints | |
| GCS::Arc a; | |
| a.start = p1; | |
| a.end = p2; | |
| a.center = p3; | |
| a.rad = r; | |
| a.startAngle = a1; | |
| a.endAngle = a2; | |
| def.index = Arcs.size(); | |
| Arcs.push_back(a); | |
| // store complete set | |
| Geoms.push_back(def); | |
| // arcs require an ArcRules constraint for the end points | |
| if (!fixed) { | |
| GCSsys.addConstraintArcRules(a); | |
| } | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(r), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a1), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a2), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 2) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addArcOfEllipse(const Part::GeomArcOfEllipse& ellipseSegment, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomArcOfEllipse* aoe = static_cast<GeomArcOfEllipse*>(ellipseSegment.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = aoe; | |
| def.type = ArcOfEllipse; | |
| Base::Vector3d center = aoe->getCenter(); | |
| Base::Vector3d startPnt = aoe->getStartPoint(/*emulateCCW=*/true); | |
| Base::Vector3d endPnt = aoe->getEndPoint(/*emulateCCW=*/true); | |
| double radmaj = aoe->getMajorRadius(); | |
| double radmin = aoe->getMinorRadius(); | |
| Base::Vector3d radmajdir = aoe->getMajorAxisDir(); | |
| double dist_C_F = sqrt(radmaj * radmaj - radmin * radmin); | |
| // solver parameters | |
| Base::Vector3d focus1 = center + dist_C_F * radmajdir; | |
| double startAngle, endAngle; | |
| aoe->getRange(startAngle, endAngle, /*emulateCCW=*/true); | |
| GCS::Point p1, p2, p3; | |
| params.push_back(new double(startPnt.x)); | |
| params.push_back(new double(startPnt.y)); | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| params.push_back(new double(endPnt.x)); | |
| params.push_back(new double(endPnt.y)); | |
| p2.x = params[params.size() - 2]; | |
| p2.y = params[params.size() - 1]; | |
| params.push_back(new double(center.x)); | |
| params.push_back(new double(center.y)); | |
| p3.x = params[params.size() - 2]; | |
| p3.y = params[params.size() - 1]; | |
| params.push_back(new double(focus1.x)); | |
| params.push_back(new double(focus1.y)); | |
| double* f1X = params[params.size() - 2]; | |
| double* f1Y = params[params.size() - 1]; | |
| def.startPointId = Points.size(); | |
| Points.push_back(p1); | |
| def.endPointId = Points.size(); | |
| Points.push_back(p2); | |
| def.midPointId = Points.size(); | |
| Points.push_back(p3); | |
| // Points.push_back(f1); | |
| // add the radius parameters | |
| params.push_back(new double(radmin)); | |
| double* rmin = params[params.size() - 1]; | |
| params.push_back(new double(startAngle)); | |
| double* a1 = params[params.size() - 1]; | |
| params.push_back(new double(endAngle)); | |
| double* a2 = params[params.size() - 1]; | |
| // set the arc for later constraints | |
| GCS::ArcOfEllipse a; | |
| a.start = p1; | |
| a.end = p2; | |
| a.center = p3; | |
| a.focus1.x = f1X; | |
| a.focus1.y = f1Y; | |
| a.radmin = rmin; | |
| a.startAngle = a1; | |
| a.endAngle = a2; | |
| def.index = ArcsOfEllipse.size(); | |
| ArcsOfEllipse.push_back(a); | |
| // store complete set | |
| Geoms.push_back(def); | |
| // arcs require an ArcRules constraint for the end points | |
| if (!fixed) { | |
| GCSsys.addConstraintArcOfEllipseRules(a); | |
| } | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(f1X), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(f1Y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(rmin), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 2) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a1), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 3) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a2), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 4) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addArcOfHyperbola(const Part::GeomArcOfHyperbola& hyperbolaSegment, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomArcOfHyperbola* aoh = static_cast<GeomArcOfHyperbola*>(hyperbolaSegment.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = aoh; | |
| def.type = ArcOfHyperbola; | |
| Base::Vector3d center = aoh->getCenter(); | |
| Base::Vector3d startPnt = aoh->getStartPoint(); | |
| Base::Vector3d endPnt = aoh->getEndPoint(); | |
| double radmaj = aoh->getMajorRadius(); | |
| double radmin = aoh->getMinorRadius(); | |
| Base::Vector3d radmajdir = aoh->getMajorAxisDir(); | |
| double dist_C_F = sqrt(radmaj * radmaj + radmin * radmin); | |
| // solver parameters | |
| Base::Vector3d focus1 = center + dist_C_F * radmajdir; //+x | |
| double startAngle, endAngle; | |
| aoh->getRange(startAngle, endAngle, /*emulateCCW=*/true); | |
| GCS::Point p1, p2, p3; | |
| params.push_back(new double(startPnt.x)); | |
| params.push_back(new double(startPnt.y)); | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| params.push_back(new double(endPnt.x)); | |
| params.push_back(new double(endPnt.y)); | |
| p2.x = params[params.size() - 2]; | |
| p2.y = params[params.size() - 1]; | |
| params.push_back(new double(center.x)); | |
| params.push_back(new double(center.y)); | |
| p3.x = params[params.size() - 2]; | |
| p3.y = params[params.size() - 1]; | |
| params.push_back(new double(focus1.x)); | |
| params.push_back(new double(focus1.y)); | |
| double* f1X = params[params.size() - 2]; | |
| double* f1Y = params[params.size() - 1]; | |
| def.startPointId = Points.size(); | |
| Points.push_back(p1); | |
| def.endPointId = Points.size(); | |
| Points.push_back(p2); | |
| def.midPointId = Points.size(); | |
| Points.push_back(p3); | |
| // add the radius parameters | |
| params.push_back(new double(radmin)); | |
| double* rmin = params[params.size() - 1]; | |
| params.push_back(new double(startAngle)); | |
| double* a1 = params[params.size() - 1]; | |
| params.push_back(new double(endAngle)); | |
| double* a2 = params[params.size() - 1]; | |
| // set the arc for later constraints | |
| GCS::ArcOfHyperbola a; | |
| a.start = p1; | |
| a.end = p2; | |
| a.center = p3; | |
| a.focus1.x = f1X; | |
| a.focus1.y = f1Y; | |
| a.radmin = rmin; | |
| a.startAngle = a1; | |
| a.endAngle = a2; | |
| def.index = ArcsOfHyperbola.size(); | |
| ArcsOfHyperbola.push_back(a); | |
| // store complete set | |
| Geoms.push_back(def); | |
| // arcs require an ArcRules constraint for the end points | |
| if (!fixed) { | |
| GCSsys.addConstraintArcOfHyperbolaRules(a); | |
| } | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(f1X), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(f1Y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(rmin), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 2) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a1), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 3) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a2), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 4) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addArcOfParabola(const Part::GeomArcOfParabola& parabolaSegment, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomArcOfParabola* aop = static_cast<GeomArcOfParabola*>(parabolaSegment.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = aop; | |
| def.type = ArcOfParabola; | |
| Base::Vector3d vertex = aop->getCenter(); | |
| Base::Vector3d startPnt = aop->getStartPoint(); | |
| Base::Vector3d endPnt = aop->getEndPoint(); | |
| Base::Vector3d focus = aop->getFocus(); | |
| double startAngle, endAngle; | |
| aop->getRange(startAngle, endAngle, /*emulateCCW=*/true); | |
| GCS::Point p1, p2, p3, p4; | |
| params.push_back(new double(startPnt.x)); | |
| params.push_back(new double(startPnt.y)); | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| params.push_back(new double(endPnt.x)); | |
| params.push_back(new double(endPnt.y)); | |
| p2.x = params[params.size() - 2]; | |
| p2.y = params[params.size() - 1]; | |
| params.push_back(new double(vertex.x)); | |
| params.push_back(new double(vertex.y)); | |
| p3.x = params[params.size() - 2]; | |
| p3.y = params[params.size() - 1]; | |
| params.push_back(new double(focus.x)); | |
| params.push_back(new double(focus.y)); | |
| p4.x = params[params.size() - 2]; | |
| p4.y = params[params.size() - 1]; | |
| def.startPointId = Points.size(); | |
| Points.push_back(p1); | |
| def.endPointId = Points.size(); | |
| Points.push_back(p2); | |
| def.midPointId = Points.size(); | |
| Points.push_back(p3); | |
| // add the radius parameters | |
| params.push_back(new double(startAngle)); | |
| double* a1 = params[params.size() - 1]; | |
| params.push_back(new double(endAngle)); | |
| double* a2 = params[params.size() - 1]; | |
| // set the arc for later constraints | |
| GCS::ArcOfParabola a; | |
| a.start = p1; | |
| a.end = p2; | |
| a.vertex = p3; | |
| a.focus1 = p4; | |
| a.startAngle = a1; | |
| a.endAngle = a2; | |
| def.index = ArcsOfParabola.size(); | |
| ArcsOfParabola.push_back(a); | |
| // store complete set | |
| Geoms.push_back(def); | |
| // arcs require an ArcRules constraint for the end points | |
| if (!fixed) { | |
| GCSsys.addConstraintArcOfParabolaRules(a); | |
| } | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p3.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p4.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p4.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a1), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 2) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(a2), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 3) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addBSpline(const Part::GeomBSplineCurve& bspline, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomBSplineCurve* bsp = static_cast<GeomBSplineCurve*>(bspline.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = bsp; | |
| def.type = BSpline; | |
| std::vector<Base::Vector3d> poles = bsp->getPoles(); | |
| std::vector<double> weights = bsp->getWeights(); | |
| std::vector<double> knots = bsp->getKnots(); | |
| std::vector<int> mult = bsp->getMultiplicities(); | |
| int degree = bsp->getDegree(); | |
| bool periodic = bsp->isPeriodic(); | |
| // OCC hack | |
| // c means there is a constraint on that weight, nc no constraint | |
| // OCC provides normalized weights when polynomic [1 1 1] [c c c] and unnormalized weights when | |
| // rational [5 1 5] [c nc c] then when changing from polynomic to rational, after the first | |
| // solve any not-constrained pole circle gets normalized to 1. This only happens when changing | |
| // from polynomic to rational, any subsequent change remains unnormalized [5 1 5] [c nc nc] This | |
| // creates a visual problem that one of the poles shrinks to 1 mm when deleting an equality | |
| // constraint. | |
| int lastoneindex = -1; | |
| int countones = 0; | |
| double lastnotone = 1.0; | |
| for (size_t i = 0; i < weights.size(); i++) { | |
| if (weights[i] != 1.0) { | |
| lastnotone = weights[i]; | |
| } | |
| else { // is 1.0 | |
| lastoneindex = i; | |
| countones++; | |
| } | |
| } | |
| if (countones == 1) { | |
| weights[lastoneindex] = (lastnotone * 0.99); | |
| } | |
| // end hack | |
| Base::Vector3d startPnt = bsp->getStartPoint(); | |
| Base::Vector3d endPnt = bsp->getEndPoint(); | |
| std::vector<GCS::Point> spoles; | |
| int i = 0; | |
| for (const auto& pole : poles) { | |
| params.push_back(new double(pole.x)); | |
| params.push_back(new double(pole.y)); | |
| GCS::Point p; | |
| p.x = params[params.size() - 2]; | |
| p.y = params[params.size() - 1]; | |
| spoles.push_back(p); | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p.x), | |
| std::forward_as_tuple(Geoms.size(), Sketcher::PointPos::none, i++) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p.y), | |
| std::forward_as_tuple(Geoms.size(), Sketcher::PointPos::none, i++) | |
| ); | |
| } | |
| } | |
| std::vector<double*> sweights; | |
| for (const auto& weight : weights) { | |
| auto r = new double(weight); | |
| params.push_back(r); | |
| sweights.push_back(params[params.size() - 1]); | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(r), | |
| std::forward_as_tuple(Geoms.size(), Sketcher::PointPos::none, i++) | |
| ); | |
| } | |
| } | |
| std::vector<double*> sknots; | |
| for (const auto& knot : knots) { | |
| double* _knot = new double(knot); | |
| // params.push_back(knot); | |
| sknots.push_back(_knot); | |
| } | |
| GCS::Point p1, p2; | |
| double* p1x = new double(startPnt.x); | |
| double* p1y = new double(startPnt.y); | |
| // If periodic, startpoint and endpoint do not play a role in the solver, this can remove | |
| // unnecessary DoF of determining where in the curve the start and the stop should be. However, | |
| // since start and end points are placed above knots, removing them leads to that knot being | |
| // unusable. | |
| params.push_back(p1x); | |
| params.push_back(p1y); | |
| p1.x = p1x; | |
| p1.y = p1y; | |
| double* p2x = new double(endPnt.x); | |
| double* p2y = new double(endPnt.y); | |
| // If periodic, startpoint and endpoint do not play a role in the solver, this can remove | |
| // unnecessary DoF of determining where in the curve the start and the stop should be. However, | |
| // since start and end points are placed above knots, removing them leads to that knot being | |
| // unusable. | |
| params.push_back(p2x); | |
| params.push_back(p2y); | |
| p2.x = p2x; | |
| p2.y = p2y; | |
| def.startPointId = Points.size(); | |
| Points.push_back(p1); | |
| def.endPointId = Points.size(); | |
| Points.push_back(p2); | |
| GCS::BSpline bs; | |
| bs.start = p1; | |
| bs.end = p2; | |
| bs.poles = spoles; | |
| bs.weights = sweights; | |
| bs.knots = sknots; | |
| bs.mult = mult; | |
| bs.degree = degree; | |
| bs.periodic = periodic; | |
| def.index = BSplines.size(); | |
| // non-solver related, just to enable initialization of knotspoints which is not a parameter of | |
| // the solver | |
| bs.knotpointGeoids.resize(knots.size()); | |
| for (auto& kpGeoId : bs.knotpointGeoids) { | |
| kpGeoId = GeoEnum::GeoUndef; | |
| } | |
| BSplines.push_back(bs); | |
| // store complete set | |
| Geoms.push_back(def); | |
| // WARNING: This is only valid where the multiplicity of the endpoints conforms with a BSpline | |
| // only then the startpoint is the first control point and the endpoint is the last control | |
| // point accordingly, it is never the case for a periodic BSpline. NOTE: For an external | |
| // B-spline (i.e. fixed=true) we must not set the coincident constraints as the points are not | |
| // movable anyway. See #issue 0003176: Sketcher: always over-constrained when referencing | |
| // external B-Spline | |
| if (!fixed && !bs.periodic) { | |
| if (bs.mult[0] > bs.degree) { | |
| GCSsys.addConstraintP2PCoincident(*(bs.poles.begin()), bs.start); | |
| } | |
| if (bs.mult[mult.size() - 1] > bs.degree) { | |
| GCSsys.addConstraintP2PCoincident(*(bs.poles.end() - 1), bs.end); | |
| } | |
| } | |
| if (!fixed) { | |
| // Note: Poles and weight parameters are emplaced above | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::start, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p2.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::end, 1) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addCircle(const Part::GeomCircle& cir, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomCircle* circ = static_cast<GeomCircle*>(cir.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = circ; | |
| def.type = Circle; | |
| Base::Vector3d center = circ->getCenter(); | |
| double radius = circ->getRadius(); | |
| GCS::Point p1; | |
| params.push_back(new double(center.x)); | |
| params.push_back(new double(center.y)); | |
| p1.x = params[params.size() - 2]; | |
| p1.y = params[params.size() - 1]; | |
| params.push_back(new double(radius)); | |
| def.midPointId = Points.size(); | |
| Points.push_back(p1); | |
| // add the radius parameter | |
| double* r = params[params.size() - 1]; | |
| // set the circle for later constraints | |
| GCS::Circle c; | |
| c.center = p1; | |
| c.rad = r; | |
| def.index = Circles.size(); | |
| Circles.push_back(c); | |
| // store complete set | |
| Geoms.push_back(def); | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(p1.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(r), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 0) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| int Sketch::addEllipse(const Part::GeomEllipse& elip, bool fixed) | |
| { | |
| std::vector<double*>& params = fixed ? FixParameters : Parameters; | |
| // create our own copy | |
| GeomEllipse* elips = static_cast<GeomEllipse*>(elip.clone()); | |
| // create the definition struct for that geom | |
| GeoDef def; | |
| def.geo = elips; | |
| def.type = Ellipse; | |
| Base::Vector3d center = elips->getCenter(); | |
| double radmaj = elips->getMajorRadius(); | |
| double radmin = elips->getMinorRadius(); | |
| Base::Vector3d radmajdir = elips->getMajorAxisDir(); | |
| double dist_C_F = sqrt(radmaj * radmaj - radmin * radmin); | |
| // solver parameters | |
| Base::Vector3d focus1 = center + dist_C_F * radmajdir; //+x | |
| // double *radmin; | |
| GCS::Point c; | |
| params.push_back(new double(center.x)); | |
| params.push_back(new double(center.y)); | |
| c.x = params[params.size() - 2]; | |
| c.y = params[params.size() - 1]; | |
| def.midPointId = Points.size(); // this takes midPointId+1 | |
| Points.push_back(c); | |
| params.push_back(new double(focus1.x)); | |
| params.push_back(new double(focus1.y)); | |
| double* f1X = params[params.size() - 2]; | |
| double* f1Y = params[params.size() - 1]; | |
| // add the radius parameters | |
| params.push_back(new double(radmin)); | |
| double* rmin = params[params.size() - 1]; | |
| // set the ellipse for later constraints | |
| GCS::Ellipse e; | |
| e.focus1.x = f1X; | |
| e.focus1.y = f1Y; | |
| e.center = c; | |
| e.radmin = rmin; | |
| def.index = Ellipses.size(); | |
| Ellipses.push_back(e); | |
| // store complete set | |
| Geoms.push_back(def); | |
| if (!fixed) { | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(c.x), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(c.y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::mid, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(f1X), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 0) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(f1Y), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 1) | |
| ); | |
| param2geoelement.emplace( | |
| std::piecewise_construct, | |
| std::forward_as_tuple(rmin), | |
| std::forward_as_tuple(Geoms.size() - 1, Sketcher::PointPos::none, 2) | |
| ); | |
| } | |
| // return the position of the newly added geometry | |
| return Geoms.size() - 1; | |
| } | |
| std::vector<Part::Geometry*> Sketch::extractGeometry( | |
| bool withConstructionElements, | |
| bool withExternalElements | |
| ) const | |
| { | |
| std::vector<Part::Geometry*> temp; | |
| temp.reserve(Geoms.size()); | |
| for (const auto& geom : Geoms) { | |
| auto gf = GeometryFacade::getFacade(geom.geo); | |
| if ((!geom.external || withExternalElements) | |
| && (!gf->getConstruction() || withConstructionElements)) { | |
| temp.push_back(geom.geo->clone()); | |
| } | |
| } | |
| return temp; | |
| } | |
| GeoListFacade Sketch::extractGeoListFacade() const | |
| { | |
| std::vector<GeometryFacadeUniquePtr> temp; | |
| temp.reserve(Geoms.size()); | |
| int internalGeometryCount = 0; | |
| for (const auto& geom : Geoms) { | |
| // GeometryFacade is the owner of this allocation | |
| auto gf = GeometryFacade::getFacade(geom.geo->clone(), true); | |
| if (!geom.external) { | |
| internalGeometryCount++; | |
| } | |
| temp.push_back(std::move(gf)); | |
| } | |
| return GeoListFacade::getGeoListModel(std::move(temp), internalGeometryCount); | |
| } | |
| void Sketch::updateExtension(int geoId, std::unique_ptr<Part::GeometryExtension>&& ext) | |
| { | |
| geoId = checkGeoId(geoId); | |
| Geoms[geoId].geo->setExtension(std::move(ext)); | |
| } | |
| Py::Tuple Sketch::getPyGeometry() const | |
| { | |
| Py::Tuple tuple(Geoms.size()); | |
| int i = 0; | |
| for (auto it = Geoms.begin(); it != Geoms.end(); ++it, ++i) { | |
| switch (it->type) { | |
| case Point: { | |
| Base::Vector3d temp(*(Points[it->startPointId].x), *(Points[it->startPointId].y), 0); | |
| tuple[i] = Py::asObject(new VectorPy(temp)); | |
| break; | |
| } | |
| case Line: { | |
| auto* lineSeg = static_cast<GeomLineSegment*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new LineSegmentPy(lineSeg)); | |
| break; | |
| } | |
| case Arc: { | |
| auto* aoc = static_cast<GeomArcOfCircle*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new ArcOfCirclePy(aoc)); | |
| break; | |
| } | |
| case Circle: { | |
| auto* circle = static_cast<GeomCircle*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new CirclePy(circle)); | |
| break; | |
| } | |
| case Ellipse: { | |
| auto* ellipse = static_cast<GeomEllipse*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new EllipsePy(ellipse)); | |
| break; | |
| } | |
| case ArcOfEllipse: { | |
| auto* ellipse = static_cast<GeomArcOfEllipse*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new ArcOfEllipsePy(ellipse)); | |
| break; | |
| } | |
| case ArcOfHyperbola: { | |
| auto* aoh = static_cast<GeomArcOfHyperbola*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new ArcOfHyperbolaPy(aoh)); | |
| break; | |
| } | |
| case ArcOfParabola: { | |
| auto* aop = static_cast<GeomArcOfParabola*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new ArcOfParabolaPy(aop)); | |
| break; | |
| } | |
| case BSpline: { | |
| auto* bsp = static_cast<GeomBSplineCurve*>(it->geo->clone()); | |
| tuple[i] = Py::asObject(new BSplineCurvePy(bsp)); | |
| break; | |
| } | |
| default: | |
| // not implemented type in the sketch! | |
| break; | |
| } | |
| } | |
| return tuple; | |
| } | |
| int Sketch::checkGeoId(int geoId) const | |
| { | |
| if (geoId < 0) { | |
| geoId += Geoms.size(); // convert negative external-geometry index to index into Geoms | |
| } | |
| if (!(geoId >= 0 && geoId < int(Geoms.size()))) { | |
| throw Base::IndexError("Sketch::checkGeoId. GeoId index out range."); | |
| } | |
| return geoId; | |
| } | |
| GCS::Curve* Sketch::getGCSCurveByGeoId(int geoId) | |
| { | |
| geoId = checkGeoId(geoId); | |
| switch (Geoms[geoId].type) { | |
| case Line: | |
| return &Lines[Geoms[geoId].index]; | |
| break; | |
| case Circle: | |
| return &Circles[Geoms[geoId].index]; | |
| break; | |
| case Arc: | |
| return &Arcs[Geoms[geoId].index]; | |
| break; | |
| case Ellipse: | |
| return &Ellipses[Geoms[geoId].index]; | |
| break; | |
| case ArcOfEllipse: | |
| return &ArcsOfEllipse[Geoms[geoId].index]; | |
| break; | |
| case ArcOfHyperbola: | |
| return &ArcsOfHyperbola[Geoms[geoId].index]; | |
| break; | |
| case ArcOfParabola: | |
| return &ArcsOfParabola[Geoms[geoId].index]; | |
| break; | |
| case BSpline: | |
| return &BSplines[Geoms[geoId].index]; | |
| break; | |
| default: | |
| return nullptr; | |
| }; | |
| } | |
| const GCS::Curve* Sketch::getGCSCurveByGeoId(int geoId) const | |
| { | |
| // I hereby guarantee that if I modify the non-const version, I will still | |
| // never modify (this). I return const copy to enforce on my users. | |
| return const_cast<Sketch*>(this)->getGCSCurveByGeoId(geoId); | |
| } | |
| // constraint adding ========================================================== | |
| int Sketch::addConstraint(const Constraint* constraint) | |
| { | |
| if (Geoms.empty()) { | |
| throw Base::ValueError( | |
| "Sketch::addConstraint. Can't add constraint to a sketch with no geometry!" | |
| ); | |
| } | |
| int rtn = -1; | |
| ConstrDef c; | |
| c.constr = const_cast<Constraint*>(constraint); | |
| c.driving = constraint->isDriving; | |
| switch (constraint->Type) { | |
| case DistanceX: | |
| if (constraint->FirstPos == PointPos::none) { // horizontal length of a line | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceXConstraint(constraint->First, c.value, c.driving); | |
| } | |
| else if (constraint->Second == GeoEnum::GeoUndef) { // point on fixed x-coordinate | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addCoordinateXConstraint(constraint->First, constraint->FirstPos, c.value, c.driving); | |
| } | |
| else if (constraint->SecondPos != PointPos::none) { // point to point horizontal distance | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceXConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| c.value, | |
| c.driving | |
| ); | |
| } | |
| break; | |
| case DistanceY: | |
| if (constraint->FirstPos == PointPos::none) { // vertical length of a line | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceYConstraint(constraint->First, c.value, c.driving); | |
| } | |
| else if (constraint->Second == GeoEnum::GeoUndef) { // point on fixed y-coordinate | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addCoordinateYConstraint(constraint->First, constraint->FirstPos, c.value, c.driving); | |
| } | |
| else if (constraint->SecondPos != PointPos::none) { // point to point vertical distance | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceYConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| c.value, | |
| c.driving | |
| ); | |
| } | |
| break; | |
| case Horizontal: | |
| if (constraint->Second == GeoEnum::GeoUndef) { // horizontal line | |
| rtn = addHorizontalConstraint(constraint->First); | |
| } | |
| else { // two points on the same horizontal line | |
| rtn = addHorizontalConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos | |
| ); | |
| } | |
| break; | |
| case Vertical: | |
| if (constraint->Second == GeoEnum::GeoUndef) { // vertical line | |
| rtn = addVerticalConstraint(constraint->First); | |
| } | |
| else { // two points on the same vertical line | |
| rtn = addVerticalConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos | |
| ); | |
| } | |
| break; | |
| case Coincident: | |
| rtn = addPointCoincidentConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos | |
| ); | |
| break; | |
| case PointOnObject: | |
| if (Geoms[checkGeoId(constraint->Second)].type == BSpline) { | |
| c.value = new double(constraint->getValue()); | |
| // Driving doesn't make sense here | |
| Parameters.push_back(c.value); | |
| rtn = addPointOnObjectConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| c.value | |
| ); | |
| } | |
| else { | |
| rtn = addPointOnObjectConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second | |
| ); | |
| } | |
| break; | |
| case Parallel: | |
| rtn = addParallelConstraint(constraint->First, constraint->Second); | |
| break; | |
| case Perpendicular: | |
| if (constraint->FirstPos == PointPos::none && constraint->SecondPos == PointPos::none | |
| && constraint->Third == GeoEnum::GeoUndef) { | |
| // simple perpendicularity | |
| rtn = addPerpendicularConstraint(constraint->First, constraint->Second); | |
| } | |
| else { | |
| // any other point-wise perpendicularity | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addAngleAtPointConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| constraint->Third, | |
| constraint->ThirdPos, | |
| c.value, | |
| constraint->Type, | |
| c.driving | |
| ); | |
| } | |
| break; | |
| case Tangent: { | |
| bool isSpecialCase = false; | |
| if (constraint->FirstPos == PointPos::none && constraint->SecondPos == PointPos::none | |
| && constraint->Third == GeoEnum::GeoUndef) { | |
| // simple tangency | |
| rtn = addTangentConstraint(constraint->First, constraint->Second); | |
| isSpecialCase = true; | |
| } | |
| else if (constraint->FirstPos == PointPos::start | |
| && constraint->Third == GeoEnum::GeoUndef) { | |
| // check for B-Spline Knot to curve tangency | |
| auto knotgeoId = checkGeoId(constraint->First); | |
| if (Geoms[knotgeoId].type == Point) { | |
| auto* point = static_cast<const GeomPoint*>(Geoms[knotgeoId].geo); | |
| if (GeometryFacade::isInternalType(point, InternalType::BSplineKnotPoint)) { | |
| auto bsplinegeoid = internalAlignmentGeometryMap.at(constraint->First); | |
| bsplinegeoid = checkGeoId(bsplinegeoid); | |
| auto linegeoid = checkGeoId(constraint->Second); | |
| if (Geoms[linegeoid].type == Line) { | |
| if (constraint->SecondPos == PointPos::none) { | |
| rtn = addTangentLineAtBSplineKnotConstraint( | |
| linegeoid, | |
| bsplinegeoid, | |
| knotgeoId | |
| ); | |
| isSpecialCase = true; | |
| } | |
| else if (constraint->SecondPos == PointPos::start | |
| || constraint->SecondPos == PointPos::end) { | |
| rtn = addTangentLineEndpointAtBSplineKnotConstraint( | |
| linegeoid, | |
| constraint->SecondPos, | |
| bsplinegeoid, | |
| knotgeoId | |
| ); | |
| isSpecialCase = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (!isSpecialCase) { | |
| // any other point-wise tangency (endpoint-to-curve, endpoint-to-endpoint, | |
| // tangent-via-point) | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addAngleAtPointConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| constraint->Third, | |
| constraint->ThirdPos, | |
| c.value, | |
| constraint->Type, | |
| c.driving | |
| ); | |
| } | |
| break; | |
| } | |
| case Distance: | |
| if (constraint->SecondPos != PointPos::none) { // point to point distance | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| c.value, | |
| c.driving | |
| ); | |
| } | |
| else if (constraint->FirstPos == PointPos::none && constraint->SecondPos == PointPos::none | |
| && constraint->Second != GeoEnum::GeoUndef | |
| && constraint->Third == GeoEnum::GeoUndef) { // circle to circle, circle to | |
| // arc, etc. | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceConstraint(constraint->First, constraint->Second, c.value, c.driving); | |
| } | |
| else if (constraint->Second != GeoEnum::GeoUndef) { | |
| if (constraint->FirstPos != PointPos::none) { // point to line distance | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| c.value, | |
| c.driving | |
| ); | |
| } | |
| } | |
| else { // line length, arc length | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDistanceConstraint(constraint->First, c.value, c.driving); | |
| } | |
| break; | |
| case Angle: | |
| if (constraint->Third != GeoEnum::GeoUndef) { | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addAngleAtPointConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| constraint->Third, | |
| constraint->ThirdPos, | |
| c.value, | |
| constraint->Type, | |
| c.driving | |
| ); | |
| } | |
| // angle between two lines (with explicit start points) | |
| else if (constraint->SecondPos != PointPos::none) { | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addAngleConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| c.value, | |
| c.driving | |
| ); | |
| } | |
| else if (constraint->Second != GeoEnum::GeoUndef) { // angle between two lines | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addAngleConstraint(constraint->First, constraint->Second, c.value, c.driving); | |
| } | |
| else if (constraint->First != GeoEnum::GeoUndef) { // orientation angle of a line | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addAngleConstraint(constraint->First, c.value, c.driving); | |
| } | |
| break; | |
| case Radius: { | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addRadiusConstraint(constraint->First, c.value, c.driving); | |
| break; | |
| } | |
| case Diameter: { | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addDiameterConstraint(constraint->First, c.value, c.driving); | |
| break; | |
| } | |
| case Weight: { | |
| c.value = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| DrivenParameters.push_back(c.value); | |
| } | |
| rtn = addRadiusConstraint(constraint->First, c.value, c.driving); | |
| break; | |
| } | |
| case Equal: | |
| rtn = addEqualConstraint(constraint->First, constraint->Second); | |
| break; | |
| case Symmetric: | |
| if (constraint->ThirdPos != PointPos::none) { | |
| rtn = addSymmetricConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| constraint->Third, | |
| constraint->ThirdPos | |
| ); | |
| } | |
| else { | |
| rtn = addSymmetricConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| constraint->Third | |
| ); | |
| } | |
| break; | |
| case InternalAlignment: | |
| switch (constraint->AlignmentType) { | |
| case EllipseMajorDiameter: | |
| rtn = addInternalAlignmentEllipseMajorDiameter(constraint->First, constraint->Second); | |
| break; | |
| case EllipseMinorDiameter: | |
| rtn = addInternalAlignmentEllipseMinorDiameter(constraint->First, constraint->Second); | |
| break; | |
| case EllipseFocus1: | |
| rtn = addInternalAlignmentEllipseFocus1(constraint->First, constraint->Second); | |
| break; | |
| case EllipseFocus2: | |
| rtn = addInternalAlignmentEllipseFocus2(constraint->First, constraint->Second); | |
| break; | |
| case HyperbolaMajor: | |
| rtn = addInternalAlignmentHyperbolaMajorDiameter( | |
| constraint->First, | |
| constraint->Second | |
| ); | |
| break; | |
| case HyperbolaMinor: | |
| rtn = addInternalAlignmentHyperbolaMinorDiameter( | |
| constraint->First, | |
| constraint->Second | |
| ); | |
| break; | |
| case HyperbolaFocus: | |
| rtn = addInternalAlignmentHyperbolaFocus(constraint->First, constraint->Second); | |
| break; | |
| case ParabolaFocus: | |
| rtn = addInternalAlignmentParabolaFocus(constraint->First, constraint->Second); | |
| break; | |
| case BSplineControlPoint: | |
| rtn = addInternalAlignmentBSplineControlPoint( | |
| constraint->First, | |
| constraint->Second, | |
| constraint->InternalAlignmentIndex | |
| ); | |
| break; | |
| case BSplineKnotPoint: | |
| rtn = addInternalAlignmentKnotPoint( | |
| constraint->First, | |
| constraint->Second, | |
| constraint->InternalAlignmentIndex | |
| ); | |
| break; | |
| case ParabolaFocalAxis: | |
| rtn = addInternalAlignmentParabolaFocalDistance( | |
| constraint->First, | |
| constraint->Second | |
| ); | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case SnellsLaw: { | |
| c.value = new double(constraint->getValue()); | |
| c.secondvalue = new double(constraint->getValue()); | |
| if (c.driving) { | |
| FixParameters.push_back(c.value); | |
| FixParameters.push_back(c.secondvalue); | |
| } | |
| else { | |
| Parameters.push_back(c.value); | |
| Parameters.push_back(c.secondvalue); | |
| DrivenParameters.push_back(c.value); | |
| DrivenParameters.push_back(c.secondvalue); | |
| } | |
| // assert(constraint->ThirdPos==none); //will work anyway... | |
| rtn = addSnellsLawConstraint( | |
| constraint->First, | |
| constraint->FirstPos, | |
| constraint->Second, | |
| constraint->SecondPos, | |
| constraint->Third, | |
| c.value, | |
| c.secondvalue, | |
| c.driving | |
| ); | |
| } break; | |
| case Sketcher::None: // ambiguous enum value | |
| case Sketcher::Block: // handled separately while adding geometry | |
| case NumConstraintTypes: | |
| break; | |
| } | |
| Constrs.push_back(c); | |
| return rtn; | |
| } | |
| int Sketch::addConstraints(const std::vector<Constraint*>& ConstraintList) | |
| { | |
| int rtn = -1; | |
| int cid = 0; | |
| for (auto it = ConstraintList.cbegin(); it != ConstraintList.cend(); ++it, ++cid) { | |
| rtn = addConstraint(*it); | |
| if (rtn == -1) { | |
| int humanConstraintId = cid + 1; | |
| Base::Console().error("Sketcher constraint number %d is malformed!\n", humanConstraintId); | |
| MalformedConstraints.push_back(humanConstraintId); | |
| } | |
| } | |
| return rtn; | |
| } | |
| int Sketch::addConstraints( | |
| const std::vector<Constraint*>& ConstraintList, | |
| const std::vector<bool>& unenforceableConstraints | |
| ) | |
| { | |
| int rtn = -1; | |
| int cid = 0; | |
| for (auto it = ConstraintList.cbegin(); it != ConstraintList.cend(); ++it, ++cid) { | |
| if (!unenforceableConstraints[cid] && (*it)->Type != Block && (*it)->isActive) { | |
| rtn = addConstraint(*it); | |
| if (rtn == -1) { | |
| int humanConstraintId = cid + 1; | |
| Base::Console().error("Sketcher constraint number %d is malformed!\n", humanConstraintId); | |
| MalformedConstraints.push_back(humanConstraintId); | |
| } | |
| } | |
| else { | |
| ++ConstraintsCounter; // For correct solver redundant reporting | |
| } | |
| } | |
| return rtn; | |
| } | |
| void Sketch::getBlockedGeometry( | |
| std::vector<bool>& blockedGeometry, | |
| std::vector<bool>& unenforceableConstraints, | |
| const std::vector<Constraint*>& ConstraintList | |
| ) const | |
| { | |
| std::vector<int> internalAlignmentConstraintIndex; | |
| std::vector<int> internalAlignmentgeo; | |
| std::vector<int> geo2blockingconstraintindex(blockedGeometry.size(), -1); | |
| // Detect Blocked and internal constraints | |
| int i = 0; | |
| for (auto it = ConstraintList.cbegin(); it != ConstraintList.cend(); ++it, ++i) { | |
| switch ((*it)->Type) { | |
| case Block: { | |
| int geoid = (*it)->First; | |
| if (geoid >= 0 && geoid < int(blockedGeometry.size())) { | |
| blockedGeometry[geoid] = true; | |
| geo2blockingconstraintindex[geoid] = i; | |
| } | |
| } break; | |
| case InternalAlignment: | |
| internalAlignmentConstraintIndex.push_back(i); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| // if a GeoId is blocked and it is linked to Internal Alignment, then GeoIds linked via Internal | |
| // Alignment are also to be blocked | |
| for (auto idx : internalAlignmentConstraintIndex) { | |
| if (blockedGeometry[ConstraintList[idx]->Second]) { | |
| blockedGeometry[ConstraintList[idx]->First] = true; | |
| // associated geometry gets the same blocking constraint index as the blocked element | |
| geo2blockingconstraintindex[ConstraintList[idx]->First] | |
| = geo2blockingconstraintindex[ConstraintList[idx]->Second]; | |
| internalAlignmentgeo.push_back(ConstraintList[idx]->First); | |
| unenforceableConstraints[idx] = true; | |
| } | |
| } | |
| i = 0; | |
| for (auto it = ConstraintList.begin(); it != ConstraintList.end(); ++it, ++i) { | |
| if ((*it)->isDriving) { | |
| // additionally any further constraint on auxiliary elements linked via Internal | |
| // Alignment are also unenforceable. | |
| for (auto& iag : internalAlignmentgeo) { | |
| if ((*it)->First == iag || (*it)->Second == iag || (*it)->Third == iag) { | |
| unenforceableConstraints[i] = true; | |
| } | |
| } | |
| // IMPORTANT NOTE: | |
| // The rest of the ignoring of redundant/conflicting applies to constraints introduced | |
| // before the blocking constraint only Constraints introduced after the block will not | |
| // be ignored and will lead to redundancy/conflicting status as per normal solver | |
| // behaviour | |
| // further, any constraint taking only one element, which is blocked is also | |
| // unenforceable | |
| if ((*it)->Second == GeoEnum::GeoUndef && (*it)->Third == GeoEnum::GeoUndef | |
| && (*it)->First >= 0) { | |
| if (blockedGeometry[(*it)->First] && i < geo2blockingconstraintindex[(*it)->First]) { | |
| unenforceableConstraints[i] = true; | |
| } | |
| } | |
| // further any constraint on only two elements where both elements are blocked or one is | |
| // blocked and the other is an axis or external provided that the constraints precede | |
| // the last block constraint. | |
| else if ((*it)->Third == GeoEnum::GeoUndef) { | |
| if (((*it)->First >= 0 && (*it)->Second >= 0 && blockedGeometry[(*it)->First] | |
| && blockedGeometry[(*it)->Second] | |
| && (i < geo2blockingconstraintindex[(*it)->First] | |
| || i < geo2blockingconstraintindex[(*it)->Second])) | |
| || ((*it)->First < 0 && (*it)->Second >= 0 && blockedGeometry[(*it)->Second] | |
| && i < geo2blockingconstraintindex[(*it)->Second]) | |
| || ((*it)->First >= 0 && (*it)->Second < 0 && blockedGeometry[(*it)->First] | |
| && i < geo2blockingconstraintindex[(*it)->First])) { | |
| unenforceableConstraints[i] = true; | |
| } | |
| } | |
| // further any constraint on three elements where the three of them are blocked, or two | |
| // are blocked and the other is an axis or external geo or any constraint on three | |
| // elements where one is blocked and the other two are axis or external geo, provided | |
| // that the constraints precede the last block constraint. | |
| else { | |
| if (((*it)->First >= 0 && (*it)->Second >= 0 && (*it)->Third >= 0 | |
| && blockedGeometry[(*it)->First] && blockedGeometry[(*it)->Second] | |
| && blockedGeometry[(*it)->Third] | |
| && (i < geo2blockingconstraintindex[(*it)->First] | |
| || i < geo2blockingconstraintindex[(*it)->Second] | |
| || i < geo2blockingconstraintindex[(*it)->Third])) | |
| || ((*it)->First < 0 && (*it)->Second >= 0 && (*it)->Third >= 0 | |
| && blockedGeometry[(*it)->Second] && blockedGeometry[(*it)->Third] | |
| && (i < geo2blockingconstraintindex[(*it)->Second] | |
| || i < geo2blockingconstraintindex[(*it)->Third])) | |
| || ((*it)->First >= 0 && (*it)->Second < 0 && (*it)->Third >= 0 | |
| && blockedGeometry[(*it)->First] && blockedGeometry[(*it)->Third] | |
| && (i < geo2blockingconstraintindex[(*it)->First] | |
| || i < geo2blockingconstraintindex[(*it)->Third])) | |
| || ((*it)->First >= 0 && (*it)->Second >= 0 && (*it)->Third < 0 | |
| && blockedGeometry[(*it)->First] && blockedGeometry[(*it)->Second] | |
| && (i < geo2blockingconstraintindex[(*it)->First] | |
| || i < geo2blockingconstraintindex[(*it)->Second])) | |
| || ((*it)->First >= 0 && (*it)->Second < 0 && (*it)->Third < 0 | |
| && blockedGeometry[(*it)->First] | |
| && i < geo2blockingconstraintindex[(*it)->First]) | |
| || ((*it)->First < 0 && (*it)->Second >= 0 && (*it)->Third < 0 | |
| && blockedGeometry[(*it)->Second] | |
| && i < geo2blockingconstraintindex[(*it)->Second]) | |
| || ((*it)->First < 0 && (*it)->Second < 0 && (*it)->Third >= 0 | |
| && blockedGeometry[(*it)->Third] | |
| && i < geo2blockingconstraintindex[(*it)->Third])) { | |
| unenforceableConstraints[i] = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| int Sketch::addCoordinateXConstraint(int geoId, PointPos pos, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| int pointId = getPointId(geoId, pos); | |
| if (pointId >= 0 && pointId < int(Points.size())) { | |
| GCS::Point& p = Points[pointId]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintCoordinateX(p, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addCoordinateYConstraint(int geoId, PointPos pos, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| int pointId = getPointId(geoId, pos); | |
| if (pointId >= 0 && pointId < int(Points.size())) { | |
| GCS::Point& p = Points[pointId]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintCoordinateY(p, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addDistanceXConstraint(int geoId, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type != Line) { | |
| return -1; | |
| } | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintDifference(l.p1.x, l.p2.x, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| int Sketch::addDistanceYConstraint(int geoId, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type != Line) { | |
| return -1; | |
| } | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintDifference(l.p1.y, l.p2.y, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| int Sketch::addDistanceXConstraint( | |
| int geoId1, | |
| PointPos pos1, | |
| int geoId2, | |
| PointPos pos2, | |
| double* value, | |
| bool driving | |
| ) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintDifference(p1.x, p2.x, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addDistanceYConstraint( | |
| int geoId1, | |
| PointPos pos1, | |
| int geoId2, | |
| PointPos pos2, | |
| double* value, | |
| bool driving | |
| ) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintDifference(p1.y, p2.y, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| // horizontal line constraint | |
| int Sketch::addHorizontalConstraint(int geoId) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type != Line) { | |
| return -1; | |
| } | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintHorizontal(l, tag); | |
| return ConstraintsCounter; | |
| } | |
| // two points on a horizontal line constraint | |
| int Sketch::addHorizontalConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintHorizontal(p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| // vertical line constraint | |
| int Sketch::addVerticalConstraint(int geoId) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type != Line) { | |
| return -1; | |
| } | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintVertical(l, tag); | |
| return ConstraintsCounter; | |
| } | |
| // two points on a vertical line constraint | |
| int Sketch::addVerticalConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintVertical(p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addPointCoincidentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PCoincident(p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addParallelConstraint(int geoId1, int geoId2) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Line || Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| GCS::Line& l1 = Lines[Geoms[geoId1].index]; | |
| GCS::Line& l2 = Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintParallel(l1, l2, tag); | |
| return ConstraintsCounter; | |
| } | |
| // simple perpendicularity constraint | |
| int Sketch::addPerpendicularConstraint(int geoId1, int geoId2) | |
| { | |
| // accepts the following combinations: | |
| // 1) Line1, Line2/Circle2/Arc2 | |
| // 2) Circle1, Line2 (converted to case #1) | |
| // 3) Arc1, Line2 (converted to case #1) | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId2].type == Line) { | |
| if (Geoms[geoId1].type == Line) { | |
| GCS::Line& l1 = Lines[Geoms[geoId1].index]; | |
| GCS::Line& l2 = Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPerpendicular(l1, l2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| std::swap(geoId1, geoId2); | |
| } | |
| } | |
| if (Geoms[geoId1].type == Line) { | |
| GCS::Line& l1 = Lines[Geoms[geoId1].index]; | |
| if (Geoms[geoId2].type == Arc || Geoms[geoId2].type == Circle) { | |
| GCS::Point& p2 = Points[Geoms[geoId2].midPointId]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnLine(p2, l1, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| Base::Console().warning( | |
| "Perpendicular constraints between %s and %s are not supported.\n", | |
| nameByType(Geoms[geoId1].type), | |
| nameByType(Geoms[geoId2].type) | |
| ); | |
| return -1; | |
| } | |
| // simple tangency constraint | |
| int Sketch::addTangentConstraint(int geoId1, int geoId2) | |
| { | |
| // accepts the following combinations: | |
| // 1) Line1, Line2/Circle2/Arc2 | |
| // 2) Circle1, Line2 (converted to case #1) | |
| // Circle1, Circle2/Arc2 | |
| // 3) Arc1, Line2 (converted to case #1) | |
| // Arc1, Circle2/Arc2 | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId2].type == Line) { | |
| if (Geoms[geoId1].type == Line) { | |
| GCS::Line& l1 = Lines[Geoms[geoId1].index]; | |
| GCS::Point& l2p1 = Points[Geoms[geoId2].startPointId]; | |
| GCS::Point& l2p2 = Points[Geoms[geoId2].endPointId]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnLine(l2p1, l1, tag); | |
| GCSsys.addConstraintPointOnLine(l2p2, l1, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| std::swap(geoId1, geoId2); | |
| } | |
| } | |
| if (Geoms[geoId1].type == Line) { | |
| GCS::Line& l = Lines[Geoms[geoId1].index]; | |
| if (Geoms[geoId2].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(l, a, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(l, c, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Ellipse) { | |
| GCS::Ellipse& e = Ellipses[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(l, e, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == ArcOfEllipse) { | |
| GCS::ArcOfEllipse& a = ArcsOfEllipse[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(l, a, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == BSpline) { | |
| Base::Console().error( | |
| "Direct tangency constraint between line and B-spline is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| } | |
| else if (Geoms[geoId1].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId1].index]; | |
| if (Geoms[geoId2].type == Circle) { | |
| GCS::Circle& c2 = Circles[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(c, c2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Ellipse) { | |
| Base::Console().error( | |
| "Direct tangency constraint between circle and ellipse is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| else if (Geoms[geoId2].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(c, a, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == BSpline) { | |
| Base::Console().error( | |
| "Direct tangency constraint between circle and B-spline is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| } | |
| else if (Geoms[geoId1].type == Ellipse) { | |
| if (Geoms[geoId2].type == Circle) { | |
| Base::Console().error( | |
| "Direct tangency constraint between circle and ellipse is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| else if (Geoms[geoId2].type == Arc) { | |
| Base::Console().error( | |
| "Direct tangency constraint between arc and ellipse is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| else if (Geoms[geoId2].type == BSpline) { | |
| Base::Console().error( | |
| "Direct tangency constraint between ellipse and B-spline is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| } | |
| else if (Geoms[geoId1].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId1].index]; | |
| if (Geoms[geoId2].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(c, a, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Ellipse) { | |
| Base::Console().error( | |
| "Direct tangency constraint between arc and ellipse is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| else if (Geoms[geoId2].type == Arc) { | |
| GCS::Arc& a2 = Arcs[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintTangent(a, a2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == BSpline) { | |
| Base::Console().error( | |
| "Direct tangency constraint between arc and B-spline is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| } | |
| else if (Geoms[geoId1].type == BSpline) { | |
| Base::Console().error( | |
| "Direct tangency constraint including B-splines is not " | |
| "supported. Use tangent-via-point instead." | |
| ); | |
| return -1; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addTangentLineAtBSplineKnotConstraint( | |
| int checkedlinegeoId, | |
| int checkedbsplinegeoId, | |
| int checkedknotgeoid | |
| ) | |
| { | |
| GCS::BSpline& b = BSplines[Geoms[checkedbsplinegeoId].index]; | |
| GCS::Line& l = Lines[Geoms[checkedlinegeoId].index]; | |
| size_t knotindex = b.knots.size(); | |
| const auto knotIt = std::ranges::find(b.knotpointGeoids, checkedknotgeoid); | |
| knotindex = std::distance(b.knotpointGeoids.begin(), knotIt); | |
| if (knotindex >= b.knots.size()) { | |
| Base::Console().error("addConstraint: Knot index out-of-range!\n"); | |
| return -1; | |
| } | |
| if (b.mult[knotindex] >= b.degree) { | |
| if (b.periodic || (knotindex > 0 && knotindex < (b.knots.size() - 1))) { | |
| Base::Console().error( | |
| "addTangentLineAtBSplineKnotConstraint: cannot set constraint " | |
| "when B-spline slope is discontinuous at knot!\n" | |
| ); | |
| return -1; | |
| } | |
| else { | |
| // TODO: Let angle-at-point do the work. Requires a `double * value` | |
| // return addAngleAtPointConstraint( | |
| // linegeoid, PointPos::none, | |
| // bsplinegeoid, PointPos::none, | |
| // knotgeoId, PointPos::start, | |
| // nullptr, Tangent, true); | |
| // For now we just throw an error. | |
| Base::Console().error( | |
| "addTangentLineAtBSplineKnotConstraint: This method cannot set tangent constraints " | |
| "at end knots of a B-spline. Constrain the start/end points instead.\n" | |
| ); | |
| return -1; | |
| } | |
| } | |
| else { | |
| // increases ConstraintsCounter | |
| int tag | |
| = Sketch::addPointOnObjectConstraint(checkedknotgeoid, PointPos::start, checkedlinegeoId); | |
| GCSsys.addConstraintTangentAtBSplineKnot(b, l, knotindex, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| int Sketch::addTangentLineEndpointAtBSplineKnotConstraint( | |
| int checkedlinegeoId, | |
| PointPos endpointPos, | |
| int checkedbsplinegeoId, | |
| int checkedknotgeoid | |
| ) | |
| { | |
| GCS::BSpline& b = BSplines[Geoms[checkedbsplinegeoId].index]; | |
| GCS::Line& l = Lines[Geoms[checkedlinegeoId].index]; | |
| auto pointId = getPointId(checkedlinegeoId, endpointPos); | |
| auto pointIdKnot = getPointId(checkedknotgeoid, PointPos::start); | |
| GCS::Point& p = Points[pointId]; | |
| GCS::Point& pk = Points[pointIdKnot]; | |
| size_t knotindex = b.knots.size(); | |
| auto knotIt = std::ranges::find(b.knotpointGeoids, checkedknotgeoid); | |
| knotindex = std::distance(b.knotpointGeoids.begin(), knotIt); | |
| if (knotindex >= b.knots.size()) { | |
| Base::Console().error("addConstraint: Knot index out-of-range!\n"); | |
| return -1; | |
| } | |
| if (b.mult[knotindex] >= b.degree) { | |
| if (b.periodic || (knotindex > 0 && knotindex < (b.knots.size() - 1))) { | |
| Base::Console().error( | |
| "addTangentLineEndpointAtBSplineKnotConstraint: cannot set " | |
| "constraint when B-spline slope is discontinuous at knot!\n" | |
| ); | |
| return -1; | |
| } | |
| else { | |
| // TODO: Let angle-at-point do the work. Requires a `double * value` | |
| // return addAngleAtPointConstraint( | |
| // linegeoid, endpointPos, | |
| // bsplinegeoid, PointPos::none, | |
| // knotgeoId, PointPos::start, | |
| // nullptr, Tangent, true); | |
| // For now we just throw an error. | |
| Base::Console().error( | |
| "addTangentLineEndpointAtBSplineKnotConstraint: This method " | |
| "cannot set tangent constraint at end knots of a B-spline. " | |
| "Constrain the start/end points instead.\n" | |
| ); | |
| return -1; | |
| } | |
| } | |
| else { | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PCoincident(p, pk, tag); | |
| GCSsys.addConstraintTangentAtBSplineKnot(b, l, knotindex, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| // This function handles any type of tangent, perpendicular and angle | |
| // constraint that involves a point. | |
| // i.e. endpoint-to-curve, endpoint-to-endpoint and tangent-via-point | |
| // geoid1, geoid2 and geoid3 as in the constraint object. | |
| // For perp-ty and tangency, angle is used to lock the direction. | |
| // angle==0 - autodetect direction. +pi/2, -pi/2 - specific direction. | |
| int Sketch::addAngleAtPointConstraint( | |
| int geoId1, | |
| PointPos pos1, | |
| int geoId2, | |
| PointPos pos2, | |
| int geoId3, | |
| PointPos pos3, | |
| double* value, | |
| ConstraintType cTyp, | |
| bool driving | |
| ) | |
| { | |
| using std::numbers::pi; | |
| if (!(cTyp == Angle || cTyp == Tangent || cTyp == Perpendicular)) { | |
| // assert(0);//none of the three types. Why are we here?? | |
| return -1; | |
| } | |
| bool avp = geoId3 != GeoEnum::GeoUndef; // is angle-via-point? | |
| bool e2c = pos2 == PointPos::none && pos1 != PointPos::none; // is endpoint-to-curve? | |
| bool e2e = pos2 != PointPos::none && pos1 != PointPos::none; // is endpoint-to-endpoint? | |
| if (!(avp || e2c || e2e)) { | |
| // assert(0);//none of the three types. Why are we here?? | |
| return -1; | |
| } | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (avp) { | |
| geoId3 = checkGeoId(geoId3); | |
| } | |
| if (Geoms[geoId1].type == Point || Geoms[geoId2].type == Point) { | |
| Base::Console().error("addAngleAtPointConstraint: one of the curves is a point!\n"); | |
| return -1; | |
| } | |
| GCS::Curve* crv1 = getGCSCurveByGeoId(geoId1); | |
| GCS::Curve* crv2 = getGCSCurveByGeoId(geoId2); | |
| if (!crv1 || !crv2) { | |
| Base::Console().error("addAngleAtPointConstraint: getGCSCurveByGeoId returned NULL!\n"); | |
| return -1; | |
| } | |
| int pointId = -1; | |
| if (avp) { | |
| pointId = getPointId(geoId3, pos3); | |
| } | |
| else if (e2e || e2c) { | |
| pointId = getPointId(geoId1, pos1); | |
| } | |
| if (pointId < 0 || pointId >= int(Points.size())) { | |
| Base::Console().error("addAngleAtPointConstraint: point index out of range.\n"); | |
| return -1; | |
| } | |
| GCS::Point& p = Points[pointId]; | |
| GCS::Point* p2 = nullptr; | |
| if (e2e) { // we need second point | |
| int pointId = getPointId(geoId2, pos2); | |
| if (pointId < 0 || pointId >= int(Points.size())) { | |
| Base::Console().error("addAngleAtPointConstraint: point index out of range.\n"); | |
| return -1; | |
| } | |
| p2 = &(Points[pointId]); | |
| } | |
| double* angle = value; | |
| // For tangency/perpendicularity, we don't just copy the angle. | |
| // The angle stored for tangency/perpendicularity is offset, so that the options | |
| // are -Pi/2 and Pi/2. If value is 0 - this is an indicator of an old sketch. | |
| // Use autodetect then. | |
| // The same functionality is implemented in SketchObject.cpp, where | |
| // it is used to permanently lock down the autodecision. | |
| if (cTyp != Angle) { | |
| // The same functionality is implemented in SketchObject.cpp, where | |
| // it is used to permanently lock down the autodecision. | |
| // the difference between the datum value and the actual angle to apply. | |
| // (datum=angle+offset) | |
| double angleOffset = 0.0; | |
| // the desired angle value (and we are to decide if 180* should be added to it) | |
| double angleDesire = 0.0; | |
| if (cTyp == Tangent) { | |
| angleOffset = -pi / 2; | |
| angleDesire = 0.0; | |
| } | |
| if (cTyp == Perpendicular) { | |
| angleOffset = 0; | |
| angleDesire = pi / 2; | |
| } | |
| if (*value == 0.0) { // autodetect tangency internal/external (and same for perpendicularity) | |
| double angleErr = GCSsys.calculateAngleViaPoint(*crv1, *crv2, p) - angleDesire; | |
| // bring angleErr to -pi..pi | |
| if (angleErr > pi) { | |
| angleErr -= pi * 2; | |
| } | |
| if (angleErr < -pi) { | |
| angleErr += pi * 2; | |
| } | |
| // the autodetector | |
| if (fabs(angleErr) > pi / 2) { | |
| angleDesire += pi; | |
| } | |
| *angle = angleDesire; | |
| } | |
| else { | |
| *angle = *value - angleOffset; | |
| } | |
| } | |
| int tag = -1; | |
| // FIXME: Perform construction of any parameters where this method is called instead of here | |
| if (e2c) { | |
| if (Geoms[geoId2].type == BSpline) { | |
| GCS::Point& p1 = Points[getPointId(geoId1, pos1)]; | |
| auto* partBsp = static_cast<GeomBSplineCurve*>(Geoms[geoId2].geo); | |
| double uNear; | |
| partBsp->closestParameter(Base::Vector3d(*p1.x, *p1.y, 0.0), uNear); | |
| double* pointparam = new double(uNear); | |
| Parameters.push_back(pointparam); | |
| --ConstraintsCounter; // Do this just before point-on-object because ConstraintsCounter | |
| // is increased again before being used | |
| tag = addPointOnObjectConstraint( | |
| geoId1, | |
| pos1, | |
| geoId2, | |
| pointparam, | |
| driving | |
| ); // increases ConstraintsCounter | |
| GCSsys.addConstraintAngleViaPointAndParam(*crv2, *crv1, p, pointparam, angle, tag, driving); | |
| } | |
| else { | |
| // increases ConstraintsCounter | |
| tag = Sketch::addPointOnObjectConstraint( | |
| geoId1, | |
| pos1, | |
| geoId2, | |
| driving | |
| ); // increases ConstraintsCounter | |
| GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); | |
| } | |
| } | |
| if (e2e) { | |
| tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PCoincident(p, *p2, tag, driving); | |
| if (Geoms[geoId1].type == BSpline && Geoms[geoId2].type == BSpline) { | |
| GCSsys.addConstraintAngleViaTwoPoints(*crv1, *crv2, p, *p2, angle, tag, driving); | |
| } | |
| else { | |
| GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); | |
| } | |
| } | |
| if (avp) { | |
| tag = ++ConstraintsCounter; | |
| if (Geoms[geoId1].type == BSpline || Geoms[geoId2].type == BSpline) { | |
| if (Geoms[geoId1].type == BSpline && Geoms[geoId2].type == BSpline) { | |
| GCS::Point& p3 = Points[getPointId(geoId3, pos3)]; | |
| auto* partBsp = static_cast<GeomBSplineCurve*>(Geoms[geoId1].geo); | |
| double uNear; | |
| partBsp->closestParameter(Base::Vector3d(*p3.x, *p3.y, 0.0), uNear); | |
| double* pointparam1 = new double(uNear); | |
| Parameters.push_back(pointparam1); | |
| --ConstraintsCounter; // Do this just before point-on-object because | |
| // ConstraintsCounter is increased again before being used | |
| addPointOnObjectConstraint( | |
| geoId3, | |
| pos3, | |
| geoId1, | |
| pointparam1, | |
| driving | |
| ); // increases ConstraintsCounter | |
| partBsp = static_cast<GeomBSplineCurve*>(Geoms[geoId2].geo); | |
| partBsp->closestParameter(Base::Vector3d(*p3.x, *p3.y, 0.0), uNear); | |
| double* pointparam2 = new double(uNear); | |
| --ConstraintsCounter; // Do this just before point-on-object because | |
| // ConstraintsCounter is increased again before being used | |
| addPointOnObjectConstraint( | |
| geoId3, | |
| pos3, | |
| geoId2, | |
| pointparam2, | |
| driving | |
| ); // increases ConstraintsCounter | |
| Parameters.push_back(pointparam2); | |
| GCSsys.addConstraintAngleViaPointAndTwoParams( | |
| *crv1, | |
| *crv2, | |
| p, | |
| pointparam1, | |
| pointparam2, | |
| angle, | |
| tag, | |
| driving | |
| ); | |
| } | |
| else { | |
| if (Geoms[geoId1].type != BSpline) { | |
| std::swap(geoId1, geoId2); | |
| std::swap(crv1, crv2); | |
| std::swap(pos1, pos2); | |
| // FIXME: Confirm whether or not this is needed | |
| // *angle = -*angle; | |
| } | |
| GCS::Point& p3 = Points[getPointId(geoId3, pos3)]; | |
| auto* partBsp = static_cast<GeomBSplineCurve*>(Geoms[geoId1].geo); | |
| double uNear; | |
| partBsp->closestParameter(Base::Vector3d(*p3.x, *p3.y, 0.0), uNear); | |
| double* pointparam = new double(uNear); | |
| Parameters.push_back(pointparam); | |
| --ConstraintsCounter; // Do this just before point-on-object because | |
| // ConstraintsCounter is increased again before being used | |
| addPointOnObjectConstraint( | |
| geoId3, | |
| pos3, | |
| geoId1, | |
| pointparam, | |
| driving | |
| ); // increases ConstraintsCounter | |
| GCSsys.addConstraintAngleViaPointAndParam(*crv1, *crv2, p, pointparam, angle, tag, driving); | |
| } | |
| } | |
| else { | |
| GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); | |
| } | |
| } | |
| return ConstraintsCounter; | |
| } | |
| // line length and arc length constraint | |
| int Sketch::addDistanceConstraint(int geoId, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| int tag = ++ConstraintsCounter; | |
| if (Geoms[geoId].type == Line) { | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| GCSsys.addConstraintP2PDistance(l.p1, l.p2, value, tag, driving); | |
| } | |
| else if (Geoms[geoId].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId].index]; | |
| GCSsys.addConstraintArcLength(a, value, tag, driving); | |
| } | |
| else { | |
| return -1; | |
| } | |
| return ConstraintsCounter; | |
| } | |
| // point to line or circular distance constraint | |
| int Sketch::addDistanceConstraint(int geoId1, PointPos pos1, int geoId2, double* value, bool driving) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| if (pointId1 < 0 && pointId1 >= int(Points.size())) { | |
| return -1; | |
| } | |
| GCS::Point& p1 = Points[pointId1]; | |
| if (Geoms[geoId2].type == Line) { | |
| GCS::Line& l2 = Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2LDistance(p1, l2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| GCS::Circle* c2; | |
| if (Geoms[geoId2].type == Circle) { | |
| c2 = &Circles[Geoms[geoId2].index]; | |
| } | |
| else if (Geoms[geoId2].type == Arc) { | |
| c2 = &Arcs[Geoms[geoId2].index]; | |
| } | |
| else { | |
| return -1; | |
| } | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2CDistance(p1, *c2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| // point to point distance constraint | |
| int Sketch::addDistanceConstraint( | |
| int geoId1, | |
| PointPos pos1, | |
| int geoId2, | |
| PointPos pos2, | |
| double* value, | |
| bool driving | |
| ) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PDistance(p1, p2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| // circular-(circular or line) distance constraint | |
| int Sketch::addDistanceConstraint(int geoId1, int geoId2, double* value, bool driving) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId2].type == Line) { | |
| GCS::Circle* c1; | |
| if (Geoms[geoId1].type == Circle) { | |
| c1 = &Circles[Geoms[geoId1].index]; | |
| } | |
| else if (Geoms[geoId1].type == Arc) { | |
| c1 = &Arcs[Geoms[geoId1].index]; | |
| } | |
| else { | |
| return -1; | |
| } | |
| GCS::Line* l = &Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintC2LDistance(*c1, *l, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| GCS::Circle *c1 {}, *c2 {}; | |
| if (Geoms[geoId1].type == Circle) { | |
| c1 = &Circles[Geoms[geoId1].index]; | |
| } | |
| else if (Geoms[geoId1].type == Arc) { | |
| c1 = &Arcs[Geoms[geoId1].index]; | |
| } | |
| if (Geoms[geoId2].type == Circle) { | |
| c2 = &Circles[Geoms[geoId2].index]; | |
| } | |
| else if (Geoms[geoId2].type == Arc) { | |
| c2 = &Arcs[Geoms[geoId2].index]; | |
| } | |
| if (c1 == nullptr || c2 == nullptr) { | |
| return -1; | |
| } | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintC2CDistance(*c1, *c2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| int Sketch::addRadiusConstraint(int geoId, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintCircleRadius(c, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintArcRadius(a, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addDiameterConstraint(int geoId, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintCircleDiameter(c, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintArcDiameter(a, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| // line orientation angle constraint | |
| int Sketch::addAngleConstraint(int geoId, double* value, bool driving) | |
| { | |
| geoId = checkGeoId(geoId); | |
| if (Geoms[geoId].type == Line) { | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PAngle(l.p1, l.p2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintL2LAngle(a.center, a.start, a.center, a.end, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| // line to line angle constraint | |
| int Sketch::addAngleConstraint(int geoId1, int geoId2, double* value, bool driving) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Line || Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| GCS::Line& l1 = Lines[Geoms[geoId1].index]; | |
| GCS::Line& l2 = Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintL2LAngle(l1, l2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| // line to line angle constraint (with explicitly given start points) | |
| int Sketch::addAngleConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2, double* value, bool driving) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Line || Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| GCS::Point *l1p1 = nullptr, *l1p2 = nullptr; | |
| if (pos1 == PointPos::start) { | |
| l1p1 = &Points[Geoms[geoId1].startPointId]; | |
| l1p2 = &Points[Geoms[geoId1].endPointId]; | |
| } | |
| else if (pos1 == PointPos::end) { | |
| l1p1 = &Points[Geoms[geoId1].endPointId]; | |
| l1p2 = &Points[Geoms[geoId1].startPointId]; | |
| } | |
| GCS::Point *l2p1 = nullptr, *l2p2 = nullptr; | |
| if (pos2 == PointPos::start) { | |
| l2p1 = &Points[Geoms[geoId2].startPointId]; | |
| l2p2 = &Points[Geoms[geoId2].endPointId]; | |
| } | |
| else if (pos2 == PointPos::end) { | |
| l2p1 = &Points[Geoms[geoId2].endPointId]; | |
| l2p2 = &Points[Geoms[geoId2].startPointId]; | |
| } | |
| if (!l1p1 || !l2p1) { | |
| return -1; | |
| } | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintL2LAngle(*l1p1, *l1p2, *l2p1, *l2p2, value, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| int Sketch::addEqualConstraint(int geoId1, int geoId2) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type == Line && Geoms[geoId2].type == Line) { | |
| GCS::Line& l1 = Lines[Geoms[geoId1].index]; | |
| GCS::Line& l2 = Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualLength(l1, l2, tag); | |
| return ConstraintsCounter; | |
| } | |
| if (Geoms[geoId2].type == Circle) { | |
| if (Geoms[geoId1].type == Circle) { | |
| GCS::Circle& c1 = Circles[Geoms[geoId1].index]; | |
| GCS::Circle& c2 = Circles[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadius(c1, c2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| std::swap(geoId1, geoId2); | |
| } | |
| } | |
| if (Geoms[geoId2].type == Ellipse) { | |
| if (Geoms[geoId1].type == Ellipse) { | |
| GCS::Ellipse& e1 = Ellipses[Geoms[geoId1].index]; | |
| GCS::Ellipse& e2 = Ellipses[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadii(e1, e2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| std::swap(geoId1, geoId2); | |
| } | |
| } | |
| if (Geoms[geoId1].type == Circle) { | |
| GCS::Circle& c1 = Circles[Geoms[geoId1].index]; | |
| if (Geoms[geoId2].type == Arc) { | |
| GCS::Arc& a2 = Arcs[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadius(c1, a2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| if (Geoms[geoId1].type == Arc && Geoms[geoId2].type == Arc) { | |
| GCS::Arc& a1 = Arcs[Geoms[geoId1].index]; | |
| GCS::Arc& a2 = Arcs[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadius(a1, a2, tag); | |
| return ConstraintsCounter; | |
| } | |
| if (Geoms[geoId2].type == ArcOfEllipse) { | |
| if (Geoms[geoId1].type == ArcOfEllipse) { | |
| GCS::ArcOfEllipse& a1 = ArcsOfEllipse[Geoms[geoId1].index]; | |
| GCS::ArcOfEllipse& a2 = ArcsOfEllipse[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadii(a1, a2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| if (Geoms[geoId2].type == ArcOfHyperbola) { | |
| if (Geoms[geoId1].type == ArcOfHyperbola) { | |
| GCS::ArcOfHyperbola& a1 = ArcsOfHyperbola[Geoms[geoId1].index]; | |
| GCS::ArcOfHyperbola& a2 = ArcsOfHyperbola[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadii(a1, a2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| if (Geoms[geoId2].type == ArcOfParabola) { | |
| if (Geoms[geoId1].type == ArcOfParabola) { | |
| GCS::ArcOfParabola& a1 = ArcsOfParabola[Geoms[geoId1].index]; | |
| GCS::ArcOfParabola& a2 = ArcsOfParabola[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualFocus(a1, a2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| if (Geoms[geoId1].type == Ellipse) { | |
| GCS::Ellipse& e1 = Ellipses[Geoms[geoId1].index]; | |
| if (Geoms[geoId2].type == ArcOfEllipse) { | |
| GCS::ArcOfEllipse& a2 = ArcsOfEllipse[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintEqualRadii(a2, e1, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| Base::Console().warning( | |
| "Equality constraints between %s and %s are not supported.\n", | |
| nameByType(Geoms[geoId1].type), | |
| nameByType(Geoms[geoId2].type) | |
| ); | |
| return -1; | |
| } | |
| // point on object constraint | |
| int Sketch::addPointOnObjectConstraint(int geoId1, PointPos pos1, int geoId2, bool driving) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| if (Geoms[geoId2].type == Line) { | |
| GCS::Line& l2 = Lines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnLine(p1, l2, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnArc(p1, a, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnCircle(p1, c, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == Ellipse) { | |
| GCS::Ellipse& e = Ellipses[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnEllipse(p1, e, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == ArcOfEllipse) { | |
| GCS::ArcOfEllipse& a = ArcsOfEllipse[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnEllipse(p1, a, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == ArcOfHyperbola) { | |
| GCS::ArcOfHyperbola& a = ArcsOfHyperbola[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnHyperbolicArc(p1, a, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| else if (Geoms[geoId2].type == ArcOfParabola) { | |
| GCS::ArcOfParabola& a = ArcsOfParabola[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintPointOnParabolicArc(p1, a, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| return -1; | |
| } | |
| int Sketch::addPointOnObjectConstraint(int geoId1, PointPos pos1, int geoId2, double* pointparam, bool driving) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| if (Geoms[geoId2].type == BSpline) { | |
| GCS::BSpline& b = BSplines[Geoms[geoId2].index]; | |
| int tag = ++ConstraintsCounter; | |
| auto partBsp = static_cast<GeomBSplineCurve*>(Geoms[geoId2].geo); | |
| double uNear; | |
| partBsp->closestParameter(Base::Vector3d(*p1.x, *p1.y, 0.0), uNear); | |
| *pointparam = uNear; | |
| GCSsys.addConstraintPointOnBSpline(p1, b, pointparam, tag, driving); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| return -1; | |
| } | |
| // symmetric points constraint | |
| int Sketch::addSymmetricConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2, int geoId3) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| geoId3 = checkGeoId(geoId3); | |
| if (Geoms[geoId3].type != Line) { | |
| return -1; | |
| } | |
| // Special handling for arc endpoint symmetry when the arc's center is on the symmetry axis. | |
| // In this specific geometric configuration, the perpendicularity part of the symmetry | |
| // constraint is inherently redundant. Applying it would lead to solver errors. | |
| // We detect this case and add only the midpoint-on-line part of the constraint, | |
| // which is sufficient to enforce symmetry without causing redundancy. | |
| // Step 1: Check if the two points (p1, p2) are endpoints of the same arc. | |
| // We iterate through all geometries to find an arc whose start/end points match our input | |
| // points. | |
| int arcGeoId = -1; | |
| for (int i = 0; i < (int)Geoms.size(); ++i) { | |
| if (Geoms[i].type == Arc) { | |
| int arcStartPointId = Geoms[i].startPointId; | |
| int arcEndPointId = Geoms[i].endPointId; | |
| int p1_Id = getPointId(geoId1, pos1); | |
| int p2_Id = getPointId(geoId2, pos2); | |
| if ((p1_Id == arcStartPointId && p2_Id == arcEndPointId) | |
| || (p1_Id == arcEndPointId && p2_Id == arcStartPointId)) { | |
| arcGeoId = i; | |
| break; | |
| } | |
| } | |
| } | |
| if (arcGeoId != -1) { | |
| // Step 2: We found the arc. Now check if its center lies on the symmetry line. | |
| int centerPointId = Geoms[arcGeoId].midPointId; | |
| GCS::Point& center = Points[centerPointId]; | |
| GCS::Line& l = Lines[Geoms[geoId3].index]; // The symmetry line | |
| double dx = *l.p2.x - *l.p1.x; | |
| double dy = *l.p2.y - *l.p1.y; | |
| double line_len_sq = dx * dx + dy * dy; | |
| if (line_len_sq > Precision::SquareConfusion()) { | |
| double area = (*center.x - *l.p1.x) * dy - (*center.y - *l.p1.y) * dx; | |
| if (std::abs(area) / sqrt(line_len_sq) < Precision::Confusion()) { | |
| // The center IS on the symmetry line. This is the degenerate case. | |
| // Weaken the constraint by only adding the midpoint part. | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| int tag = ++ConstraintsCounter; | |
| // addConstraintMidpointOnLine is a GCS::System method, but we can call it from | |
| // here. | |
| GCSsys.addConstraintMidpointOnLine(p1, p2, l.p1, l.p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| } | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| GCS::Line& l = Lines[Geoms[geoId3].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PSymmetric(p1, p2, l, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addSymmetricConstraint( | |
| int geoId1, | |
| PointPos pos1, | |
| int geoId2, | |
| PointPos pos2, | |
| int geoId3, | |
| PointPos pos3 | |
| ) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| geoId3 = checkGeoId(geoId3); | |
| int pointId1 = getPointId(geoId1, pos1); | |
| int pointId2 = getPointId(geoId2, pos2); | |
| int pointId3 = getPointId(geoId3, pos3); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size()) && pointId3 >= 0 && pointId3 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| GCS::Point& p = Points[pointId3]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PSymmetric(p1, p2, p, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addSnellsLawConstraint( | |
| int geoIdRay1, | |
| PointPos posRay1, | |
| int geoIdRay2, | |
| PointPos posRay2, | |
| int geoIdBnd, | |
| double* value, | |
| double* secondvalue, | |
| bool driving | |
| ) | |
| { | |
| geoIdRay1 = checkGeoId(geoIdRay1); | |
| geoIdRay2 = checkGeoId(geoIdRay2); | |
| geoIdBnd = checkGeoId(geoIdBnd); | |
| if (Geoms[geoIdRay1].type == Point || Geoms[geoIdRay2].type == Point) { | |
| Base::Console().error("addSnellsLawConstraint: point is not a curve. Not applicable!\n"); | |
| return -1; | |
| } | |
| GCS::Curve* ray1 = getGCSCurveByGeoId(geoIdRay1); | |
| GCS::Curve* ray2 = getGCSCurveByGeoId(geoIdRay2); | |
| GCS::Curve* boundary = getGCSCurveByGeoId(geoIdBnd); | |
| if (!ray1 || !ray2 || !boundary) { | |
| Base::Console().error("addSnellsLawConstraint: getGCSCurveByGeoId returned NULL!\n"); | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoIdRay1, posRay1); | |
| int pointId2 = getPointId(geoIdRay2, posRay2); | |
| if (pointId1 < 0 || pointId1 >= int(Points.size()) || pointId2 < 0 | |
| || pointId2 >= int(Points.size())) { | |
| Base::Console().error("addSnellsLawConstraint: point index out of range.\n"); | |
| return -1; | |
| } | |
| GCS::Point& p1 = Points[pointId1]; | |
| // add the parameters (refractive indexes) | |
| // n1 uses the place hold by n2divn1, so that is retrievable in updateNonDrivingConstraints | |
| double* n1 = value; | |
| double* n2 = secondvalue; | |
| double n2divn1 = *value; | |
| if (fabs(n2divn1) >= 1.0) { | |
| *n2 = n2divn1; | |
| *n1 = 1.0; | |
| } | |
| else { | |
| *n2 = 1.0; | |
| *n1 = 1 / n2divn1; | |
| } | |
| int tag = -1; | |
| // increases ConstraintsCounter | |
| // tag = Sketch::addPointOnObjectConstraint(geoIdRay1, posRay1, geoIdBnd); | |
| tag = ++ConstraintsCounter; | |
| // GCSsys.addConstraintP2PCoincident(p1, p2, tag); | |
| GCSsys.addConstraintSnellsLaw( | |
| *ray1, | |
| *ray2, | |
| *boundary, | |
| p1, | |
| n1, | |
| n2, | |
| posRay1 == PointPos::start, | |
| posRay2 == PointPos::end, | |
| tag, | |
| driving | |
| ); | |
| return ConstraintsCounter; | |
| } | |
| int Sketch::addInternalAlignmentEllipseMajorDiameter(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Ellipse && Geoms[geoId1].type != ArcOfEllipse) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| int pointId2 = getPointId(geoId2, PointPos::end); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| if (Geoms[geoId1].type == Ellipse) { | |
| GCS::Ellipse& e1 = Ellipses[Geoms[geoId1].index]; | |
| // constraints | |
| // 1. start point with ellipse -a | |
| // 2. end point with ellipse +a | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseMajorDiameter(e1, p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| GCS::ArcOfEllipse& a1 = ArcsOfEllipse[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseMajorDiameter(a1, p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentEllipseMinorDiameter(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Ellipse && Geoms[geoId1].type != ArcOfEllipse) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| int pointId2 = getPointId(geoId2, PointPos::end); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| if (Geoms[geoId1].type == Ellipse) { | |
| GCS::Ellipse& e1 = Ellipses[Geoms[geoId1].index]; | |
| // constraints | |
| // 1. start point with ellipse -a | |
| // 2. end point with ellipse +a | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseMinorDiameter(e1, p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| GCS::ArcOfEllipse& a1 = ArcsOfEllipse[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseMinorDiameter(a1, p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentEllipseFocus1(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Ellipse && Geoms[geoId1].type != ArcOfEllipse) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Point) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| if (Geoms[geoId1].type == Ellipse) { | |
| GCS::Ellipse& e1 = Ellipses[Geoms[geoId1].index]; | |
| // constraints | |
| // 1. start point with ellipse -a | |
| // 2. end point with ellipse +a | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseFocus1(e1, p1, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| GCS::ArcOfEllipse& a1 = ArcsOfEllipse[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseFocus1(a1, p1, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentEllipseFocus2(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != Ellipse && Geoms[geoId1].type != ArcOfEllipse) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Point) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| if (Geoms[geoId1].type == Ellipse) { | |
| GCS::Ellipse& e1 = Ellipses[Geoms[geoId1].index]; | |
| // constraints | |
| // 1. start point with ellipse -a | |
| // 2. end point with ellipse +a | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseFocus2(e1, p1, tag); | |
| return ConstraintsCounter; | |
| } | |
| else { | |
| GCS::ArcOfEllipse& a1 = ArcsOfEllipse[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentEllipseFocus2(a1, p1, tag); | |
| return ConstraintsCounter; | |
| } | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentHyperbolaMajorDiameter(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != ArcOfHyperbola) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| int pointId2 = getPointId(geoId2, PointPos::end); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| GCS::ArcOfHyperbola& a1 = ArcsOfHyperbola[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentHyperbolaMajorDiameter(a1, p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentHyperbolaMinorDiameter(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != ArcOfHyperbola) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| int pointId2 = getPointId(geoId2, PointPos::end); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| GCS::ArcOfHyperbola& a1 = ArcsOfHyperbola[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentHyperbolaMinorDiameter(a1, p1, p2, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentHyperbolaFocus(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != ArcOfHyperbola) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Point) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::ArcOfHyperbola& a1 = ArcsOfHyperbola[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentHyperbolaFocus(a1, p1, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentParabolaFocus(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != ArcOfParabola) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Point) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::ArcOfParabola& a1 = ArcsOfParabola[Geoms[geoId1].index]; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentParabolaFocus(a1, p1, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentParabolaFocalDistance(int geoId1, int geoId2) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != ArcOfParabola) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Line) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| int pointId2 = getPointId(geoId2, PointPos::end); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size()) && pointId2 >= 0 | |
| && pointId2 < int(Points.size())) { | |
| GCS::Point& p1 = Points[pointId1]; | |
| GCS::Point& p2 = Points[pointId2]; | |
| GCS::ArcOfParabola& a1 = ArcsOfParabola[Geoms[geoId1].index]; | |
| auto& vertexpoint = a1.vertex; | |
| auto& focuspoint = a1.focus1; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintP2PCoincident(p1, vertexpoint, tag); | |
| GCSsys.addConstraintP2PCoincident(p2, focuspoint, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentBSplineControlPoint(int geoId1, int geoId2, int poleindex) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != BSpline) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Circle) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::mid); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Circle& c = Circles[Geoms[geoId2].index]; | |
| GCS::BSpline& b = BSplines[Geoms[geoId1].index]; | |
| assert(poleindex < static_cast<int>(b.poles.size()) && poleindex >= 0); | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentBSplineControlPoint(b, c, poleindex, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| int Sketch::addInternalAlignmentKnotPoint(int geoId1, int geoId2, int knotindex) | |
| { | |
| std::swap(geoId1, geoId2); | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| if (Geoms[geoId1].type != BSpline) { | |
| return -1; | |
| } | |
| if (Geoms[geoId2].type != Point) { | |
| return -1; | |
| } | |
| int pointId1 = getPointId(geoId2, PointPos::start); | |
| if (pointId1 >= 0 && pointId1 < int(Points.size())) { | |
| GCS::Point& p = Points[pointId1]; | |
| GCS::BSpline& b = BSplines[Geoms[geoId1].index]; | |
| assert(knotindex < static_cast<int>(b.knots.size()) && knotindex >= 0); | |
| b.knotpointGeoids[knotindex] = geoId2; | |
| int tag = ++ConstraintsCounter; | |
| GCSsys.addConstraintInternalAlignmentKnotPoint(b, p, knotindex, tag); | |
| return ConstraintsCounter; | |
| } | |
| return -1; | |
| } | |
| double Sketch::calculateAngleViaPoint(int geoId1, int geoId2, double px, double py) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| GCS::Point p; | |
| p.x = &px; | |
| p.y = &py; | |
| // check pointers | |
| GCS::Curve* crv1 = getGCSCurveByGeoId(geoId1); | |
| GCS::Curve* crv2 = getGCSCurveByGeoId(geoId2); | |
| if (!crv1 || !crv2) { | |
| throw Base::ValueError("calculateAngleViaPoint: getGCSCurveByGeoId returned NULL!"); | |
| } | |
| return GCSsys.calculateAngleViaPoint(*crv1, *crv2, p); | |
| } | |
| double Sketch::calculateAngleViaParams(int geoId1, int geoId2, double param1, double param2) | |
| { | |
| geoId1 = checkGeoId(geoId1); | |
| geoId2 = checkGeoId(geoId2); | |
| // check pointers | |
| GCS::Curve* crv1 = getGCSCurveByGeoId(geoId1); | |
| GCS::Curve* crv2 = getGCSCurveByGeoId(geoId2); | |
| if (!crv1 || !crv2) { | |
| throw Base::ValueError("calculateAngleViaPoint: getGCSCurveByGeoId returned NULL!"); | |
| } | |
| // FIXME: This should probably not be needed | |
| auto* crv1AsBSpline = dynamic_cast<GCS::BSpline*>(crv1); | |
| if (crv1AsBSpline && crv1AsBSpline->flattenedknots.empty()) { | |
| crv1AsBSpline->setupFlattenedKnots(); | |
| } | |
| auto* crv2AsBSpline = dynamic_cast<GCS::BSpline*>(crv2); | |
| if (crv2AsBSpline && crv2AsBSpline->flattenedknots.empty()) { | |
| crv2AsBSpline->setupFlattenedKnots(); | |
| } | |
| return GCSsys.calculateAngleViaParams(*crv1, *crv2, ¶m1, ¶m2); | |
| } | |
| Base::Vector3d Sketch::calculateNormalAtPoint(int geoIdCurve, double px, double py) const | |
| { | |
| geoIdCurve = checkGeoId(geoIdCurve); | |
| GCS::Point p; | |
| p.x = &px; | |
| p.y = &py; | |
| // check pointers | |
| const GCS::Curve* crv = getGCSCurveByGeoId(geoIdCurve); | |
| if (!crv) { | |
| throw Base::ValueError("calculateNormalAtPoint: getGCSCurveByGeoId returned NULL!\n"); | |
| } | |
| double tx = 0.0, ty = 0.0; | |
| GCSsys.calculateNormalAtPoint(*crv, p, tx, ty); | |
| return Base::Vector3d(tx, ty, 0.0); | |
| } | |
| bool Sketch::updateGeometry() | |
| { | |
| int i = 0; | |
| for (const GeoDef& it : Geoms) { | |
| try { | |
| updateGeometry(it); | |
| ++i; | |
| } | |
| catch (Base::Exception& e) { | |
| Base::Console().error("Updating geometry: Error build geometry(%d): %s\n", i, e.what()); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| void Sketch::tryUpdateGeometry() | |
| { | |
| for (const GeoDef& it : Geoms) { | |
| updateGeometry(it); | |
| } | |
| } | |
| void Sketch::updateGeometry(const GeoDef& it) | |
| { | |
| if (it.type == Point) { | |
| updatePoint(it); | |
| } | |
| else if (it.type == Line) { | |
| updateLineSegment(it); | |
| } | |
| else if (it.type == Arc) { | |
| updateArcOfCircle(it); | |
| } | |
| else if (it.type == ArcOfEllipse) { | |
| updateArcOfEllipse(it); | |
| } | |
| else if (it.type == Circle) { | |
| updateCircle(it); | |
| } | |
| else if (it.type == Ellipse) { | |
| updateEllipse(it); | |
| } | |
| else if (it.type == ArcOfHyperbola) { | |
| updateArcOfHyperbola(it); | |
| } | |
| else if (it.type == ArcOfParabola) { | |
| updateArcOfParabola(it); | |
| } | |
| else if (it.type == BSpline) { | |
| updateBSpline(it); | |
| } | |
| } | |
| void Sketch::updatePoint(const GeoDef& def) | |
| { | |
| GeomPoint* point = static_cast<GeomPoint*>(def.geo); | |
| auto pointf = GeometryFacade::getFacade(point); | |
| point->setPoint(Vector3d(*Points[def.startPointId].x, *Points[def.startPointId].y, 0.0)); | |
| } | |
| void Sketch::updateLineSegment(const GeoDef& def) | |
| { | |
| GeomLineSegment* lineSeg = static_cast<GeomLineSegment*>(def.geo); | |
| lineSeg->setPoints( | |
| Vector3d(*Lines[def.index].p1.x, *Lines[def.index].p1.y, 0.0), | |
| Vector3d(*Lines[def.index].p2.x, *Lines[def.index].p2.y, 0.0) | |
| ); | |
| } | |
| void Sketch::updateArcOfCircle(const GeoDef& def) | |
| { | |
| GCS::Arc& myArc = Arcs[def.index]; | |
| // the following 4 lines are redundant since these equations are already included in | |
| // the arc constraints *myArc.start.x = *myArc.center.x + *myArc.rad * | |
| // cos(*myArc.startAngle); *myArc.start.y = *myArc.center.y + *myArc.rad * | |
| // sin(*myArc.startAngle); *myArc.end.x = *myArc.center.x + *myArc.rad * | |
| // cos(*myArc.endAngle); *myArc.end.y = *myArc.center.y + *myArc.rad * | |
| // sin(*myArc.endAngle); | |
| GeomArcOfCircle* aoc = static_cast<GeomArcOfCircle*>(def.geo); | |
| aoc->setCenter(Vector3d(*Points[def.midPointId].x, *Points[def.midPointId].y, 0.0)); | |
| aoc->setRadius(*myArc.rad); | |
| aoc->setRange(*myArc.startAngle, *myArc.endAngle, /*emulateCCWXY=*/true); | |
| } | |
| void Sketch::updateArcOfEllipse(const GeoDef& def) | |
| { | |
| GCS::ArcOfEllipse& myArc = ArcsOfEllipse[def.index]; | |
| GeomArcOfEllipse* aoe = static_cast<GeomArcOfEllipse*>(def.geo); | |
| Base::Vector3d center = Vector3d(*Points[def.midPointId].x, *Points[def.midPointId].y, 0.0); | |
| Base::Vector3d f1 = Vector3d(*myArc.focus1.x, *myArc.focus1.y, 0.0); | |
| double radmin = *myArc.radmin; | |
| Base::Vector3d fd = f1 - center; | |
| double radmaj = sqrt(fd * fd + radmin * radmin); | |
| aoe->setCenter(center); | |
| // ensure that ellipse's major radius is always larger than minor radius... may | |
| // still cause problems with degenerates. | |
| if (radmaj >= aoe->getMinorRadius()) { | |
| aoe->setMajorRadius(radmaj); | |
| aoe->setMinorRadius(radmin); | |
| } | |
| else { | |
| aoe->setMinorRadius(radmin); | |
| aoe->setMajorRadius(radmaj); | |
| } | |
| aoe->setMajorAxisDir(fd); | |
| aoe->setRange(*myArc.startAngle, *myArc.endAngle, /*emulateCCWXY=*/true); | |
| } | |
| void Sketch::updateArcOfHyperbola(const GeoDef& def) | |
| { | |
| GCS::ArcOfHyperbola& myArc = ArcsOfHyperbola[def.index]; | |
| GeomArcOfHyperbola* aoh = static_cast<GeomArcOfHyperbola*>(def.geo); | |
| Base::Vector3d center = Vector3d(*Points[def.midPointId].x, *Points[def.midPointId].y, 0.0); | |
| Base::Vector3d f1 = Vector3d(*myArc.focus1.x, *myArc.focus1.y, 0.0); | |
| double radmin = *myArc.radmin; | |
| Base::Vector3d fd = f1 - center; | |
| double radmaj = sqrt(fd * fd - radmin * radmin); | |
| aoh->setCenter(center); | |
| if (radmaj >= aoh->getMinorRadius()) { | |
| aoh->setMajorRadius(radmaj); | |
| aoh->setMinorRadius(radmin); | |
| } | |
| else { | |
| aoh->setMinorRadius(radmin); | |
| aoh->setMajorRadius(radmaj); | |
| } | |
| aoh->setMajorAxisDir(fd); | |
| aoh->setRange(*myArc.startAngle, *myArc.endAngle, /*emulateCCWXY=*/true); | |
| } | |
| void Sketch::updateArcOfParabola(const GeoDef& def) | |
| { | |
| GCS::ArcOfParabola& myArc = ArcsOfParabola[def.index]; | |
| GeomArcOfParabola* aop = static_cast<GeomArcOfParabola*>(def.geo); | |
| Base::Vector3d vertex = Vector3d(*Points[def.midPointId].x, *Points[def.midPointId].y, 0.0); | |
| Base::Vector3d f1 = Vector3d(*myArc.focus1.x, *myArc.focus1.y, 0.0); | |
| Base::Vector3d fd = f1 - vertex; | |
| aop->setXAxisDir(fd); | |
| aop->setCenter(vertex); | |
| aop->setFocal(fd.Length()); | |
| aop->setRange(*myArc.startAngle, *myArc.endAngle, /*emulateCCWXY=*/true); | |
| } | |
| void Sketch::updateCircle(const GeoDef& def) | |
| { | |
| GeomCircle* circ = static_cast<GeomCircle*>(def.geo); | |
| circ->setCenter(Vector3d(*Points[def.midPointId].x, *Points[def.midPointId].y, 0.0)); | |
| circ->setRadius(*Circles[def.index].rad); | |
| } | |
| void Sketch::updateEllipse(const GeoDef& def) | |
| { | |
| GeomEllipse* ellipse = static_cast<GeomEllipse*>(def.geo); | |
| Base::Vector3d center = Vector3d(*Points[def.midPointId].x, *Points[def.midPointId].y, 0.0); | |
| Base::Vector3d f1 = Vector3d(*Ellipses[def.index].focus1.x, *Ellipses[def.index].focus1.y, 0.0); | |
| double radmin = *Ellipses[def.index].radmin; | |
| Base::Vector3d fd = f1 - center; | |
| double radmaj = sqrt(fd * fd + radmin * radmin); | |
| ellipse->setCenter(center); | |
| // ensure that ellipse's major radius is always larger than minor radius... may | |
| // still cause problems with degenerates. | |
| if (radmaj >= ellipse->getMinorRadius()) { | |
| ellipse->setMajorRadius(radmaj); | |
| ellipse->setMinorRadius(radmin); | |
| } | |
| else { | |
| ellipse->setMinorRadius(radmin); | |
| ellipse->setMajorRadius(radmaj); | |
| } | |
| ellipse->setMajorAxisDir(fd); | |
| } | |
| void Sketch::updateBSpline(const GeoDef& def) | |
| { | |
| GCS::BSpline& mybsp = BSplines[def.index]; | |
| GeomBSplineCurve* bsp = static_cast<GeomBSplineCurve*>(def.geo); | |
| std::vector<Base::Vector3d> poles; | |
| std::vector<double> weights; | |
| std::vector<GCS::Point>::const_iterator it1; | |
| std::vector<double*>::const_iterator it2; | |
| for (it1 = mybsp.poles.begin(), it2 = mybsp.weights.begin(); | |
| it1 != mybsp.poles.end() && it2 != mybsp.weights.end(); | |
| ++it1, ++it2) { | |
| poles.emplace_back(*(*it1).x, *(*it1).y, 0.0); | |
| weights.push_back(*(*it2)); | |
| } | |
| bsp->setPoles(poles, weights); | |
| std::vector<double> knots; | |
| std::vector<int> mult; | |
| // This is the code that should be here when/if b-spline gets its full | |
| // implementation in the solver. | |
| /*std::vector<double *>::const_iterator it3; | |
| std::vector<int>::const_iterator it4; | |
| for( it3 = mybsp.knots.begin(), it4 = mybsp.mult.begin(); it3 != mybsp.knots.end() | |
| && it4 != mybsp.mult.end(); ++it3, ++it4) { knots.push_back(*(*it3)); | |
| mult.push_back((*it4)); | |
| } | |
| bsp->setKnots(knots,mult);*/ | |
| } | |
| bool Sketch::updateNonDrivingConstraints() | |
| { | |
| for (auto& constrDef : Constrs) { | |
| if (constrDef.driving) { | |
| continue; | |
| } | |
| if (constrDef.constr->Type == SnellsLaw) { | |
| double n1 = *(constrDef.value); | |
| double n2 = *(constrDef.secondvalue); | |
| constrDef.constr->setValue(n2 / n1); | |
| } | |
| else if (constrDef.constr->Type == Angle) { | |
| constrDef.constr->setValue(std::fmod(*(constrDef.value), 2.0 * std::numbers::pi)); | |
| } | |
| else if (constrDef.constr->Type == Diameter && constrDef.constr->First >= 0) { | |
| // two cases, the geometry parameter is fixed or it is not | |
| // NOTE: This is different from being blocked, as new block constraint may fix | |
| // the parameter or not depending on whether other driving constraints are present | |
| int geoId = constrDef.constr->First; | |
| geoId = checkGeoId(geoId); | |
| double* rad = nullptr; | |
| if (Geoms[geoId].type == Circle) { | |
| GCS::Circle& c = Circles[Geoms[geoId].index]; | |
| rad = c.rad; | |
| } | |
| else if (Geoms[geoId].type == Arc) { | |
| GCS::Arc& a = Arcs[Geoms[geoId].index]; | |
| rad = a.rad; | |
| } | |
| if (auto pos = std::ranges::find(FixParameters, rad); pos != FixParameters.end()) { | |
| constrDef.constr->setValue(*(constrDef.value)); | |
| } | |
| else { | |
| constrDef.constr->setValue(2.0 * *(constrDef.value)); | |
| } | |
| } | |
| else { | |
| constrDef.constr->setValue(*(constrDef.value)); | |
| } | |
| } | |
| return true; | |
| } | |
| // solving ========================================================== | |
| int Sketch::solve() | |
| { | |
| Base::TimeElapsed start_time; | |
| std::string solvername; | |
| auto result = internalSolve(solvername); | |
| Base::TimeElapsed end_time; | |
| if (debugMode == GCS::Minimal || debugMode == GCS::IterationLevel) { | |
| Base::Console().log( | |
| "Sketcher::Solve()-%s-T:%s\n", | |
| solvername.c_str(), | |
| Base::TimeElapsed::diffTime(start_time, end_time).c_str() | |
| ); | |
| } | |
| SolveTime = Base::TimeElapsed::diffTimeF(start_time, end_time); | |
| return result; | |
| } | |
| int Sketch::internalSolve(std::string& solvername, int level) | |
| { | |
| if (!isInitMove) { // make sure we are in single subsystem mode | |
| clearTemporaryConstraints(); | |
| isFine = true; | |
| } | |
| int ret = -1; | |
| bool valid_solution; | |
| int defaultsoltype = -1; | |
| if (isInitMove) { | |
| solvername = "DogLeg"; // DogLeg is used for dragging (same as before) | |
| ret = GCSsys.solve(isFine, GCS::DogLeg); | |
| } | |
| else { | |
| switch (defaultSolver) { | |
| case 0: | |
| solvername = "BFGS"; | |
| ret = GCSsys.solve(isFine, GCS::BFGS); | |
| defaultsoltype = 2; | |
| break; | |
| case 1: // solving with the LevenbergMarquardt solver | |
| solvername = "LevenbergMarquardt"; | |
| ret = GCSsys.solve(isFine, GCS::LevenbergMarquardt); | |
| defaultsoltype = 1; | |
| break; | |
| case 2: // solving with the BFGS solver | |
| solvername = "DogLeg"; | |
| ret = GCSsys.solve(isFine, GCS::DogLeg); | |
| defaultsoltype = 0; | |
| break; | |
| } | |
| } | |
| // if successfully solved try to write the parameters back | |
| if (ret == GCS::Success) { | |
| GCSsys.applySolution(); | |
| valid_solution = updateGeometry(); | |
| if (!valid_solution) { | |
| GCSsys.undoSolution(); | |
| updateGeometry(); | |
| Base::Console().warning("Invalid solution from %s solver.\n", solvername.c_str()); | |
| } | |
| else { | |
| updateNonDrivingConstraints(); | |
| } | |
| } | |
| else { | |
| valid_solution = false; | |
| if (debugMode == GCS::Minimal || debugMode == GCS::IterationLevel) { | |
| Base::Console().log("Sketcher::Solve()-%s- Failed!! Falling back...\n", solvername.c_str()); | |
| } | |
| } | |
| if (!valid_solution && !isInitMove) { // Fall back to other solvers | |
| for (int soltype = 0; soltype < 4; soltype++) { | |
| if (soltype == defaultsoltype) { | |
| continue; // skip default solver | |
| } | |
| switch (soltype) { | |
| case 0: | |
| solvername = "DogLeg"; | |
| ret = GCSsys.solve(isFine, GCS::DogLeg); | |
| break; | |
| case 1: // solving with the LevenbergMarquardt solver | |
| solvername = "LevenbergMarquardt"; | |
| ret = GCSsys.solve(isFine, GCS::LevenbergMarquardt); | |
| break; | |
| case 2: // solving with the BFGS solver | |
| solvername = "BFGS"; | |
| ret = GCSsys.solve(isFine, GCS::BFGS); | |
| break; | |
| // last resort: augment the system with a second subsystem and use the SQP solver | |
| case 3: | |
| solvername = "SQP(augmented system)"; | |
| InitParameters.resize(Parameters.size()); | |
| int i = 0; | |
| for (auto it = Parameters.begin(); it != Parameters.end(); ++it, ++i) { | |
| InitParameters[i] = **it; | |
| GCSsys.addConstraintEqual( | |
| *it, | |
| &InitParameters[i], | |
| GCS::DefaultTemporaryConstraint | |
| ); | |
| } | |
| GCSsys.initSolution(); | |
| ret = GCSsys.solve(isFine); | |
| break; | |
| } | |
| // if successfully solved try to write the parameters back | |
| if (ret == GCS::Success) { | |
| GCSsys.applySolution(); | |
| valid_solution = updateGeometry(); | |
| if (!valid_solution) { | |
| GCSsys.undoSolution(); | |
| updateGeometry(); | |
| Base::Console().warning("Invalid solution from %s solver.\n", solvername.c_str()); | |
| ret = GCS::SuccessfulSolutionInvalid; | |
| } | |
| else { | |
| updateNonDrivingConstraints(); | |
| } | |
| } | |
| else { | |
| valid_solution = false; | |
| if (debugMode == GCS::Minimal || debugMode == GCS::IterationLevel) { | |
| Base::Console().log( | |
| "Sketcher::Solve()-%s- Failed!! Falling back...\n", | |
| solvername.c_str() | |
| ); | |
| } | |
| } | |
| if (soltype == 3) { // cleanup temporary constraints of the augmented system | |
| clearTemporaryConstraints(); | |
| } | |
| if (valid_solution) { | |
| if (soltype == 1) { | |
| Base::Console().log( | |
| "Important: the LevenbergMarquardt solver succeeded where " | |
| "the DogLeg solver had failed.\n" | |
| ); | |
| } | |
| else if (soltype == 2) { | |
| Base::Console().log( | |
| "Important: the BFGS solver succeeded where the DogLeg and " | |
| "LevenbergMarquardt solvers have failed.\n" | |
| ); | |
| } | |
| else if (soltype == 3) { | |
| Base::Console().log( | |
| "Important: the SQP solver succeeded where all single " | |
| "subsystem solvers have failed.\n" | |
| ); | |
| } | |
| else if (soltype > 0) { | |
| Base::Console().log("All solvers failed.\n"); | |
| } | |
| break; | |
| } | |
| } // soltype | |
| } | |
| // For OCCT reliant geometry that needs an extra solve() for example to update non-driving | |
| // constraints. | |
| if (resolveAfterGeometryUpdated && ret == GCS::Success && level == 0) { | |
| return internalSolve(solvername, 1); | |
| } | |
| return ret; | |
| } | |
| int Sketch::initMove(const std::vector<GeoElementId>& geoEltIds, bool fine) | |
| { | |
| if (hasConflicts()) { | |
| // don't try to move sketches that contain conflicting constraints | |
| isInitMove = false; | |
| return -1; | |
| } | |
| isFine = fine; | |
| clearTemporaryConstraints(); | |
| MoveParameters.clear(); | |
| // We need to reserve enough size in the vec or the dynamic resizing | |
| // (emplace_back in the for loop below) will trigger reallocation. | |
| // Which will corrupt pointers we're storing. | |
| size_t reserveSize = 0; | |
| for (auto& pair : geoEltIds) { | |
| int geoId = checkGeoId(pair.GeoId); | |
| Sketcher::PointPos pos = pair.Pos; | |
| if (Geoms[geoId].type == BSpline && (pos == PointPos::none || pos == PointPos::mid)) { | |
| GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; | |
| reserveSize += bsp.poles.size() * 2; | |
| } | |
| else { | |
| reserveSize += 6; // 6 is the max for all other cases. | |
| } | |
| } | |
| MoveParameters.reserve(reserveSize); | |
| for (auto& pair : geoEltIds) { | |
| int geoId = checkGeoId(pair.GeoId); | |
| Sketcher::PointPos pos = pair.Pos; | |
| if (Geoms[geoId].type == Point) { | |
| if (pos == PointPos::start) { | |
| GCS::Point& point = Points[Geoms[geoId].startPointId]; | |
| GCS::Point p0; | |
| p0.x = &MoveParameters.emplace_back(*point.x); | |
| p0.y = &MoveParameters.emplace_back(*point.y); | |
| GCSsys.addConstraintP2PCoincident(p0, point, GCS::DefaultTemporaryConstraint); | |
| } | |
| } | |
| else if (Geoms[geoId].type == Line) { | |
| if (pos == PointPos::start || pos == PointPos::end) { | |
| GCS::Point p0; | |
| GCS::Point& p = pos == PointPos::start ? Points[Geoms[geoId].startPointId] | |
| : Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*p.x); | |
| p0.y = &MoveParameters.emplace_back(*p.y); | |
| GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::none || pos == PointPos::mid) { | |
| GCS::Point p1, p2; | |
| GCS::Line& l = Lines[Geoms[geoId].index]; | |
| p1.x = &MoveParameters.emplace_back(*l.p1.x); | |
| p1.y = &MoveParameters.emplace_back(*l.p1.y); | |
| p2.x = &MoveParameters.emplace_back(*l.p2.x); | |
| p2.y = &MoveParameters.emplace_back(*l.p2.y); | |
| GCSsys.addConstraintP2PCoincident(p1, l.p1, GCS::DefaultTemporaryConstraint); | |
| GCSsys.addConstraintP2PCoincident(p2, l.p2, GCS::DefaultTemporaryConstraint); | |
| } | |
| } | |
| else if (Geoms[geoId].type == Circle) { | |
| GCS::Point& center = Points[Geoms[geoId].midPointId]; | |
| GCS::Point p0, p1; | |
| if (pos == PointPos::mid) { | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y); | |
| GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::none) { | |
| // bool pole = GeometryFacade::isInternalType(Geoms[geoId].geo, | |
| // InternalType::BSplineControlPoint); | |
| GCS::Circle& c = Circles[Geoms[geoId].index]; | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y + *c.rad); | |
| GCSsys.addConstraintPointOnCircle(p0, c, GCS::DefaultTemporaryConstraint); | |
| p1.x = &MoveParameters.emplace_back(*center.x); | |
| p1.y = &MoveParameters.emplace_back(*center.y); | |
| int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); | |
| GCSsys.rescaleConstraint(i - 1, 0.01); | |
| GCSsys.rescaleConstraint(i, 0.01); | |
| } | |
| } | |
| else if (Geoms[geoId].type == Ellipse) { | |
| if (pos == PointPos::mid || pos == PointPos::none) { | |
| GCS::Point& center = Points[Geoms[geoId].midPointId]; | |
| GCS::Point p0; | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y); | |
| GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); | |
| } | |
| } | |
| else if (Geoms[geoId].type == ArcOfEllipse) { | |
| GCS::Point& center = Points[Geoms[geoId].midPointId]; | |
| GCS::Point p0, p1; | |
| if (pos == PointPos::mid || pos == PointPos::none) { | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y); | |
| GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::start || pos == PointPos::end) { | |
| if (pos == PointPos::start || pos == PointPos::end) { | |
| GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] | |
| : Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*p.x); | |
| p0.y = &MoveParameters.emplace_back(*p.y); | |
| GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); | |
| } | |
| p1.x = &MoveParameters.emplace_back(*center.x); | |
| p1.y = &MoveParameters.emplace_back(*center.y); | |
| int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); | |
| GCSsys.rescaleConstraint(i - 1, 0.01); | |
| GCSsys.rescaleConstraint(i, 0.01); | |
| } | |
| } | |
| else if (Geoms[geoId].type == ArcOfHyperbola) { | |
| GCS::Point& center = Points[Geoms[geoId].midPointId]; | |
| GCS::Point p0, p1; | |
| if (pos == PointPos::mid || pos == PointPos::none) { | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y); | |
| GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::start || pos == PointPos::end) { | |
| GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] | |
| : Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*p.x); | |
| p0.y = &MoveParameters.emplace_back(*p.y); | |
| GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); | |
| p1.x = &MoveParameters.emplace_back(*center.x); | |
| p1.y = &MoveParameters.emplace_back(*center.y); | |
| int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); | |
| GCSsys.rescaleConstraint(i - 1, 0.01); | |
| GCSsys.rescaleConstraint(i, 0.01); | |
| } | |
| } | |
| else if (Geoms[geoId].type == ArcOfParabola) { | |
| GCS::Point& center = Points[Geoms[geoId].midPointId]; | |
| GCS::Point p0, p1; | |
| if (pos == PointPos::mid || pos == PointPos::none) { | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y); | |
| GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::start || pos == PointPos::end) { | |
| GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] | |
| : Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*p.x); | |
| p0.y = &MoveParameters.emplace_back(*p.y); | |
| GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); | |
| p1.x = &MoveParameters.emplace_back(*center.x); | |
| p1.y = &MoveParameters.emplace_back(*center.y); | |
| int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); | |
| GCSsys.rescaleConstraint(i - 1, 0.01); | |
| GCSsys.rescaleConstraint(i, 0.01); | |
| } | |
| } | |
| else if (Geoms[geoId].type == BSpline) { | |
| if (pos == PointPos::start || pos == PointPos::end) { | |
| GCS::Point p0; | |
| GCS::Point& p = pos == PointPos::start ? Points[Geoms[geoId].startPointId] | |
| : Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*p.x); | |
| p0.y = &MoveParameters.emplace_back(*p.y); | |
| GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::none || pos == PointPos::mid) { | |
| GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; | |
| for (auto pole : bsp.poles) { | |
| GCS::Point p1; | |
| p1.x = &MoveParameters.emplace_back(*pole.x); | |
| p1.y = &MoveParameters.emplace_back(*pole.y); | |
| GCSsys.addConstraintP2PCoincident(p1, pole, GCS::DefaultTemporaryConstraint); | |
| } | |
| } | |
| } | |
| else if (Geoms[geoId].type == Arc) { | |
| GCS::Point& center = Points[Geoms[geoId].midPointId]; | |
| GCS::Point p0, p1; | |
| if (pos == PointPos::mid) { | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y); | |
| GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::none && geoEltIds.size() > 1) { | |
| // When group dragging, arcs should move without modification. | |
| GCS::Point p2; | |
| GCS::Point& sp = Points[Geoms[geoId].startPointId]; | |
| GCS::Point& ep = Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*sp.x); | |
| p0.y = &MoveParameters.emplace_back(*sp.y); | |
| GCSsys.addConstraintP2PCoincident(p0, sp, GCS::DefaultTemporaryConstraint); | |
| p2.x = &MoveParameters.emplace_back(*ep.x); | |
| p2.y = &MoveParameters.emplace_back(*ep.y); | |
| GCSsys.addConstraintP2PCoincident(p2, ep, GCS::DefaultTemporaryConstraint); | |
| p1.x = &MoveParameters.emplace_back(*center.x); | |
| p1.y = &MoveParameters.emplace_back(*center.y); | |
| int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); | |
| GCSsys.rescaleConstraint(i - 2, 0.01); | |
| GCSsys.rescaleConstraint(i - 1, 0.01); | |
| GCSsys.rescaleConstraint(i, 0.01); | |
| } | |
| else if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::none) { | |
| if (pos == PointPos::start || pos == PointPos::end) { | |
| GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] | |
| : Points[Geoms[geoId].endPointId]; | |
| p0.x = &MoveParameters.emplace_back(*p.x); | |
| p0.y = &MoveParameters.emplace_back(*p.y); | |
| GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); | |
| } | |
| else if (pos == PointPos::none) { | |
| GCS::Arc& a = Arcs[Geoms[geoId].index]; | |
| p0.x = &MoveParameters.emplace_back(*center.x); | |
| p0.y = &MoveParameters.emplace_back(*center.y + *a.rad); | |
| GCSsys.addConstraintPointOnArc(p0, a, GCS::DefaultTemporaryConstraint); | |
| } | |
| p1.x = &MoveParameters.emplace_back(*center.x); | |
| p1.y = &MoveParameters.emplace_back(*center.y); | |
| int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); | |
| GCSsys.rescaleConstraint(i - 1, 0.01); | |
| GCSsys.rescaleConstraint(i, 0.01); | |
| } | |
| } | |
| } | |
| InitParameters = MoveParameters; | |
| GCSsys.initSolution(); | |
| isInitMove = true; | |
| return 0; | |
| } | |
| int Sketch::initMove(int geoId, PointPos pos, bool fine) | |
| { | |
| std::vector<GeoElementId> geoEltIds = {GeoElementId(geoId, pos)}; | |
| return initMove(geoEltIds, fine); | |
| } | |
| void Sketch::resetInitMove() | |
| { | |
| isInitMove = false; | |
| } | |
| int Sketch::initBSplinePieceMove(int geoId, PointPos pos, const Base::Vector3d& firstPoint, bool fine) | |
| { | |
| isFine = fine; | |
| geoId = checkGeoId(geoId); | |
| clearTemporaryConstraints(); | |
| // don't try to move sketches that contain conflicting constraints | |
| if (hasConflicts()) { | |
| isInitMove = false; | |
| return -1; | |
| } | |
| // this is only meant for B-Splines | |
| if (Geoms[geoId].type != BSpline || pos == PointPos::start || pos == PointPos::end) { | |
| return -1; | |
| } | |
| GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; | |
| // If spline has too few poles, just move all | |
| if (bsp.poles.size() <= std::size_t(bsp.degree + 1)) { | |
| return initMove(geoId, pos, fine); | |
| } | |
| // Find the closest knot | |
| auto partBsp = static_cast<GeomBSplineCurve*>(Geoms[geoId].geo); | |
| double uNear; | |
| partBsp->closestParameter(firstPoint, uNear); | |
| auto& knots = bsp.knots; | |
| auto upperknot = std::upper_bound(knots.begin(), knots.end(), uNear, [](double u, double* element) { | |
| return u < *element; | |
| }); | |
| size_t idx = 0; | |
| // skipping the first knot for adjustment | |
| // TODO: ensure this works for periodic as well | |
| for (size_t i = 1; i < bsp.mult.size() && knots[i] != *upperknot; ++i) { | |
| idx += bsp.mult[i]; | |
| } | |
| MoveParameters.resize(2 * (bsp.degree + 1)); // x[idx],y[idx],x[idx+1],y[idx+1],... | |
| size_t mvindex = 0; | |
| auto lastIt = (idx + bsp.degree + 1) % bsp.poles.size(); | |
| for (size_t i = idx; i != lastIt; i = (i + 1) % bsp.poles.size(), ++mvindex) { | |
| GCS::Point p1; | |
| p1.x = &MoveParameters[mvindex]; | |
| ++mvindex; | |
| p1.y = &MoveParameters[mvindex]; | |
| *p1.x = *bsp.poles[i].x; | |
| *p1.y = *bsp.poles[i].y; | |
| GCSsys.addConstraintP2PCoincident(p1, bsp.poles[i], GCS::DefaultTemporaryConstraint); | |
| } | |
| InitParameters = MoveParameters; | |
| GCSsys.initSolution(); | |
| isInitMove = true; | |
| return 0; | |
| } | |
| int Sketch::moveGeometries(const std::vector<GeoElementId>& geoEltIds, Base::Vector3d toPoint, bool relative) | |
| { | |
| if (hasConflicts()) { | |
| // don't try to move sketches that contain conflicting constraints | |
| return -1; | |
| } | |
| if (!isInitMove) { | |
| initMove(geoEltIds); | |
| initToPoint = toPoint; | |
| moveStep = 0; | |
| } | |
| else { | |
| if (!relative && RecalculateInitialSolutionWhileMovingPoint) { | |
| if (moveStep == 0) { | |
| moveStep = (toPoint - initToPoint).Length(); | |
| } | |
| else { | |
| // I am getting too far away from the original solution so reinit the solution | |
| if ((toPoint - initToPoint).Length() > 20 * moveStep) { | |
| initMove(geoEltIds); | |
| initToPoint = toPoint; | |
| } | |
| } | |
| } | |
| } | |
| if (relative) { | |
| for (size_t i = 0; i < MoveParameters.size() - 1; i += 2) { | |
| MoveParameters[i] = InitParameters[i] + toPoint.x; | |
| MoveParameters[i + 1] = InitParameters[i + 1] + toPoint.y; | |
| } | |
| } | |
| else { | |
| size_t i = 0; | |
| for (auto& pair : geoEltIds) { | |
| if (i >= MoveParameters.size()) { | |
| break; | |
| } | |
| int geoId = checkGeoId(pair.GeoId); | |
| Sketcher::PointPos pos = pair.Pos; | |
| if (Geoms[geoId].type == Point) { | |
| if (pos == PointPos::start) { | |
| MoveParameters[i] = toPoint.x; | |
| MoveParameters[i + 1] = toPoint.y; | |
| i += 2; | |
| } | |
| } | |
| else if (Geoms[geoId].type == Line) { | |
| if (pos == PointPos::start || pos == PointPos::end) { | |
| MoveParameters[i] = toPoint.x; | |
| MoveParameters[i + 1] = toPoint.y; | |
| i += 2; | |
| } | |
| else if (pos == PointPos::none || pos == PointPos::mid) { | |
| double dx = (InitParameters[i + 2] - InitParameters[i]) * 0.5; | |
| double dy = (InitParameters[i + 3] - InitParameters[i + 1]) * 0.5; | |
| MoveParameters[i] = toPoint.x - dx; | |
| MoveParameters[i + 1] = toPoint.y - dy; | |
| MoveParameters[i + 2] = toPoint.x + dx; | |
| MoveParameters[i + 3] = toPoint.y + dy; | |
| i += 4; | |
| } | |
| } | |
| else if (Geoms[geoId].type == Circle || Geoms[geoId].type == Ellipse) { | |
| if (pos == PointPos::mid || pos == PointPos::none) { | |
| MoveParameters[i] = toPoint.x; | |
| MoveParameters[i + 1] = toPoint.y; | |
| i += 2; | |
| } | |
| } | |
| else if (Geoms[geoId].type == Arc || Geoms[geoId].type == ArcOfEllipse | |
| || Geoms[geoId].type == ArcOfHyperbola || Geoms[geoId].type == ArcOfParabola) { | |
| MoveParameters[i] = toPoint.x; | |
| MoveParameters[i + 1] = toPoint.y; | |
| i += 2; | |
| } | |
| else if (Geoms[geoId].type == BSpline) { | |
| if (pos == PointPos::start || pos == PointPos::end) { | |
| MoveParameters[i] = toPoint.x; | |
| MoveParameters[i + 1] = toPoint.y; | |
| i += 2; | |
| } | |
| else if (pos == PointPos::none || pos == PointPos::mid) { | |
| GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; | |
| double cx = 0, cy = 0; // geometric center | |
| for (size_t j = 0; j < bsp.poles.size() * 2; j += 2) { | |
| cx += InitParameters[i + j]; | |
| cy += InitParameters[i + j + 1]; | |
| j += 2; | |
| } | |
| cx /= bsp.poles.size(); | |
| cy /= bsp.poles.size(); | |
| for (size_t j = 0; j < bsp.poles.size() * 2; j += 2) { | |
| MoveParameters[i + j] = toPoint.x + InitParameters[i + j] - cx; | |
| MoveParameters[i + j + 1] = toPoint.y + InitParameters[i + j + 1] - cy; | |
| j += 2; | |
| } | |
| i += bsp.poles.size() * 2; | |
| } | |
| } | |
| } | |
| } | |
| return solve(); | |
| } | |
| int Sketch::moveGeometry(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative) | |
| { | |
| std::vector<GeoElementId> geoEltIds = {GeoElementId(geoId, pos)}; | |
| return moveGeometries(geoEltIds, toPoint, relative); | |
| } | |
| int Sketch::setDatum(int /*constrId*/, double /*value*/) | |
| { | |
| return -1; | |
| } | |
| int Sketch::getPointId(int geoId, PointPos pos) const | |
| { | |
| // do a range check first | |
| if (geoId < 0 || geoId >= (int)Geoms.size()) { | |
| return -1; | |
| } | |
| switch (pos) { | |
| case PointPos::start: | |
| return Geoms[geoId].startPointId; | |
| case PointPos::end: | |
| return Geoms[geoId].endPointId; | |
| case PointPos::mid: | |
| return Geoms[geoId].midPointId; | |
| case PointPos::none: | |
| break; | |
| } | |
| return -1; | |
| } | |
| Base::Vector3d Sketch::getPoint(int geoId, PointPos pos) const | |
| { | |
| geoId = checkGeoId(geoId); | |
| int pointId = getPointId(geoId, pos); | |
| if (pointId != -1) { | |
| return Base::Vector3d(*Points[pointId].x, *Points[pointId].y, 0); | |
| } | |
| return Base::Vector3d(); | |
| } | |
| TopoShape Sketch::toShape() const | |
| { | |
| TopoShape result; | |
| std::vector<GeoDef>::const_iterator it = Geoms.begin(); | |
| bool first = true; | |
| for (; it!=Geoms.end(); ++it) { | |
| if (!it->geo->Construction) { | |
| TopoDS_Shape sh = it->geo->toShape(); | |
| if (first) { | |
| first = false; | |
| result.setShape(sh); | |
| } | |
| else { | |
| result.setShape(result.fuse(sh)); | |
| } | |
| } | |
| } | |
| return result; | |
| std::list<TopoDS_Edge> edge_list; | |
| std::list<TopoDS_Vertex> vertex_list; | |
| std::list<TopoDS_Wire> wires; | |
| // collecting all (non constructive and non external) edges out of the sketch | |
| for (; it != Geoms.end(); ++it) { | |
| auto gf = GeometryFacade::getFacade(it->geo); | |
| if (!it->external && !gf->getConstruction()) { | |
| if (it->type != Point) { | |
| auto shape = it->geo->toShape(); | |
| if (!shape.IsNull()) { | |
| edge_list.push_back(TopoDS::Edge(shape)); | |
| } | |
| } | |
| else { | |
| vertex_list.push_back(TopoDS::Vertex(it->geo->toShape())); | |
| } | |
| } | |
| } | |
| // Hint: Use ShapeAnalysis_FreeBounds::ConnectEdgesToWires() as an alternative | |
| // | |
| // sort them together to wires | |
| while (!edge_list.empty()) { | |
| BRepBuilderAPI_MakeWire mkWire; | |
| // add and erase first edge | |
| mkWire.Add(edge_list.front()); | |
| edge_list.pop_front(); | |
| TopoDS_Wire new_wire = mkWire.Wire(); // current new wire | |
| // try to connect each edge to the wire, the wire is complete if no more edges are | |
| // connectible | |
| bool found = false; | |
| do { | |
| found = false; | |
| for (auto pE = edge_list.begin(); pE != edge_list.end(); ++pE) { | |
| mkWire.Add(*pE); | |
| if (mkWire.Error() != BRepBuilderAPI_DisconnectedWire) { | |
| // edge added ==> remove it from list | |
| found = true; | |
| edge_list.erase(pE); | |
| new_wire = mkWire.Wire(); | |
| break; | |
| } | |
| } | |
| } while (found); | |
| // Fix any topological issues of the wire | |
| ShapeFix_Wire aFix; | |
| aFix.SetPrecision(Precision::Confusion()); | |
| aFix.Load(new_wire); | |
| aFix.FixReorder(); | |
| aFix.FixConnected(); | |
| aFix.FixClosed(); | |
| wires.push_back(aFix.Wire()); | |
| } | |
| if (wires.size() == 1 && vertex_list.empty()) { | |
| result = *wires.begin(); | |
| } | |
| else if (wires.size() > 1 || !vertex_list.empty()) { | |
| // FIXME: The right way here would be to determine the outer and inner wires and | |
| // generate a face with holes (inner wires have to be tagged REVERSE or INNER). | |
| // that's the only way to transport a somewhat more complex sketch... | |
| // result = *wires.begin(); | |
| // I think a compound can be used as container because it is just a collection of | |
| // shapes and doesn't need too much information about the topology. | |
| // The actual knowledge how to create a prism from several wires should go to the Pad | |
| // feature (Werner). | |
| BRep_Builder builder; | |
| TopoDS_Compound comp; | |
| builder.MakeCompound(comp); | |
| for (auto& wire : wires) { | |
| builder.Add(comp, wire); | |
| } | |
| for (auto& vertex : vertex_list) { | |
| builder.Add(comp, vertex); | |
| } | |
| result.setShape(comp); | |
| } | |
| return result; | |
| } | |
| // Persistence implementer ------------------------------------------------- | |
| unsigned int Sketch::getMemSize() const | |
| { | |
| return 0; | |
| } | |
| void Sketch::Save(Writer&) const | |
| {} | |
| void Sketch::Restore(XMLReader&) | |
| {} | |