Skip to content

Commit

Permalink
AngleViaPoint: internal/external tangency locking
Browse files Browse the repository at this point in the history
*Added automatic tangency type lockdown for all new constraints (only
for point-wise tangency).
Tangency type is stored in the constraint datum field, as an angle value
shifted by Pi/2 (to be able to treat 0.0 as undefined type).
Added ability to switch the tangency by setting datum value from python
(can be abused by passing arbitrary angle).
Further simplified the tangency related code in Sketch.cpp.
  • Loading branch information
DeepSOIC committed Nov 25, 2014
1 parent 9e3fa8c commit ef77191
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 115 deletions.
180 changes: 69 additions & 111 deletions src/Mod/Sketcher/App/Sketch.cpp
Expand Up @@ -686,19 +686,18 @@ int Sketch::addConstraint(const Constraint *constraint)
}
break;
case Tangent:
if (constraint->Third != Constraint::GeoUndef){
rtn = addTangentViaPointConstraint(constraint->First,
constraint->Second,
constraint->Third, constraint->ThirdPos);
} else if (constraint->SecondPos != none) // tangency at common point
rtn = addTangentConstraint(constraint->First,constraint->FirstPos,
constraint->Second,constraint->SecondPos);
else if (constraint->Second != Constraint::GeoUndef) {
if (constraint->FirstPos != none) // "First" is a tangency point
rtn = addTangentConstraint(constraint->First,constraint->FirstPos,
constraint->Second);
else // simple tangency
rtn = addTangentConstraint(constraint->First,constraint->Second);
if (constraint->FirstPos == none &&
constraint->SecondPos == none &&
constraint->Third == Constraint::GeoUndef){
//simple tangency
rtn = addTangentConstraint(constraint->First,constraint->Second);
} else {
//any other point-wise tangency (endpoint-to-curve, endpoint-to-endpoint, tangent-via-point)
rtn = addTangentPointConstraint(
constraint->First, constraint->FirstPos,
constraint->Second, constraint->SecondPos,
constraint->Third, constraint->ThirdPos,
constraint->Value);
}
break;
case Distance:
Expand Down Expand Up @@ -1324,132 +1323,91 @@ int Sketch::addTangentConstraint(int geoId1, int geoId2)
return -1;
}

// endpoint-to-curve tangency
int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2)
//This function handles any type of tangent constraint that involves a point.
//i.e. endpoint-to-curve, endpoint-to-endpoint and tangent-via-point
//geoid1, geoid2 and geoid3 as in in the constraint object.
int Sketch::addTangentPointConstraint(
int geoId1, PointPos pos1,
int geoId2, PointPos pos2,
int geoId3, PointPos pos3,
double value)
{
geoId1 = checkGeoId(geoId1);
geoId2 = checkGeoId(geoId2);

int pointId1 = getPointId(geoId1, pos1);
bool tvp = geoId3!=Constraint::GeoUndef; //is tangent-via-point?
bool e2c = pos2 == none && pos1 != none;//is endpoint-to-curve?
bool e2e = pos2 != none && pos1 != none;//is endpoint-to-endpoint?

if (pointId1 < 0 || pointId1 >= int(Points.size())){
Base::Console().Error("addTangentConstraint (endpoint to curve): point index out of range.\n");
return -1;
}

GCS::Point &p1 = Points[pointId1];

if(Geoms[geoId1].type == Point || Geoms[geoId2].type == Point)
return -1;//supplied points are not endpoints of curves.
GCS::Curve* crv1 = getGCSCurveByGeoId(geoId1);
GCS::Curve* crv2 = getGCSCurveByGeoId(geoId2);
if (!crv1 || !crv2) {
Base::Console().Error("addTangentConstraint (endpoint to curve): getGCSCurveByGeoId returned NULL!\n");
if (!( tvp || e2c || e2e )) {
assert(0);//none of the three types. Why are we here??
return -1;
}

// add the parameter for the angle
FixParameters.push_back(new double(0.0));
double *angle = FixParameters[FixParameters.size()-1];

//decide if the tangency is internal (angle=0) or external (angle=pi)
//FIXME: The point-on-object constraint should be solved before doing this
//to be strictly correct. But if the point is not way off, the result will be
//close.
*angle = GCSsys.calculateAngleViaPoint(*crv1, *crv2, p1);
if(abs(*angle) > M_PI/2 )
*angle = M_PI;
else
*angle = 0.0;


//ConstraintsCounter will be incremented in the following call.
int tag =
Sketch::addPointOnObjectConstraint(geoId1, pos1, geoId2);//increases ConstraintsCounter
GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p1, angle, tag);
return ConstraintsCounter;
}

// endpoint-to-endpoint tangency
int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2)
{
geoId1 = checkGeoId(geoId1);
geoId2 = checkGeoId(geoId2);
if(tvp)
geoId3 = checkGeoId(geoId3);

int pointId1 = getPointId(geoId1, pos1);
int pointId2 = getPointId(geoId2, pos2);

if (pointId1 < 0 || pointId1 >= int(Points.size()) ||
pointId2 < 0 || pointId2 >= int(Points.size())) {
Base::Console().Error("addTangentConstraint (endpoint to endpoint): point index out of range.\n");
return -1;
}

GCS::Point &p1 = Points[pointId1];
GCS::Point &p2 = Points[pointId2];

if(Geoms[geoId1].type == Point || Geoms[geoId2].type == Point)
return -1;//supplied points are not endpoints of curves.
GCS::Curve* crv1 = getGCSCurveByGeoId(geoId1);
GCS::Curve* crv2 = getGCSCurveByGeoId(geoId2);
if (!crv1 || !crv2) {
Base::Console().Error("addTangentConstraint (endpoint to endpoint): getGCSCurveByGeoId returned NULL!\n");
if (Geoms[geoId1].type == Point ||
Geoms[geoId2].type == Point){
assert(0);//point is not a curve. No tangency!
return -1;
}

// add the parameter for the angle
FixParameters.push_back(new double(0.0));
double *angle = FixParameters[FixParameters.size()-1];

//decide if the tangency is internal (angle=0) or external (angle=pi)
*angle = GCSsys.calculateAngleViaPoint(*crv1, *crv2, p1, p2);
if(abs(*angle) > M_PI/2 )
*angle = M_PI;
else
*angle = 0.0;

int tag = ++ConstraintsCounter;
GCSsys.addConstraintP2PCoincident(p1, p2, tag);
GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p1, angle, tag);
return ConstraintsCounter;
}

int Sketch::addTangentViaPointConstraint(int geoId1, int geoId2, int geoId3, PointPos pos3)
{
geoId1 = checkGeoId(geoId1);
geoId2 = checkGeoId(geoId2);
geoId3 = checkGeoId(geoId3);

if (Geoms[geoId1].type == Point ||
Geoms[geoId2].type == Point)
return -1;//first two objects must be curves!

GCS::Curve* crv1 =getGCSCurveByGeoId(geoId1);
GCS::Curve* crv2 =getGCSCurveByGeoId(geoId2);
if (!crv1 || !crv2) {
assert(0);
Base::Console().Error("addTangentViaPointConstraint: getGCSCurveByGeoId returned NULL!\n");
return -1;
}

int pointId = getPointId(geoId3, pos3);;
int pointId = -1;
if(tvp)
pointId = getPointId(geoId3, pos3);
else if (e2e || e2c)
pointId = getPointId(geoId1, pos1);

if (pointId < 0 || pointId >= int(Points.size())){
Base::Console().Error("addTangentViaPointConstraint: point index out of range.\n");
assert(0);
Base::Console().Error("addTangentConstraint (endpoint to curve): point index out of range.\n");
return -1;
}
GCS::Point &p = Points[pointId];
GCS::Point* p2 = 0;
if(e2e){//we need second point
int pointId = getPointId(geoId2, pos2);
if (pointId < 0 || pointId >= int(Points.size())){
assert(0);
Base::Console().Error("addTangentConstraint (endpoint to curve): point index out of range.\n");
return -1;
}
p2 = &(Points[pointId]);
}

// add the parameter for the angle
FixParameters.push_back(new double(0.0));
double *angle = FixParameters[FixParameters.size()-1];

//decide if the tangency is internal (angle=0) or external (angle=pi)
*angle = GCSsys.calculateAngleViaPoint(*crv1, *crv2, p);
if(abs(*angle) > M_PI/2 )
*angle = M_PI;
else
*angle = 0.0;
//value==0 - autodecide tangency.
if (value==0.0) {
*angle = GCSsys.calculateAngleViaPoint(*crv1, *crv2, p);
if(abs(*angle) > M_PI/2 )
value = +M_PI/2;
else
value = -M_PI/2;
}

*angle = value + M_PI/2;

int tag = -1;
if(e2c)
tag = Sketch::addPointOnObjectConstraint(geoId1, pos1, geoId2);//increases ConstraintsCounter
if (e2e){
tag = ++ConstraintsCounter;
GCSsys.addConstraintP2PCoincident(p, *p2, tag);
}
if(tvp)
tag = ++ConstraintsCounter;

int tag = ++ConstraintsCounter;
GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag);
return ConstraintsCounter;
}
Expand Down
8 changes: 5 additions & 3 deletions src/Mod/Sketcher/App/Sketch.h
Expand Up @@ -164,9 +164,11 @@ class SketcherExport Sketch :public Base::Persistence
int addPerpendicularConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2);
/// add a tangency constraint between two geometries
int addTangentConstraint(int geoId1, int geoId2);
int addTangentConstraint(int geoId1, PointPos pos1, int geoId2);
int addTangentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2);
int addTangentViaPointConstraint(int geoId1, int geoId2, int geoId3, PointPos pos3);
int addTangentPointConstraint(
int geoId1, PointPos pos1,
int geoId2, PointPos pos2,
int geoId3, PointPos pos3,
double value);
/// add a radius constraint on a circle or an arc
int addRadiusConstraint(int geoId, double value);
/// add an angle constraint on a line or between two lines
Expand Down
68 changes: 67 additions & 1 deletion src/Mod/Sketcher/App/SketchObject.cpp
Expand Up @@ -194,7 +194,8 @@ int SketchObject::setDatum(int ConstrId, double Datum)
type != DistanceX &&
type != DistanceY &&
type != Radius &&
type != Angle)
type != Angle &&
type != Tangent)//for tangent, value==0 is autodecide, value==Pi/2 is external and value==-Pi/2 is internal
return -1;

if ((type == Distance || type == Radius) && Datum <= 0)
Expand Down Expand Up @@ -429,13 +430,32 @@ int SketchObject::setConstruction(int GeoId, bool on)
return 0;
}

//ConstraintList is used only to make copies.
int SketchObject::addConstraints(const std::vector<Constraint *> &ConstraintList)
{
const std::vector< Constraint * > &vals = this->Constraints.getValues();

std::vector< Constraint * > newVals(vals);
newVals.insert(newVals.end(), ConstraintList.begin(), ConstraintList.end());

//test if tangent constraints have been added; AutoLockTangency.
std::vector< Constraint * > tbd;//list of temporary copies that need to be deleted
for(int i = newVals.size()-ConstraintList.size(); i<newVals.size(); i++){
if( newVals[i]->Type == Tangent ){
Constraint *constNew = newVals[i]->clone();
AutoLockTangency(constNew);
tbd.push_back(constNew);
newVals[i] = constNew;
}
}

this->Constraints.setValues(newVals);

//clean up - delete temporary copies of constraints that were made to affect the constraints
for(int i=0; i<tbd.size(); i++){
delete (tbd[i]);
}

return this->Constraints.getSize()-1;
}

Expand All @@ -445,6 +465,10 @@ int SketchObject::addConstraint(const Constraint *constraint)

std::vector< Constraint * > newVals(vals);
Constraint *constNew = constraint->clone();

if (constNew->Type == Tangent)
AutoLockTangency(constNew);

newVals.push_back(constNew);
this->Constraints.setValues(newVals);
delete constNew;
Expand Down Expand Up @@ -2268,6 +2292,48 @@ int SketchObject::getVertexIndexGeoPos(int GeoId, PointPos PosId) const
return -1;
}

///Locks tangency type of a tangent constraint.
///The constraint passed must be writable (i.e. the one that is not
/// yet in the constraint list).
///Tangency type (internal/external) is derived from current geometry
/// the constraint refers to.
///
///This function catches exceptions, because it's not a reason to
/// not create a constraint if tangency type cannot be determined.
void SketchObject::AutoLockTangency(Constraint *cstr)
{
try{
assert (cstr->Type==Tangent);
if(cstr->Value != 0.0) /*tangency type already set*/
return;
//decide on tangency type. Write the angle value into the datum field of the constraint.
int geoId1, geoId2, geoIdPt;
PointPos posPt;
geoId1 = cstr->First;
geoId2 = cstr->Second;
geoIdPt = cstr->Third;
posPt = cstr->ThirdPos;
if (geoIdPt == Constraint::GeoUndef){//not tangent-via-point, try endpoint-to-endpoint...
geoIdPt = cstr->First;
posPt = cstr->FirstPos;
}
if (posPt == none){//not endpoint-to-curve and not endpoint-to-endpoint tangent (is simple tangency)
//no tangency lockdown is implemented for simple tangency
} else {
Base::Vector3d p = getPoint(geoIdPt, posPt);
double ang = calculateAngleViaPoint(geoId1, geoId2, p.x, p.y);
if ( abs(ang) > M_PI/2 )
cstr->Value = M_PI/2; //external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is invalid and threated as "undecided".
else
cstr->Value = -M_PI/2; //internal tangency.
}
} catch (Base::Exception& e){
//failure to determine tangency type is not a big deal, so a warning.
assert(0);//but it shouldn't happen (failure to determine tangency type)!
Base::Console().Warning("Error in AutoLockTangency. %s", e.what());
}
}

// Python Sketcher feature ---------------------------------------------------------

namespace App {
Expand Down
2 changes: 2 additions & 0 deletions src/Mod/Sketcher/App/SketchObject.h
Expand Up @@ -180,6 +180,8 @@ class SketcherExport SketchObject : public Part::Part2DObject

std::vector<int> VertexId2GeoId;
std::vector<PointPos> VertexId2PosId;

void AutoLockTangency(Constraint* cstr);
};

typedef App::FeaturePythonT<SketchObject> SketchObjectPython;
Expand Down

0 comments on commit ef77191

Please sign in to comment.