4557 lines
172 KiB
C++
4557 lines
172 KiB
C++
/***************************************************************************
|
|
* 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 *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
# include <iostream>
|
|
# include <cmath>
|
|
|
|
# include <BRep_Builder.hxx>
|
|
# include <BRepBuilderAPI_MakeWire.hxx>
|
|
# include <Precision.hxx>
|
|
# include <ShapeFix_Wire.hxx>
|
|
# include <Standard_Version.hxx>
|
|
# include <TopoDS_Compound.hxx>
|
|
# include <TopoDS.hxx>
|
|
# include <TopoDS_Edge.hxx>
|
|
#endif
|
|
|
|
#include <Base/Console.h>
|
|
#include <Base/Exception.h>
|
|
#include <Base/Reader.h>
|
|
#include <Base/TimeInfo.h>
|
|
#include <Base/Writer.h>
|
|
#include <Base/VectorPy.h>
|
|
#include <Mod/Part/App/ArcOfCirclePy.h>
|
|
#include <Mod/Part/App/ArcOfEllipsePy.h>
|
|
#include <Mod/Part/App/ArcOfHyperbolaPy.h>
|
|
#include <Mod/Part/App/ArcOfParabolaPy.h>
|
|
#include <Mod/Part/App/BSplineCurvePy.h>
|
|
#include <Mod/Part/App/CirclePy.h>
|
|
#include <Mod/Part/App/EllipsePy.h>
|
|
#include <Mod/Part/App/HyperbolaPy.h>
|
|
#include <Mod/Part/App/LineSegmentPy.h>
|
|
#include <Mod/Part/App/ParabolaPy.h>
|
|
|
|
#include "Sketch.h"
|
|
#include "Constraint.h"
|
|
#include "GeometryFacade.h"
|
|
#include "SolverGeometryExtension.h"
|
|
|
|
|
|
//#define DEBUG_BLOCK_CONSTRAINT
|
|
#undef 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 (std::vector<double*>::iterator it = Parameters.begin(); it != Parameters.end(); ++it)
|
|
if (*it) delete *it;
|
|
Parameters.clear();
|
|
DrivenParameters.clear();
|
|
for (std::vector<double*>::iterator it = FixParameters.begin(); it != FixParameters.end(); ++it)
|
|
if (*it) delete *it;
|
|
FixParameters.clear();
|
|
|
|
param2geoelement.clear();
|
|
pDependencyGroups.clear();
|
|
solverExtensions.clear();
|
|
|
|
internalAlignmentGeometryMap.clear();
|
|
|
|
// deleting the geometry copied into this sketch
|
|
for (std::vector<GeoDef>::iterator it = Geoms.begin(); it != Geoms.end(); ++it)
|
|
if (it->geo) delete it->geo;
|
|
Geoms.clear();
|
|
|
|
// deleting the non-Driving constraints copied into this sketch
|
|
//for (std::vector<Constraint *>::iterator it = NonDrivingConstraints.begin(); it != NonDrivingConstraints.end(); ++it)
|
|
// if (*it) delete *it;
|
|
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->isDriving && 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::TimeInfo start_time;
|
|
|
|
clear();
|
|
|
|
std::vector<Part::Geometry *> intGeoList, extGeoList;
|
|
for (int i=0; i < int(GeoList.size())-extGeoCount; i++)
|
|
intGeoList.push_back(GeoList[i]);
|
|
for (int i=int(GeoList.size())-extGeoCount; i < int(GeoList.size()); i++)
|
|
extGeoList.push_back(GeoList[i]);
|
|
|
|
std::vector<bool> onlyBlockedGeometry(intGeoList.size(),false); // these geometries are blocked, frozen and sent as fixed parameters to the solver
|
|
std::vector<bool> unenforceableConstraints(ConstraintList.size(),false); // these constraints are unenforceable due to a Blocked constraint
|
|
|
|
/* 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);
|
|
|
|
#ifdef DEBUG_BLOCK_CONSTRAINT
|
|
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");
|
|
#endif //DEBUG_BLOCK_CONSTRAINT
|
|
|
|
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);
|
|
|
|
#ifdef DEBUG_BLOCK_CONSTRAINT
|
|
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);
|
|
}
|
|
}
|
|
#endif //DEBUG_BLOCK_CONSTRAINT
|
|
}
|
|
|
|
// 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::TimeInfo end_time;
|
|
|
|
Base::Console().Log("Sketcher::setUpSketch()-T:%s\n",Base::TimeInfo::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 *> ¶ms_to_block)
|
|
{
|
|
if(!params_to_block.empty()) { // only there are parameters to fix
|
|
for( auto p : params_to_block ) {
|
|
auto findparam = std::find(Parameters.begin(),Parameters.end(), p);
|
|
|
|
if(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 *> ¶ms_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());
|
|
|
|
#ifdef DEBUG_BLOCK_CONSTRAINT
|
|
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);
|
|
}
|
|
#endif //DEBUG_BLOCK_CONSTRAINT
|
|
|
|
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);
|
|
|
|
auto element = param2geoelement.find(thisparam);
|
|
|
|
if (element != param2geoelement.end()) {
|
|
|
|
auto blockable = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),std::get<0>(element->second));
|
|
|
|
if( 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::find(params_to_block.begin(), params_to_block.end(), 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;
|
|
#ifdef DEBUG_BLOCK_CONSTRAINT
|
|
Base::Console().Log("\nTentatively blocking group %d, with param=%x", i, thisparam);
|
|
#endif //DEBUG_BLOCK_CONSTRAINT
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Check if groups are satisfied or are licitly unsatisfiable and thus deemed as satisfied
|
|
bool unsatisfied_groups = false;
|
|
for(size_t i = 0; i < prop_groups.size(); i++) {
|
|
// 4.1. unsatisfiable group
|
|
if(prop_groups[i].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_groups[i].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->getTypeId() == GeomPoint::getClassTypeId()) { // 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->getTypeId() == GeomLineSegment::getClassTypeId()) { // 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->getTypeId() == GeomCircle::getClassTypeId()) { // 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->getTypeId() == GeomEllipse::getClassTypeId()) { // 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->getTypeId() == GeomArcOfCircle::getClassTypeId()) { // 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->getTypeId() == GeomArcOfEllipse::getClassTypeId()) { // 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->getTypeId() == GeomArcOfHyperbola::getClassTypeId()) { // 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->getTypeId() == GeomArcOfParabola::getClassTypeId()) { // 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->getTypeId() == GeomBSplineCurve::getClassTypeId()) { // 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 *> &geo, bool fixed)
|
|
{
|
|
int ret = -1;
|
|
for (std::vector<Part::Geometry *>::const_iterator it=geo.begin(); it != geo.end(); ++it)
|
|
ret = addGeometry(*it, fixed);
|
|
return ret;
|
|
}
|
|
|
|
int Sketch::addGeometry(const std::vector<Part::Geometry *> &geo,
|
|
const std::vector<bool> &blockedGeometry)
|
|
{
|
|
assert(geo.size() == blockedGeometry.size());
|
|
|
|
int ret = -1;
|
|
std::vector<Part::Geometry *>::const_iterator it;
|
|
std::vector<bool>::const_iterator bit;
|
|
|
|
for (it=geo.begin(),bit=blockedGeometry.begin(); it != geo.end() && bit !=blockedGeometry.end(); ++it,++bit)
|
|
ret = addGeometry(*it, *bit);
|
|
return ret;
|
|
}
|
|
|
|
int Sketch::addPoint(const Part::GeomPoint &point, bool fixed)
|
|
{
|
|
std::vector<double *> ¶ms = 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 *> ¶ms = 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 *> ¶ms = 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 *> ¶ms = 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 *> ¶ms = 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 ¶bolaSegment, bool fixed)
|
|
{
|
|
std::vector<double *> ¶ms = 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 *> ¶ms = 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(std::vector<Base::Vector3d>::const_iterator it = poles.begin(); it != poles.end(); ++it){
|
|
params.push_back(new double( (*it).x ));
|
|
params.push_back(new double( (*it).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(std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it) {
|
|
auto r = new double( (*it) );
|
|
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(std::vector<double>::const_iterator it = knots.begin(); it != knots.end(); ++it) {
|
|
double * knot = new double( (*it) );
|
|
//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(std::vector<int>::iterator it = bs.knotpointGeoids.begin(); it != bs.knotpointGeoids.end(); ++it) {
|
|
(*it) = 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 *> ¶ms = 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 *> ¶ms = 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 (std::vector<GeoDef>::const_iterator it=Geoms.begin(); it != Geoms.end(); ++it) {
|
|
auto gf = GeometryFacade::getFacade(it->geo);
|
|
if ((!it->external || withExternalElements) && (!gf->getConstruction() || withConstructionElements))
|
|
temp.push_back(it->geo->clone());
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
|
|
GeoListFacade Sketch::extractGeoListFacade() const
|
|
{
|
|
std::vector<GeometryFacadeUniquePtr> temp;
|
|
temp.reserve(Geoms.size());
|
|
int internalGeometryCount = 0;
|
|
for (std::vector<GeoDef>::const_iterator it=Geoms.begin(); it != Geoms.end(); ++it) {
|
|
auto gf = GeometryFacade::getFacade(it->geo->clone(), true); // GeometryFacade is the owner of this allocation
|
|
if(!it->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 (std::vector<GeoDef>::const_iterator it=Geoms.begin(); it != Geoms.end(); ++it, i++) {
|
|
if (it->type == Point) {
|
|
Base::Vector3d temp(*(Points[it->startPointId].x),*(Points[it->startPointId].y),0);
|
|
tuple[i] = Py::asObject(new VectorPy(temp));
|
|
}
|
|
else if (it->type == Line) {
|
|
GeomLineSegment *lineSeg = static_cast<GeomLineSegment*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new LineSegmentPy(lineSeg));
|
|
}
|
|
else if (it->type == Arc) {
|
|
GeomArcOfCircle *aoc = static_cast<GeomArcOfCircle*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new ArcOfCirclePy(aoc));
|
|
}
|
|
else if (it->type == Circle) {
|
|
GeomCircle *circle = static_cast<GeomCircle*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new CirclePy(circle));
|
|
}
|
|
else if (it->type == Ellipse) {
|
|
GeomEllipse *ellipse = static_cast<GeomEllipse*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new EllipsePy(ellipse));
|
|
}
|
|
else if (it->type == ArcOfEllipse) {
|
|
GeomArcOfEllipse *ellipse = static_cast<GeomArcOfEllipse*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new ArcOfEllipsePy(ellipse));
|
|
}
|
|
else if (it->type == ArcOfHyperbola) {
|
|
GeomArcOfHyperbola *aoh = static_cast<GeomArcOfHyperbola*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new ArcOfHyperbolaPy(aoh));
|
|
}
|
|
else if (it->type == ArcOfParabola) {
|
|
GeomArcOfParabola *aop = static_cast<GeomArcOfParabola*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new ArcOfParabolaPy(aop));
|
|
}
|
|
else if (it->type == BSpline) {
|
|
GeomBSplineCurve *bsp = static_cast<GeomBSplineCurve*>(it->geo->clone());
|
|
tuple[i] = Py::asObject(new BSplineCurvePy(bsp));
|
|
}
|
|
else {
|
|
// not implemented type in the sketch!
|
|
}
|
|
}
|
|
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:
|
|
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->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
|
|
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);
|
|
}
|
|
else if (constraint->SecondPos != PointPos::none){ // angle between two lines (with explicit start points)
|
|
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 (std::vector<Constraint *>::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++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 (std::vector<Constraint *>::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++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 (std::vector<Constraint *>::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++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(std::vector<int>::iterator it = internalAlignmentConstraintIndex.begin(); it != internalAlignmentConstraintIndex.end() ; it++) {
|
|
if (blockedGeometry[ConstraintList[(*it)]->Second]) {
|
|
blockedGeometry[ConstraintList[(*it)]->First] = true;
|
|
// associated geometry gets the same blocking constraint index as the blocked element
|
|
geo2blockingconstraintindex[ConstraintList[(*it)]->First]= geo2blockingconstraintindex[ConstraintList[(*it)]->Second];
|
|
internalAlignmentgeo.push_back(ConstraintList[(*it)]->First);
|
|
unenforceableConstraints[(*it)]= true;
|
|
}
|
|
}
|
|
|
|
i = 0;
|
|
for (std::vector<Constraint *>::const_iterator 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(std::vector<int>::iterator itg = internalAlignmentgeo.begin(); itg != internalAlignmentgeo.end() ; itg++) {
|
|
if( (*it)->First==*itg || (*it)->Second==*itg || (*it)->Third==*itg ) {
|
|
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[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[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[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;
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
auto knotIt = std::find(b.knotpointGeoids.begin(),
|
|
b.knotpointGeoids.end(), 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 constraint at end knots of a B-spline. Please constrain the start/end points instead.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
int tag = Sketch::addPointOnObjectConstraint(checkedknotgeoid, PointPos::start, checkedlinegeoId);//increases ConstraintsCounter
|
|
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::find(b.knotpointGeoids.begin(),
|
|
b.knotpointGeoids.end(), 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. Please 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)
|
|
{
|
|
|
|
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.
|
|
double angleOffset = 0.0;//the difference between the datum value and the actual angle to apply. (datum=angle+offset)
|
|
double angleDesire = 0.0;//the desired angle value (and we are to decide if 180* should be added to it)
|
|
if (cTyp == Tangent) {angleOffset = -M_PI/2; angleDesire = 0.0;}
|
|
if (cTyp == Perpendicular) {angleOffset = 0; angleDesire = M_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 > M_PI) angleErr -= M_PI*2;
|
|
if (angleErr < -M_PI) angleErr += M_PI*2;
|
|
|
|
//the autodetector
|
|
if(fabs(angleErr) > M_PI/2 )
|
|
angleDesire += M_PI;
|
|
|
|
*angle = angleDesire;
|
|
}
|
|
else
|
|
*angle = *value-angleOffset;
|
|
}
|
|
|
|
int tag = -1;
|
|
if(e2c)
|
|
tag = Sketch::addPointOnObjectConstraint(geoId1, pos1, geoId2, driving);//increases ConstraintsCounter
|
|
if (e2e){
|
|
tag = ++ConstraintsCounter;
|
|
GCSsys.addConstraintP2PCoincident(p, *p2, tag, driving);
|
|
}
|
|
if(avp)
|
|
tag = ++ConstraintsCounter;
|
|
|
|
GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving);
|
|
return ConstraintsCounter;
|
|
}
|
|
|
|
// line length constraint
|
|
int Sketch::addDistanceConstraint(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.addConstraintP2PDistance(l.p1, l.p2, value, tag, driving);
|
|
return ConstraintsCounter;
|
|
}
|
|
|
|
// point to line 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 (Geoms[geoId2].type != Line)
|
|
return -1;
|
|
|
|
if (pointId1 >= 0 && pointId1 < int(Points.size())) {
|
|
GCS::Point &p1 = Points[pointId1];
|
|
GCS::Line &l2 = Lines[Geoms[geoId2].index];
|
|
|
|
int tag = ++ConstraintsCounter;
|
|
GCSsys.addConstraintP2LDistance(p1, l2, value, tag, driving);
|
|
return ConstraintsCounter;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
|
|
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;
|
|
//tag = Sketch::addPointOnObjectConstraint(geoIdRay1, posRay1, geoIdBnd);//increases ConstraintsCounter
|
|
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);
|
|
|
|
tag = ++ConstraintsCounter;
|
|
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);
|
|
}
|
|
|
|
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 (std::vector<GeoDef>::const_iterator it=Geoms.begin(); it != Geoms.end(); ++it, i++) {
|
|
try {
|
|
if (it->type == Point) {
|
|
GeomPoint *point = static_cast<GeomPoint*>(it->geo);
|
|
auto pointf = GeometryFacade::getFacade(point);
|
|
|
|
point->setPoint(Vector3d(*Points[it->startPointId].x,
|
|
*Points[it->startPointId].y,
|
|
0.0));
|
|
}
|
|
else if (it->type == Line) {
|
|
GeomLineSegment *lineSeg = static_cast<GeomLineSegment*>(it->geo);
|
|
lineSeg->setPoints(Vector3d(*Lines[it->index].p1.x,
|
|
*Lines[it->index].p1.y,
|
|
0.0),
|
|
Vector3d(*Lines[it->index].p2.x,
|
|
*Lines[it->index].p2.y,
|
|
0.0)
|
|
);
|
|
}
|
|
else if (it->type == Arc) {
|
|
GCS::Arc &myArc = Arcs[it->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*>(it->geo);
|
|
aoc->setCenter(Vector3d(*Points[it->midPointId].x,
|
|
*Points[it->midPointId].y,
|
|
0.0)
|
|
);
|
|
aoc->setRadius(*myArc.rad);
|
|
aoc->setRange(*myArc.startAngle, *myArc.endAngle, /*emulateCCW=*/true);
|
|
}
|
|
else if (it->type == ArcOfEllipse) {
|
|
GCS::ArcOfEllipse &myArc = ArcsOfEllipse[it->index];
|
|
|
|
GeomArcOfEllipse *aoe = static_cast<GeomArcOfEllipse*>(it->geo);
|
|
|
|
Base::Vector3d center = Vector3d(*Points[it->midPointId].x, *Points[it->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);
|
|
if ( radmaj >= aoe->getMinorRadius() ){//ensure that ellipse's major radius is always larger than minor raduis... may still cause problems with degenerates.
|
|
aoe->setMajorRadius(radmaj);
|
|
aoe->setMinorRadius(radmin);
|
|
} else {
|
|
aoe->setMinorRadius(radmin);
|
|
aoe->setMajorRadius(radmaj);
|
|
}
|
|
aoe->setMajorAxisDir(fd);
|
|
aoe->setRange(*myArc.startAngle, *myArc.endAngle, /*emulateCCW=*/true);
|
|
}
|
|
else if (it->type == Circle) {
|
|
GeomCircle *circ = static_cast<GeomCircle*>(it->geo);
|
|
circ->setCenter(Vector3d(*Points[it->midPointId].x,
|
|
*Points[it->midPointId].y,
|
|
0.0)
|
|
);
|
|
circ->setRadius(*Circles[it->index].rad);
|
|
}
|
|
else if (it->type == Ellipse) {
|
|
|
|
GeomEllipse *ellipse = static_cast<GeomEllipse*>(it->geo);
|
|
|
|
Base::Vector3d center = Vector3d(*Points[it->midPointId].x, *Points[it->midPointId].y, 0.0);
|
|
Base::Vector3d f1 = Vector3d(*Ellipses[it->index].focus1.x, *Ellipses[it->index].focus1.y, 0.0);
|
|
double radmin = *Ellipses[it->index].radmin;
|
|
|
|
Base::Vector3d fd=f1-center;
|
|
double radmaj = sqrt(fd*fd+radmin*radmin);
|
|
|
|
ellipse->setCenter(center);
|
|
if ( radmaj >= ellipse->getMinorRadius() ){//ensure that ellipse's major radius is always larger than minor raduis... may still cause problems with degenerates.
|
|
ellipse->setMajorRadius(radmaj);
|
|
ellipse->setMinorRadius(radmin);
|
|
} else {
|
|
ellipse->setMinorRadius(radmin);
|
|
ellipse->setMajorRadius(radmaj);
|
|
}
|
|
ellipse->setMajorAxisDir(fd);
|
|
}
|
|
else if (it->type == ArcOfHyperbola) {
|
|
GCS::ArcOfHyperbola &myArc = ArcsOfHyperbola[it->index];
|
|
|
|
GeomArcOfHyperbola *aoh = static_cast<GeomArcOfHyperbola*>(it->geo);
|
|
|
|
Base::Vector3d center = Vector3d(*Points[it->midPointId].x, *Points[it->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, /*emulateCCW=*/true);
|
|
}
|
|
else if (it->type == ArcOfParabola) {
|
|
GCS::ArcOfParabola &myArc = ArcsOfParabola[it->index];
|
|
|
|
GeomArcOfParabola *aop = static_cast<GeomArcOfParabola*>(it->geo);
|
|
|
|
Base::Vector3d vertex = Vector3d(*Points[it->midPointId].x, *Points[it->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, /*emulateCCW=*/true);
|
|
}
|
|
else if (it->type == BSpline) {
|
|
GCS::BSpline &mybsp = BSplines[it->index];
|
|
|
|
GeomBSplineCurve *bsp = static_cast<GeomBSplineCurve*>(it->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);*/
|
|
}
|
|
} catch (Base::Exception &e) {
|
|
Base::Console().Error("Updating geometry: Error build geometry(%d): %s\n",
|
|
i,e.what());
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Sketch::updateNonDrivingConstraints()
|
|
{
|
|
for (std::vector<ConstrDef>::iterator it = Constrs.begin();it!=Constrs.end();++it){
|
|
if(!(*it).driving) {
|
|
if((*it).constr->Type==SnellsLaw) {
|
|
double n1 = *((*it).value);
|
|
double n2 = *((*it).secondvalue);
|
|
|
|
(*it).constr->setValue(n2/n1);
|
|
}
|
|
else if((*it).constr->Type==Angle) {
|
|
|
|
(*it).constr->setValue(std::fmod(*((*it).value), 2.0*M_PI));
|
|
}
|
|
else if((*it).constr->Type==Diameter && (*it).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 = (*it).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;
|
|
}
|
|
|
|
auto pos = std::find(FixParameters.begin(), FixParameters.end(), rad);
|
|
|
|
if (pos != FixParameters.end())
|
|
(*it).constr->setValue(*((*it).value));
|
|
else
|
|
(*it).constr->setValue(2.0**((*it).value));
|
|
}
|
|
else {
|
|
(*it).constr->setValue(*((*it).value));
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// solving ==========================================================
|
|
|
|
int Sketch::solve()
|
|
{
|
|
Base::TimeInfo start_time;
|
|
std::string solvername;
|
|
|
|
auto result = internalSolve(solvername);
|
|
|
|
Base::TimeInfo end_time;
|
|
|
|
if(debugMode==GCS::Minimal || debugMode==GCS::IterationLevel){
|
|
|
|
Base::Console().Log("Sketcher::Solve()-%s-T:%s\n",solvername.c_str(),Base::TimeInfo::diffTime(start_time,end_time).c_str());
|
|
}
|
|
|
|
SolveTime = Base::TimeInfo::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;
|
|
case 3: // last resort: augment the system with a second subsystem and use the SQP solver
|
|
solvername = "SQP(augmented system)";
|
|
InitParameters.resize(Parameters.size());
|
|
int i=0;
|
|
for (std::vector<double*>::iterator 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");
|
|
|
|
if (soltype > 0) {
|
|
Base::Console().Log("If you see this message please report a way of reproducing this result at\n");
|
|
Base::Console().Log("http://www.freecadweb.org/tracker/main_page.php\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(int geoId, PointPos pos, bool fine)
|
|
{
|
|
isFine = fine;
|
|
|
|
geoId = checkGeoId(geoId);
|
|
|
|
clearTemporaryConstraints();
|
|
|
|
// don't try to move sketches that contain conflicting constraints
|
|
if (hasConflicts()) {
|
|
isInitMove = false;
|
|
return -1;
|
|
}
|
|
|
|
if (Geoms[geoId].type == Point) {
|
|
if (pos == PointPos::start) {
|
|
GCS::Point &point = Points[Geoms[geoId].startPointId];
|
|
GCS::Point p0;
|
|
MoveParameters.resize(2); // px,py
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *point.x;
|
|
*p0.y = *point.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,point,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Line) {
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
MoveParameters.resize(2); // x,y
|
|
GCS::Point p0;
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
if (pos == PointPos::start) {
|
|
GCS::Point &p = Points[Geoms[geoId].startPointId];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::end) {
|
|
GCS::Point &p = Points[Geoms[geoId].endPointId];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
}
|
|
else if (pos == PointPos::none || pos == PointPos::mid) {
|
|
MoveParameters.resize(4); // x1,y1,x2,y2
|
|
GCS::Point p1, p2;
|
|
p1.x = &MoveParameters[0];
|
|
p1.y = &MoveParameters[1];
|
|
p2.x = &MoveParameters[2];
|
|
p2.y = &MoveParameters[3];
|
|
GCS::Line &l = Lines[Geoms[geoId].index];
|
|
*p1.x = *l.p1.x;
|
|
*p1.y = *l.p1.y;
|
|
*p2.x = *l.p2.x;
|
|
*p2.y = *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 ¢er = Points[Geoms[geoId].midPointId];
|
|
GCS::Point p0,p1;
|
|
if (pos == PointPos::mid) {
|
|
MoveParameters.resize(2); // cx,cy
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,center,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::none) {
|
|
//bool pole = GeometryFacade::isInternalType(Geoms[geoId].geo, InternalType::BSplineControlPoint);
|
|
MoveParameters.resize(4); // x,y,cx,cy - For poles blocking the center
|
|
GCS::Circle &c = Circles[Geoms[geoId].index];
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y + *c.rad;
|
|
GCSsys.addConstraintPointOnCircle(p0,c,GCS::DefaultTemporaryConstraint);
|
|
p1.x = &MoveParameters[2];
|
|
p1.y = &MoveParameters[3];
|
|
*p1.x = *center.x;
|
|
*p1.y = *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) {
|
|
|
|
GCS::Point ¢er = Points[Geoms[geoId].midPointId];
|
|
GCS::Point p0,p1;
|
|
if (pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters.resize(2); // cx,cy
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p0,center,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == ArcOfEllipse) {
|
|
|
|
GCS::Point ¢er = Points[Geoms[geoId].midPointId];
|
|
GCS::Point p0,p1;
|
|
if (pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters.resize(2); // cx,cy
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,center,GCS::DefaultTemporaryConstraint);
|
|
|
|
}
|
|
else if (pos == PointPos::start || pos == PointPos::end) {
|
|
|
|
MoveParameters.resize(4); // x,y,cx,cy
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
GCS::Point &p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId]
|
|
: Points[Geoms[geoId].endPointId];;
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
|
|
p1.x = &MoveParameters[2];
|
|
p1.y = &MoveParameters[3];
|
|
*p1.x = *center.x;
|
|
*p1.y = *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 ¢er = Points[Geoms[geoId].midPointId];
|
|
GCS::Point p0,p1;
|
|
if (pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters.resize(2); // cx,cy
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p0,center,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::start || pos == PointPos::end) {
|
|
|
|
MoveParameters.resize(4); // x,y,cx,cy
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
GCS::Point &p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId]
|
|
: Points[Geoms[geoId].endPointId];;
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
p1.x = &MoveParameters[2];
|
|
p1.y = &MoveParameters[3];
|
|
*p1.x = *center.x;
|
|
*p1.y = *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 ¢er = Points[Geoms[geoId].midPointId];
|
|
GCS::Point p0,p1;
|
|
if (pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters.resize(2); // cx,cy
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p0,center,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::start || pos == PointPos::end) {
|
|
|
|
MoveParameters.resize(4); // x,y,cx,cy
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
GCS::Point &p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId]
|
|
: Points[Geoms[geoId].endPointId];;
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
p1.x = &MoveParameters[2];
|
|
p1.y = &MoveParameters[3];
|
|
*p1.x = *center.x;
|
|
*p1.y = *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) {
|
|
MoveParameters.resize(2); // x,y
|
|
GCS::Point p0;
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
if (pos == PointPos::start) {
|
|
GCS::Point &p = Points[Geoms[geoId].startPointId];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::end) {
|
|
GCS::Point &p = Points[Geoms[geoId].endPointId];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
}
|
|
else if (pos == PointPos::none || pos == PointPos::mid) {
|
|
GCS::BSpline &bsp = BSplines[Geoms[geoId].index];
|
|
MoveParameters.resize(bsp.poles.size()*2); // x0,y0,x1,y1,....xp,yp
|
|
|
|
int mvindex = 0;
|
|
for(std::vector<GCS::Point>::iterator it = bsp.poles.begin(); it != bsp.poles.end() ; it++, mvindex++) {
|
|
GCS::Point p1;
|
|
p1.x = &MoveParameters[mvindex];
|
|
mvindex++;
|
|
p1.y = &MoveParameters[mvindex];
|
|
|
|
*p1.x = *(*it).x;
|
|
*p1.y = *(*it).y;
|
|
|
|
GCSsys.addConstraintP2PCoincident(p1,(*it),GCS::DefaultTemporaryConstraint);
|
|
}
|
|
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Arc) {
|
|
GCS::Point ¢er = Points[Geoms[geoId].midPointId];
|
|
GCS::Point p0,p1;
|
|
if (pos == PointPos::mid) {
|
|
MoveParameters.resize(2); // cx,cy
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,center,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::none) {
|
|
MoveParameters.resize(4); // x,y,cx,cy
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
GCS::Point &p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId]
|
|
: Points[Geoms[geoId].endPointId];;
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *p.x;
|
|
*p0.y = *p.y;
|
|
GCSsys.addConstraintP2PCoincident(p0,p,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
else if (pos == PointPos::none) {
|
|
GCS::Arc &a = Arcs[Geoms[geoId].index];
|
|
p0.x = &MoveParameters[0];
|
|
p0.y = &MoveParameters[1];
|
|
*p0.x = *center.x;
|
|
*p0.y = *center.y + *a.rad;
|
|
GCSsys.addConstraintPointOnArc(p0,a,GCS::DefaultTemporaryConstraint);
|
|
}
|
|
p1.x = &MoveParameters[2];
|
|
p1.y = &MoveParameters[3];
|
|
*p1.x = *center.x;
|
|
*p1.y = *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;
|
|
}
|
|
|
|
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::movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative)
|
|
{
|
|
geoId = checkGeoId(geoId);
|
|
|
|
// don't try to move sketches that contain conflicting constraints
|
|
if (hasConflicts())
|
|
return -1;
|
|
|
|
if (!isInitMove) {
|
|
initMove(geoId, pos);
|
|
initToPoint = toPoint;
|
|
moveStep = 0;
|
|
}
|
|
else {
|
|
if(!relative && RecalculateInitialSolutionWhileMovingPoint) {
|
|
if (moveStep == 0) {
|
|
moveStep = (toPoint-initToPoint).Length();
|
|
}
|
|
else {
|
|
if( (toPoint-initToPoint).Length() > 20*moveStep) { // I am getting too far away from the original solution so reinit the solution
|
|
initMove(geoId, pos);
|
|
initToPoint = toPoint;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (relative) {
|
|
for (int i=0; i < int(MoveParameters.size()-1); i+=2) {
|
|
MoveParameters[i] = InitParameters[i] + toPoint.x;
|
|
MoveParameters[i+1] = InitParameters[i+1] + toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Point) {
|
|
if (pos == PointPos::start) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Line) {
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
else if (pos == PointPos::none || pos == PointPos::mid) {
|
|
double dx = (InitParameters[2]-InitParameters[0])/2;
|
|
double dy = (InitParameters[3]-InitParameters[1])/2;
|
|
MoveParameters[0] = toPoint.x - dx;
|
|
MoveParameters[1] = toPoint.y - dy;
|
|
MoveParameters[2] = toPoint.x + dx;
|
|
MoveParameters[3] = toPoint.y + dy;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Circle) {
|
|
if (pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Arc) {
|
|
if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == Ellipse) {
|
|
if (pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == ArcOfEllipse) {
|
|
if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == ArcOfHyperbola) {
|
|
if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == ArcOfParabola) {
|
|
if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid || pos == PointPos::none) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
}
|
|
else if (Geoms[geoId].type == BSpline) {
|
|
if (pos == PointPos::start || pos == PointPos::end) {
|
|
MoveParameters[0] = toPoint.x;
|
|
MoveParameters[1] = toPoint.y;
|
|
}
|
|
else if (pos == PointPos::none || pos == PointPos::mid) {
|
|
GCS::BSpline &bsp = BSplines[Geoms[geoId].index];
|
|
|
|
double cx = 0, cy = 0; // geometric center
|
|
for (int i=0; i < int(InitParameters.size()-1); i+=2) {
|
|
cx += InitParameters[i];
|
|
cy += InitParameters[i+1];
|
|
}
|
|
|
|
cx /= bsp.poles.size();
|
|
cy /= bsp.poles.size();
|
|
|
|
for (int i=0; i < int(MoveParameters.size()-1); i+=2) {
|
|
|
|
MoveParameters[i] = toPoint.x + InitParameters[i] - cx;
|
|
MoveParameters[i+1] = toPoint.y + InitParameters[i+1] - cy;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return solve();
|
|
}
|
|
|
|
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();
|
|
|
|
#if 0
|
|
|
|
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;
|
|
#else
|
|
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 (std::list<TopoDS_Edge>::iterator 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 (std::list<TopoDS_Wire>::iterator wt = wires.begin(); wt != wires.end(); ++wt)
|
|
builder.Add(comp, *wt);
|
|
for (std::list<TopoDS_Vertex>::iterator wt = vertex_list.begin(); wt != vertex_list.end(); ++wt)
|
|
builder.Add(comp, *wt);
|
|
result.setShape(comp);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
// Persistence implementer -------------------------------------------------
|
|
|
|
unsigned int Sketch::getMemSize() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Sketch::Save(Writer &) const
|
|
{
|
|
|
|
}
|
|
|
|
void Sketch::Restore(XMLReader &)
|
|
{
|
|
|
|
}
|
|
|