AngleViaPoint Constraint
AngleViaPoint: fixes in UI routines + new messages Goofed undo message in tangency via point is fixed. Forgotten updateActive, clearSelection have been added. New more informative error messages for tangent constraint. AngleViaPoint: using it instead of via line tangency * replaced the helper construction line for ellipse-to-ellipse and similar tangency with a point. Using tangent-via-point there * deleted tangency via line for point-to-point on (cherry picked from commit 9e3fa8c8de0f49c0ef3c978e015eb905358dbdd9) AngleViaPoint: internal/external tangency locking *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. AngleViaPoint: added license to Geo.cpp AngleViaPoint: renames in Constraints.cpp/.h Changed some names to increase self-explanatoryness: bool "remapped" renamed to "pvecChangedFlag" "ReconstructEverything()" renamed to "ReconstructGeomPointers()" AngleViaPoint: renames in Constraints.cpp/.h Changed some names to increase self-explanatoryness: bool "remapped" renamed to "pvecChangedFlag" "ReconstructEverything()" renamed to "ReconstructGeomPointers()" AngleViaPoint: using for endpoint perpendicularity + direction lockdown, just as with tangency. + quite a lot of old code is gone because of that AngleViaPoint: perp-ty UI routine made similar to tangent (Git has made a very messy diff.) The changes are: * Perpendicularity-via-point (3-element selection) support added. * Endpoint-to-curve and endpoint-to-endpoint supports all shape combinations. * a bit of code cleanup and clarifications. AngleViaPoint: placement of perpendicular icon in 3d view AngleViaPoint: fix: allow setDatum of perpendicular constraint AngleViaPoint: fix: centers of ellipses are not endpoints isSimpleVertex used to return false for centers of ellipses and arcs of ellipses, which made them being accepted for point-to-point tangency. Should be fixed forever, mo more changes are expected to be necessary for new types of geometry. AngleViaPoint: precalc with OCC (work in progress) Work in progress (not yet working). Using OCC's tangent to replace implementation of SketchObject::calculateAngleViaPoint. AngleViaPoint: fix math: normal now points inwards, where it was intended initially and goofed up. AngleViaPoint: adding comments to the code AngleViaPoint: using GeomCurve::closestParameterToBasicCurve for angle precalculation AngleViaPoint: Py method: changeConstraintsLocking changeConstraintsLocking(True) - locks/re-locks all lockable tangency/perpendicularity constraints of the sketch (applicable to existing sketches). changeConstraintsLocking(False) - removes locking information from lockable constraints AngleViaPoint: final SketchObject::calculateAngleViaPoint Now, finally, using OCC functionality (thanks Abdullah!), without composing temporary Sketch object.
This commit is contained in:
parent
9f821e06b5
commit
3838bddcdb
|
@ -276,6 +276,17 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
|||
this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos) intArg4;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp("PerpendicularViaPoint", ConstraintType) == 0) {
|
||||
this->getConstraintPtr()->Type = Perpendicular;
|
||||
//valid = true;//non-standard assignment
|
||||
this->getConstraintPtr()->First = intArg1;
|
||||
this->getConstraintPtr()->FirstPos = Sketcher::none;
|
||||
this->getConstraintPtr()->Second = intArg2;
|
||||
this->getConstraintPtr()->SecondPos = Sketcher::none;
|
||||
this->getConstraintPtr()->Third = intArg3;
|
||||
this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos) intArg4;
|
||||
return 0;
|
||||
}
|
||||
if (valid) {
|
||||
this->getConstraintPtr()->First = intArg1;
|
||||
this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) intArg2;
|
||||
|
@ -412,6 +423,12 @@ std::string ConstraintPy::representation(void) const
|
|||
else
|
||||
result << "'TangentViaPoint'>";
|
||||
break;
|
||||
case Perpendicular :
|
||||
if (this->getConstraintPtr()->Third == Constraint::GeoUndef)
|
||||
result << "'Perpendicular'>";
|
||||
else
|
||||
result << "'PerpendicularViaPoint'>";
|
||||
break;
|
||||
case Distance : result << "'Distance'>";break;
|
||||
case Angle :
|
||||
if (this->getConstraintPtr()->Third == Constraint::GeoUndef)
|
||||
|
|
|
@ -674,31 +674,35 @@ int Sketch::addConstraint(const Constraint *constraint)
|
|||
rtn = addParallelConstraint(constraint->First,constraint->Second);
|
||||
break;
|
||||
case Perpendicular:
|
||||
if (constraint->SecondPos != none) // perpendicularity at common point
|
||||
rtn = addPerpendicularConstraint(constraint->First,constraint->FirstPos,
|
||||
constraint->Second,constraint->SecondPos);
|
||||
else if (constraint->Second != Constraint::GeoUndef) {
|
||||
if (constraint->FirstPos != none) // "First" is a connecting point
|
||||
rtn = addPerpendicularConstraint(constraint->First,constraint->FirstPos,
|
||||
constraint->Second);
|
||||
else // simple perpendicularity
|
||||
rtn = addPerpendicularConstraint(constraint->First,constraint->Second);
|
||||
if (constraint->FirstPos == none &&
|
||||
constraint->SecondPos == none &&
|
||||
constraint->Third == Constraint::GeoUndef){
|
||||
//simple perpendicularity
|
||||
rtn = addPerpendicularConstraint(constraint->First,constraint->Second);
|
||||
} else {
|
||||
//any other point-wise perpendicularity
|
||||
rtn = addAngleAtPointConstraint(
|
||||
constraint->First, constraint->FirstPos,
|
||||
constraint->Second, constraint->SecondPos,
|
||||
constraint->Third, constraint->ThirdPos,
|
||||
constraint->Value, constraint->Type);
|
||||
}
|
||||
|
||||
|
||||
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 = addAngleAtPointConstraint(
|
||||
constraint->First, constraint->FirstPos,
|
||||
constraint->Second, constraint->SecondPos,
|
||||
constraint->Third, constraint->ThirdPos,
|
||||
constraint->Value, constraint->Type);
|
||||
}
|
||||
break;
|
||||
case Distance:
|
||||
|
@ -718,11 +722,11 @@ int Sketch::addConstraint(const Constraint *constraint)
|
|||
break;
|
||||
case Angle:
|
||||
if (constraint->Third != Constraint::GeoUndef){
|
||||
rtn = addAngleViaPointConstraint (
|
||||
constraint->First,
|
||||
constraint->Second,
|
||||
rtn = addAngleAtPointConstraint (
|
||||
constraint->First, constraint->FirstPos,
|
||||
constraint->Second, constraint->SecondPos,
|
||||
constraint->Third, constraint->ThirdPos,
|
||||
constraint->Value);
|
||||
constraint->Value, constraint->Type);
|
||||
} else if (constraint->SecondPos != none) // angle between two lines (with explicit start points)
|
||||
rtn = addAngleConstraint(constraint->First,constraint->FirstPos,
|
||||
constraint->Second,constraint->SecondPos,constraint->Value);
|
||||
|
@ -1034,187 +1038,6 @@ int Sketch::addPerpendicularConstraint(int geoId1, int geoId2)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// perpendicularity at specific point constraint
|
||||
int Sketch::addPerpendicularConstraint(int geoId1, PointPos pos1, int geoId2)
|
||||
{
|
||||
// accepts the following combinations:
|
||||
// 1) Line1, start/end, Line2/Circle2/Arc2
|
||||
// 2) Arc1, start/end, Line2/Circle2/Arc2
|
||||
geoId1 = checkGeoId(geoId1);
|
||||
geoId2 = checkGeoId(geoId2);
|
||||
|
||||
int pointId1 = getPointId(geoId1, pos1);
|
||||
|
||||
if (pointId1 < 0 || pointId1 >= int(Points.size()))
|
||||
return addPerpendicularConstraint(geoId1, geoId2);
|
||||
|
||||
GCS::Point &p1 = Points[pointId1];
|
||||
if (Geoms[geoId1].type == Line) {
|
||||
GCS::Line &l1 = Lines[Geoms[geoId1].index];
|
||||
if (Geoms[geoId2].type == Line) {
|
||||
GCS::Line &l2 = Lines[Geoms[geoId2].index];
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPointOnLine(p1, l2, tag);
|
||||
GCSsys.addConstraintPerpendicular(l1, l2, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (Geoms[geoId2].type == Arc) {
|
||||
GCS::Arc &a2 = Arcs[Geoms[geoId2].index];
|
||||
GCS::Point &p2 = Points[Geoms[geoId2].midPointId];
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPointOnArc(p1, a2, tag);
|
||||
GCSsys.addConstraintPointOnLine(p2, l1, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (Geoms[geoId2].type == Circle) {
|
||||
GCS::Circle &c2 = Circles[Geoms[geoId2].index];
|
||||
GCS::Point &p2 = Points[Geoms[geoId2].midPointId];
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPointOnCircle(p1, c2, tag);
|
||||
GCSsys.addConstraintPointOnLine(p2, l1, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (Geoms[geoId2].type == Ellipse) {
|
||||
|
||||
GCS::Ellipse &c2 = Ellipses[Geoms[geoId2].index];
|
||||
GCS::Point &p2 = Points[Geoms[geoId2].midPointId];
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPointOnEllipse(p1, c2, tag);
|
||||
GCSsys.addConstraintPointOnLine(p2, l1, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
}
|
||||
else if (Geoms[geoId1].type == Arc) {
|
||||
GCS::Arc &a1 = Arcs[Geoms[geoId1].index];
|
||||
if (Geoms[geoId2].type == Line) {
|
||||
GCS::Line &l2 = Lines[Geoms[geoId2].index];
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPointOnLine(p1, l2, tag);
|
||||
GCSsys.addConstraintPointOnLine(a1.center, l2, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (Geoms[geoId2].type == Arc || Geoms[geoId2].type == Circle || Geoms[geoId2].type == Ellipse) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCS::Point ¢er = Points[Geoms[geoId2].midPointId];
|
||||
double *radius;
|
||||
if (Geoms[geoId2].type == Arc) {
|
||||
GCS::Arc &a2 = Arcs[Geoms[geoId2].index];
|
||||
radius = a2.rad;
|
||||
}
|
||||
else if (Geoms[geoId2].type == Circle) {
|
||||
GCS::Circle &c2 = Circles[Geoms[geoId2].index];
|
||||
radius = c2.rad;
|
||||
}
|
||||
else {
|
||||
|
||||
GCS::Ellipse &c2 = Ellipses[Geoms[geoId2].index];
|
||||
radius = c2.radmin;
|
||||
}
|
||||
if (pos1 == start)
|
||||
GCSsys.addConstraintPerpendicularCircle2Arc(center, radius, a1, tag);
|
||||
else if (pos1 == end)
|
||||
GCSsys.addConstraintPerpendicularArc2Circle(a1, center, radius, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// perpendicularity at common point constraint
|
||||
int Sketch::addPerpendicularConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2)
|
||||
{
|
||||
// accepts the following combinations:
|
||||
// 1) Line1, start/end, Line2/Arc2, start/end
|
||||
// 2) Arc1, start/end, Line2, start/end (converted to case #1)
|
||||
// 3) Arc1, start/end, Arc2, start/end
|
||||
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()))
|
||||
return -1;
|
||||
|
||||
GCS::Point &p1 = Points[pointId1];
|
||||
GCS::Point &p2 = Points[pointId2];
|
||||
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.addConstraintP2PCoincident(p1, p2, tag);
|
||||
GCSsys.addConstraintPerpendicular(l1, l2, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else {
|
||||
std::swap(geoId1, geoId2);
|
||||
std::swap(pos1, pos2);
|
||||
std::swap(pointId1, pointId2);
|
||||
p1 = Points[pointId1];
|
||||
p2 = Points[pointId2];
|
||||
}
|
||||
}
|
||||
|
||||
if (Geoms[geoId1].type == Line) {
|
||||
GCS::Line &l1 = Lines[Geoms[geoId1].index];
|
||||
if (Geoms[geoId2].type == Arc) {
|
||||
GCS::Arc &a2 = Arcs[Geoms[geoId2].index];
|
||||
if (pos2 == start) {
|
||||
if (pos1 == start) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPerpendicularLine2Arc(l1.p2, l1.p1, a2, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (pos1 == end) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPerpendicularLine2Arc(l1.p1, l1.p2, a2, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
}
|
||||
else if (pos2 == end) {
|
||||
if (pos1 == start) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPerpendicularArc2Line(a2, l1.p1, l1.p2, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (pos1 == end) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintPerpendicularArc2Line(a2, l1.p2, l1.p1, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (Geoms[geoId1].type == Arc) {
|
||||
GCS::Arc &a1 = Arcs[Geoms[geoId1].index];
|
||||
if (Geoms[geoId2].type == Arc) {
|
||||
GCS::Arc &a2 = Arcs[Geoms[geoId2].index];
|
||||
if (pos1 == start && (pos2 == start || pos2 == end)) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
if (pos2 == start)
|
||||
GCSsys.addConstraintPerpendicularArc2Arc(a1, true, a2, false, tag);
|
||||
else // if (pos2 == end)
|
||||
GCSsys.addConstraintPerpendicularArc2Arc(a1, true, a2, true, tag);
|
||||
// GCSsys.addConstraintTangentArc2Arc(a2, false, a1, false, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
else if (pos1 == end && (pos2 == start || pos2 == end)) {
|
||||
int tag = ++ConstraintsCounter;
|
||||
if (pos2 == start)
|
||||
GCSsys.addConstraintPerpendicularArc2Arc(a1, false, a2, false, tag);
|
||||
else // if (pos2 == end)
|
||||
GCSsys.addConstraintPerpendicularArc2Arc(a1, false, a2, true, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// simple tangency constraint
|
||||
int Sketch::addTangentConstraint(int geoId1, int geoId2)
|
||||
{
|
||||
|
@ -1324,132 +1147,122 @@ 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, 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 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)
|
||||
{
|
||||
|
||||
if(!(cTyp == Angle || cTyp == Tangent || cTyp == Perpendicular)) {
|
||||
assert(0);//none of the three types. Why are we here??
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool avp = geoId3!=Constraint::GeoUndef; //is angle-via-point?
|
||||
bool e2c = pos2 == none && pos1 != none;//is endpoint-to-curve?
|
||||
bool e2e = pos2 != none && pos1 != 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);
|
||||
|
||||
int pointId1 = getPointId(geoId1, pos1);
|
||||
|
||||
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");
|
||||
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);
|
||||
|
||||
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");
|
||||
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(avp)
|
||||
geoId3 = checkGeoId(geoId3);
|
||||
|
||||
if (Geoms[geoId1].type == Point ||
|
||||
Geoms[geoId2].type == Point)
|
||||
return -1;//first two objects must be curves!
|
||||
Geoms[geoId2].type == Point){
|
||||
assert(0);//point is not a curve. No tangency/whatever!
|
||||
return -1;
|
||||
}
|
||||
|
||||
GCS::Curve* crv1 =getGCSCurveByGeoId(geoId1);
|
||||
GCS::Curve* crv2 =getGCSCurveByGeoId(geoId2);
|
||||
if (!crv1 || !crv2) {
|
||||
Base::Console().Error("addTangentViaPointConstraint: getGCSCurveByGeoId returned NULL!\n");
|
||||
assert(0);
|
||||
Base::Console().Error("addAngleAtPointConstraint: getGCSCurveByGeoId returned NULL!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pointId = getPointId(geoId3, pos3);;
|
||||
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("addTangentViaPointConstraint: point index out of range.\n");
|
||||
assert(0);
|
||||
Base::Console().Error("addAngleAtPointConstraint: 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("addAngleAtPointConstraint: 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;
|
||||
//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)
|
||||
*angle = value;
|
||||
else {
|
||||
//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(abs(angleErr) > M_PI/2 )
|
||||
angleDesire += M_PI;
|
||||
|
||||
*angle = angleDesire;
|
||||
} else
|
||||
*angle = value-angleOffset;
|
||||
}
|
||||
|
||||
|
||||
int tag = -1;
|
||||
if(e2c)
|
||||
tag = Sketch::addPointOnObjectConstraint(geoId1, pos1, geoId2);//increases ConstraintsCounter
|
||||
if (e2e){
|
||||
tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintP2PCoincident(p, *p2, tag);
|
||||
}
|
||||
if(avp)
|
||||
tag = ++ConstraintsCounter;
|
||||
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
|
@ -1644,30 +1457,6 @@ int Sketch::addAngleConstraint(int geoId1, PointPos pos1, int geoId2, PointPos p
|
|||
return ConstraintsCounter;
|
||||
}
|
||||
|
||||
int Sketch::addAngleViaPointConstraint(int geoId1, int geoId2, int geoId3, PointPos pos3, double value)
|
||||
{
|
||||
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);
|
||||
int pointId = getPointId(geoId3, pos3);;
|
||||
GCS::Point &p = Points[pointId];
|
||||
|
||||
// add the parameter for the angle
|
||||
FixParameters.push_back(new double(value));
|
||||
double *angle = FixParameters[FixParameters.size()-1];
|
||||
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag);
|
||||
return ConstraintsCounter;
|
||||
|
||||
}
|
||||
|
||||
int Sketch::addEqualConstraint(int geoId1, int geoId2)
|
||||
{
|
||||
|
|
|
@ -160,13 +160,14 @@ public:
|
|||
int addParallelConstraint(int geoId1, int geoId2);
|
||||
/// add a perpendicular constraint between two lines
|
||||
int addPerpendicularConstraint(int geoId1, int geoId2);
|
||||
int addPerpendicularConstraint(int geoId1, PointPos pos1, int geoId2);
|
||||
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 addAngleAtPointConstraint(
|
||||
int geoId1, PointPos pos1,
|
||||
int geoId2, PointPos pos2,
|
||||
int geoId3, PointPos pos3,
|
||||
double value,
|
||||
ConstraintType cTyp);
|
||||
/// 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
|
||||
|
|
|
@ -194,7 +194,9 @@ 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
|
||||
type != Perpendicular)
|
||||
return -1;
|
||||
|
||||
if ((type == Distance || type == Radius) && Datum <= 0)
|
||||
|
@ -435,13 +437,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 || newVals[i]->Type == Perpendicular ){
|
||||
Constraint *constNew = newVals[i]->clone();
|
||||
AutoLockTangencyAndPerpty(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;
|
||||
}
|
||||
|
||||
|
@ -451,6 +472,10 @@ int SketchObject::addConstraint(const Constraint *constraint)
|
|||
|
||||
std::vector< Constraint * > newVals(vals);
|
||||
Constraint *constNew = constraint->clone();
|
||||
|
||||
if (constNew->Type == Tangent || constNew->Type == Perpendicular)
|
||||
AutoLockTangencyAndPerpty(constNew);
|
||||
|
||||
newVals.push_back(constNew);
|
||||
this->Constraints.setValues(newVals);
|
||||
delete constNew;
|
||||
|
@ -2264,16 +2289,35 @@ void SketchObject::validateConstraints()
|
|||
}
|
||||
}
|
||||
|
||||
//This function is necessary for precalculation of an angle when adding
|
||||
// an angle constraint. It is also used here, in SketchObject, to
|
||||
// lock down the type of tangency/perpendicularity.
|
||||
double SketchObject::calculateAngleViaPoint(int GeoId1, int GeoId2, double px, double py)
|
||||
{
|
||||
//DeepSOIC: this may be slow, but I wanted to reuse the conversion from Geometry to GCS shapes that is done in Sketch
|
||||
Sketcher::Sketch sk;
|
||||
int i1 = sk.addGeometry(this->getGeometry(GeoId1));
|
||||
int i2 = sk.addGeometry(this->getGeometry(GeoId2));
|
||||
const Part::GeomCurve &g1 = *(dynamic_cast<const Part::GeomCurve*>(this->getGeometry(GeoId1)));
|
||||
const Part::GeomCurve &g2 = *(dynamic_cast<const Part::GeomCurve*>(this->getGeometry(GeoId2)));
|
||||
Base::Vector3d p(px, py, 0.0);
|
||||
|
||||
return sk.calculateAngleViaPoint(i1,i2,px,py);
|
||||
double u1 = 0.0;
|
||||
double u2 = 0.0;
|
||||
if (! g1.closestParameterToBasicCurve(p, u1) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: closestParameter(curve1) failed!");
|
||||
if (! g2.closestParameterToBasicCurve(p, u2) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: closestParameter(curve2) failed!");
|
||||
|
||||
gp_Dir tan1, tan2;
|
||||
if (! g1.tangent(u1,tan1) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: tangent1 failed!");
|
||||
if (! g2.tangent(u2,tan2) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: tangent2 failed!");
|
||||
|
||||
assert(abs(tan1.Z())<0.0001);
|
||||
assert(abs(tan2.Z())<0.0001);
|
||||
|
||||
double ang = atan2(-tan2.X()*tan1.Y()+tan2.Y()*tan1.X(), tan2.X()*tan1.X() + tan2.Y()*tan1.Y());
|
||||
return ang;
|
||||
}
|
||||
|
||||
//Tests if the provided point lies exactly in a curve (satisfies
|
||||
// point-on-object constraint). It is used to decide whether it is nesessary to
|
||||
// constrain a point onto curves when 3-element selection tangent-via-point-like
|
||||
// constraints are applied.
|
||||
bool SketchObject::isPointOnCurve(int geoIdCurve, double px, double py)
|
||||
{
|
||||
//DeepSOIC: this may be slow, but I wanted to reuse the existing code
|
||||
|
@ -2288,6 +2332,7 @@ bool SketchObject::isPointOnCurve(int geoIdCurve, double px, double py)
|
|||
return err*err < 10.0*sk.getSolverPrecision();
|
||||
}
|
||||
|
||||
//This one was done just for fun to see to what precision the constraints are solved.
|
||||
double SketchObject::calculateConstraintError(int ConstrId)
|
||||
{
|
||||
Sketcher::Sketch sk;
|
||||
|
@ -2409,6 +2454,124 @@ int SketchObject::getVertexIndexGeoPos(int GeoId, PointPos PosId) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
///changeConstraintsLocking locks or unlocks all tangent and perpendicular
|
||||
/// constraints. (Constraint locking prevents it from flipping to another valid
|
||||
/// configuration, when e.g. external geometry is updated from outside.) The
|
||||
/// sketch solve is not triggered by the function, but the SketchObject is
|
||||
/// touched (a recompute will be necessary). The geometry should not be affected
|
||||
/// by the function.
|
||||
///The bLock argument specifies, what to do. If true, all constraints are
|
||||
/// unlocked and locked again. If false, all tangent and perp. constraints are
|
||||
/// unlocked.
|
||||
int SketchObject::changeConstraintsLocking(bool bLock)
|
||||
{
|
||||
int cntSuccess = 0;
|
||||
int cntToBeAffected = 0;//==cntSuccess+cntFail
|
||||
const std::vector< Constraint * > &vals = this->Constraints.getValues();
|
||||
|
||||
std::vector< Constraint * > newVals(vals);//modifiable copy of pointers array
|
||||
|
||||
std::vector< Constraint * > tbd;//list of temporary Constraint copies that need to be deleted later
|
||||
|
||||
for(int i = 0; i<newVals.size(); i++){
|
||||
if( newVals[i]->Type == Tangent || newVals[i]->Type == Perpendicular ){
|
||||
//create a constraint copy, affect it, replace the pointer
|
||||
cntToBeAffected++;
|
||||
Constraint *constNew = newVals[i]->clone();
|
||||
bool ret = AutoLockTangencyAndPerpty(constNew, /*bForce=*/true, bLock);
|
||||
if (ret) cntSuccess++;
|
||||
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]);
|
||||
}
|
||||
|
||||
Base::Console().Log("ChangeConstraintsLocking: affected %i of %i tangent/perp constraints\n",
|
||||
cntSuccess, cntToBeAffected);
|
||||
|
||||
return cntSuccess;
|
||||
}
|
||||
|
||||
///Locks tangency/perpendicularity type of such a 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.
|
||||
///Same for perpendicularity type.
|
||||
///
|
||||
///This function catches exceptions, because it's not a reason to
|
||||
/// not create a constraint if tangency/perp-ty type cannot be determined.
|
||||
///
|
||||
///Arguments:
|
||||
/// cstr - pointer to a constraint to be locked/unlocked
|
||||
/// bForce - specifies whether to ignore tha already locked constraint or not.
|
||||
/// bLock - specufies whether to lock the constraint or not (if bForce is
|
||||
/// true, the constraint gets unlocked, otherwise nothing is done at all).
|
||||
///
|
||||
///Return values:
|
||||
/// true - success.
|
||||
/// false - fail (this indicates an error, or that a constraint locking isn't supported).
|
||||
bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool bLock)
|
||||
{
|
||||
try{
|
||||
assert ( cstr->Type == Tangent || cstr->Type == Perpendicular);
|
||||
if(cstr->Value != 0.0 && ! bForce) /*tangency type already set. If not bForce - don't touch.*/
|
||||
return true;
|
||||
if(!bLock){
|
||||
cstr->Value=0.0;//reset
|
||||
} else {
|
||||
//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. Do nothing.
|
||||
return false;
|
||||
} else {
|
||||
Base::Vector3d p = getPoint(geoIdPt, posPt);
|
||||
|
||||
//this piece of code is also present in Sketch.cpp, correct for offset
|
||||
//and to do the autodecision for old sketches.
|
||||
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 (cstr->Type == Tangent) {angleOffset = -M_PI/2; angleDesire = 0.0;}
|
||||
if (cstr->Type == Perpendicular) {angleOffset = 0; angleDesire = M_PI/2;}
|
||||
|
||||
double angleErr = calculateAngleViaPoint(geoId1, geoId2, p.x, p.y) - 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(abs(angleErr) > M_PI/2 )
|
||||
angleDesire += M_PI;
|
||||
|
||||
cstr->Value = angleDesire + angleOffset; //external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is invalid and threated as "undecided".
|
||||
}
|
||||
}
|
||||
} 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 \n", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Python Sketcher feature ---------------------------------------------------------
|
||||
|
||||
namespace App {
|
||||
|
|
|
@ -155,6 +155,7 @@ public:
|
|||
double calculateAngleViaPoint(int geoId1, int geoId2, double px, double py);
|
||||
bool isPointOnCurve(int geoIdCurve, double px, double py);
|
||||
double calculateConstraintError(int ConstrId);
|
||||
int changeConstraintsLocking(bool bLock);
|
||||
|
||||
// from base class
|
||||
virtual PyObject *getPyObject(void);
|
||||
|
@ -185,6 +186,8 @@ private:
|
|||
|
||||
std::vector<int> VertexId2GeoId;
|
||||
std::vector<PointPos> VertexId2PosId;
|
||||
|
||||
bool AutoLockTangencyAndPerpty(Constraint* cstr, bool bForce = false, bool bLock = true);
|
||||
};
|
||||
|
||||
typedef App::FeaturePythonT<SketchObject> SketchObjectPython;
|
||||
|
|
|
@ -157,6 +157,23 @@
|
|||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="changeConstraintsLocking">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
changeConstraintsLocking(bLock) - locks or unlocks all tangent and
|
||||
perpendicular constraints. (Constraint locking prevents it from
|
||||
flipping to another valid configuration, when e.g. external geometry
|
||||
is updated from outside.) The sketch solve is not triggered by the
|
||||
function, but the SketchObject is touched (a recompute will be
|
||||
necessary). The geometry should not be affected by the function.
|
||||
|
||||
The bLock argument specifies, what to do. If true, all constraints
|
||||
are unlocked and locked again. If false, all tangent and perp.
|
||||
constraints are unlocked.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
|
||||
<Attribute Name="ConstraintCount" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Number of Constraints in this sketch</UserDocu>
|
||||
|
|
|
@ -729,6 +729,19 @@ PyObject* SketchObjectPy::calculateConstraintError(PyObject *args)
|
|||
return Py::new_reference_to(Py::Float(err));
|
||||
}
|
||||
|
||||
PyObject* SketchObjectPy::changeConstraintsLocking(PyObject *args)
|
||||
{
|
||||
int bLock=0;
|
||||
if (!PyArg_ParseTuple(args, "i", &bLock))
|
||||
return 0;
|
||||
|
||||
SketchObject* obj = this->getSketchObjectPtr();
|
||||
|
||||
int naff = obj->changeConstraintsLocking((bool)bLock);
|
||||
|
||||
return Py::new_reference_to(Py::Int(naff));
|
||||
}
|
||||
|
||||
PyObject* SketchObjectPy::ExposeInternalGeometry(PyObject *args)
|
||||
{
|
||||
int GeoId;
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace GCS
|
|||
///////////////////////////////////////
|
||||
|
||||
Constraint::Constraint()
|
||||
: origpvec(0), pvec(0), scale(1.), tag(0), remapped(true)
|
||||
: origpvec(0), pvec(0), scale(1.), tag(0), pvecChangedFlag(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -50,13 +50,13 @@ void Constraint::redirectParams(MAP_pD_pD redirectionmap)
|
|||
if (it != redirectionmap.end())
|
||||
pvec[i] = it->second;
|
||||
}
|
||||
remapped=true;
|
||||
pvecChangedFlag=true;
|
||||
}
|
||||
|
||||
void Constraint::revertParams()
|
||||
{
|
||||
pvec = origpvec;
|
||||
remapped=true;
|
||||
pvecChangedFlag=true;
|
||||
}
|
||||
|
||||
ConstraintType Constraint::getTypeId()
|
||||
|
@ -2195,7 +2195,7 @@ ConstraintAngleViaPoint::ConstraintAngleViaPoint(Curve &acrv1, Curve &acrv2, Poi
|
|||
crv1 = acrv1.Copy();
|
||||
crv2 = acrv2.Copy();
|
||||
origpvec = pvec;
|
||||
remapped=true;
|
||||
pvecChangedFlag=true;
|
||||
rescale();
|
||||
}
|
||||
ConstraintAngleViaPoint::~ConstraintAngleViaPoint()
|
||||
|
@ -2204,7 +2204,7 @@ ConstraintAngleViaPoint::~ConstraintAngleViaPoint()
|
|||
delete crv2; crv2 = 0;
|
||||
}
|
||||
|
||||
void ConstraintAngleViaPoint::ReconstructEverything()
|
||||
void ConstraintAngleViaPoint::ReconstructGeomPointers()
|
||||
{
|
||||
int cnt=0;
|
||||
cnt++;//skip angle - we have an inline function for that
|
||||
|
@ -2212,7 +2212,7 @@ void ConstraintAngleViaPoint::ReconstructEverything()
|
|||
poa.y = pvec[cnt]; cnt++;
|
||||
crv1->ReconstructOnNewPvec(pvec,cnt);
|
||||
crv2->ReconstructOnNewPvec(pvec,cnt);
|
||||
remapped=false;
|
||||
pvecChangedFlag=false;
|
||||
}
|
||||
|
||||
ConstraintType ConstraintAngleViaPoint::getTypeId()
|
||||
|
@ -2227,7 +2227,7 @@ void ConstraintAngleViaPoint::rescale(double coef)
|
|||
|
||||
double ConstraintAngleViaPoint::error()
|
||||
{
|
||||
if (remapped) ReconstructEverything();
|
||||
if (pvecChangedFlag) ReconstructGeomPointers();
|
||||
double ang=*angle();
|
||||
Vector2D n1 = crv1->CalculateNormal(poa);
|
||||
Vector2D n2 = crv2->CalculateNormal(poa);
|
||||
|
@ -2254,7 +2254,7 @@ double ConstraintAngleViaPoint::grad(double *param)
|
|||
|
||||
double deriv=0.;
|
||||
|
||||
if (remapped) ReconstructEverything();
|
||||
if (pvecChangedFlag) ReconstructGeomPointers();
|
||||
|
||||
if (param == angle()) deriv += -1.0;
|
||||
Vector2D n1 = crv1->CalculateNormal(poa);
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace GCS
|
|||
InternalAlignmentPoint2Ellipse = 15,
|
||||
EqualMajorAxesEllipse = 16,
|
||||
EllipticalArcRangeToEndPoints = 17,
|
||||
AngleViaPoint = 19 //DeepSOIC: skipped 18 for the very unlikely case that tangency via point will be included
|
||||
AngleViaPoint = 18
|
||||
};
|
||||
|
||||
enum InternalAlignmentType {
|
||||
|
@ -75,7 +75,7 @@ namespace GCS
|
|||
VEC_pD pvec;
|
||||
double scale;
|
||||
int tag;
|
||||
bool remapped; //indicates that pvec has changed and saved pointers must be reconstructed (currently used only in AngleViaPoint)
|
||||
bool pvecChangedFlag; //indicates that pvec has changed and saved pointers must be reconstructed (currently used only in AngleViaPoint)
|
||||
public:
|
||||
Constraint();
|
||||
virtual ~Constraint(){}
|
||||
|
@ -436,9 +436,17 @@ namespace GCS
|
|||
private:
|
||||
inline double* angle() { return pvec[0]; };
|
||||
Curve* crv1;
|
||||
Curve* crv2;//warning: need to be reconstructed if pvec was redirected (test remapped variable before use!)
|
||||
Point poa;//pot=point of angle //warning: needs to be reconstructed if pvec was redirected (test remapped variable before use!)
|
||||
void ReconstructEverything();
|
||||
Curve* crv2;
|
||||
//These two pointers hold copies of the curves that were passed on
|
||||
// constraint creation. The curves must be deleted upon destruction of
|
||||
// the constraint. It is necessary to have copies, since messing with
|
||||
// original objects that were passed is a very bad idea (but messing is
|
||||
// necessary, because we need to support redirectParams()/revertParams
|
||||
// functions.
|
||||
//The pointers in the curves need to be reconstructed if pvec was redirected
|
||||
// (test pvecChangedFlag variable before use!)
|
||||
Point poa;//poa=point of angle //needs to be reconstructed if pvec was redirected/reverted. The point is easily shallow-copied by C++, so no pointer type here and no delete is necessary.
|
||||
void ReconstructGeomPointers(); //writes pointers in pvec to the parameters of crv1, crv2 and poa
|
||||
public:
|
||||
ConstraintAngleViaPoint(Curve &acrv1, Curve &acrv2, Point p, double* angle);
|
||||
~ConstraintAngleViaPoint();
|
||||
|
|
|
@ -1,3 +1,25 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) Victor Titov (DeepSOIC) *
|
||||
* (vv.titov@gmail.com) 2014 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#define DEBUG_DERIVS 0
|
||||
#if DEBUG_DERIVS
|
||||
|
@ -72,24 +94,24 @@ Vector2D Circle::CalculateNormal(Point &p, double* derivparam)
|
|||
Vector2D ret(0.0, 0.0);
|
||||
if(derivparam){
|
||||
if (derivparam == center.x) {
|
||||
ret.x += -1;
|
||||
ret.x += 1;
|
||||
ret.y += 0;
|
||||
};
|
||||
if (derivparam == center.y) {
|
||||
ret.x += 0;
|
||||
ret.y += -1;
|
||||
ret.y += 1;
|
||||
};
|
||||
if (derivparam == p.x) {
|
||||
ret.x += +1;
|
||||
ret.x += -1;
|
||||
ret.y += 0;
|
||||
};
|
||||
if (derivparam == p.y) {
|
||||
ret.x += 0;
|
||||
ret.y += +1;
|
||||
ret.y += -1;
|
||||
};
|
||||
} else {
|
||||
ret.x = pv.x - cv.x;
|
||||
ret.y = pv.y - cv.y;
|
||||
ret.x = cv.x - pv.x;
|
||||
ret.y = cv.y - pv.y;
|
||||
};
|
||||
|
||||
return ret;
|
||||
|
@ -186,8 +208,8 @@ Vector2D Ellipse::CalculateNormal(Point &p, double* derivparam)
|
|||
dpf2e.x += -pf2.x/pow(pf2.length(),2)*(dpf2.x*pf2e.x + dpf2.y*pf2e.y);//second part of normalization dreivative
|
||||
dpf2e.y += -pf2.y/pow(pf2.length(),2)*(dpf2.x*pf2e.x + dpf2.y*pf2e.y);
|
||||
|
||||
ret.x = dpf1e.x + dpf2e.x;
|
||||
ret.y = dpf1e.y + dpf2e.y;//DeepSOIC: derivative calculated manually... error-prone =) Tested, fixed, looks good.
|
||||
ret.x = -(dpf1e.x + dpf2e.x);
|
||||
ret.y = -(dpf1e.y + dpf2e.y);//DeepSOIC: derivative calculated manually... error-prone =) Tested, fixed, looks good.
|
||||
|
||||
//numeric derivatives for testing
|
||||
#if 0 //make sure to enable DEBUG_DERIVS when enabling
|
||||
|
@ -213,8 +235,8 @@ Vector2D Ellipse::CalculateNormal(Point &p, double* derivparam)
|
|||
Vector2D pf2 = Vector2D(pv.x - f2v.x, pv.y - f2v.y);
|
||||
Vector2D pf1e = pf1.getNormalized();
|
||||
Vector2D pf2e = pf2.getNormalized();
|
||||
ret.x = pf1e.x + pf2e.x;
|
||||
ret.y = pf1e.y + pf2e.y;
|
||||
ret.x = -(pf1e.x + pf2e.x);
|
||||
ret.y = -(pf1e.y + pf2e.y);
|
||||
};
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -62,14 +62,13 @@ namespace GCS
|
|||
public:
|
||||
|
||||
//returns normal vector. The vector should point to the left when one
|
||||
//walks along the curve from start to end. Ellipses and circles are
|
||||
//assumed to be walked counterclockwise, so the vector should point
|
||||
//into the shape.
|
||||
|
||||
//derivparam is a pointer to a curve parameter to compute the
|
||||
//derivative for. if derivparam is nullptr, the actual normal vector is
|
||||
//returned, otherwise a derivative of normal vector by *derivparam is
|
||||
//returned
|
||||
// walks along the curve from start to end. Ellipses and circles are
|
||||
// assumed to be walked counterclockwise, so the vector should point
|
||||
// into the shape.
|
||||
//derivparam is a pointer to a curve parameter (or point coordinate) to
|
||||
// compute the derivative for. if derivparam is nullptr, the actual
|
||||
// normal vector is returned, otherwise a derivative of normal vector by
|
||||
// *derivparam is returned.
|
||||
virtual Vector2D CalculateNormal(Point &p, double* derivparam = 0) = 0;
|
||||
|
||||
//adds curve's parameters to pvec (used by constraints)
|
||||
|
|
|
@ -148,21 +148,34 @@ bool isSimpleVertex(const Sketcher::SketchObject* Obj, int GeoId, PointPos PosId
|
|||
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
||||
if (geo->getTypeId() == Part::GeomPoint::getClassTypeId())
|
||||
return true;
|
||||
else if (PosId == Sketcher::mid &&
|
||||
(geo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
||||
geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()))
|
||||
else if (PosId == Sketcher::mid)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Makes a tangency constraint using external construction line between
|
||||
bool IsPointAlreadyOnCurve(int GeoIdCurve, int GeoIdPoint, Sketcher::PointPos PosIdPoint, Sketcher::SketchObject* Obj)
|
||||
{
|
||||
//This func is a "smartness" behind three-element tangent-, perp.- and angle-via-point.
|
||||
//We want to find out, if the point supplied by user is already on
|
||||
// both of the curves. If not, necessary point-on-object constraints
|
||||
// are to be added automatically.
|
||||
//Simple geometric test seems to be the best, because a point can be
|
||||
// constrained to a curve in a number of ways (e.g. it is an endpoint of an
|
||||
// arc, or is coincident to endpoint of an arc, or it is an endpoint of an
|
||||
// ellipse's majopr diameter line). Testing all those possibilities is way
|
||||
// too much trouble, IMO(DeepSOIC).
|
||||
Base::Vector3d p = Obj->getPoint(GeoIdPoint, PosIdPoint);
|
||||
return Obj->isPointOnCurve(GeoIdCurve, p.x, p.y);
|
||||
}
|
||||
|
||||
/// Makes a simple tangency constraint using extra point + tangent via point
|
||||
/// geom1 => an ellipse
|
||||
/// geom2 => any of an ellipse, an arc of ellipse, a circle, or an arc (of circle)
|
||||
/// NOTE: A command must be opened before calling this function, which this function
|
||||
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
||||
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
||||
void SketcherGui::makeTangentToEllipseviaConstructionLine(const Sketcher::SketchObject* Obj,
|
||||
void SketcherGui::makeTangentToEllipseviaNewPoint(const Sketcher::SketchObject* Obj,
|
||||
const Part::Geometry *geom1,
|
||||
const Part::Geometry *geom2,
|
||||
int geoId1,
|
||||
|
@ -193,35 +206,22 @@ void SketcherGui::makeTangentToEllipseviaConstructionLine(const Sketcher::Sketch
|
|||
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
||||
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
||||
|
||||
Base::Vector3d perp = Base::Vector3d(direction.y,-direction.x);
|
||||
|
||||
Base::Vector3d endpoint = PoE+perp;
|
||||
|
||||
int currentgeoid= Obj->getHighestCurveIndex();
|
||||
|
||||
try {
|
||||
// Add a construction line
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
|
||||
Obj->getNameInDocument(),
|
||||
PoE.x,PoE.y,endpoint.x,endpoint.y); // create line for major axis
|
||||
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ",Obj->getNameInDocument(),currentgeoid+1);
|
||||
|
||||
// Add a point
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
||||
Obj->getNameInDocument(), PoE.x,PoE.y);
|
||||
int GeoIdPoint = Obj->getHighestCurveIndex();
|
||||
|
||||
// Point on first object
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,Sketcher::start,geoId1); // constrain major axis
|
||||
Obj->getNameInDocument(),GeoIdPoint,Sketcher::start,geoId1); // constrain major axis
|
||||
// Point on second object
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,Sketcher::start,geoId2); // constrain major axis
|
||||
// tangent to first object
|
||||
Obj->getNameInDocument(),GeoIdPoint,Sketcher::start,geoId2); // constrain major axis
|
||||
// tangent via point
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,geoId1);
|
||||
// tangent to second object
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,geoId2);
|
||||
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
|
||||
Obj->getNameInDocument(), geoId1, geoId2 ,GeoIdPoint, Sketcher::start);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
|
@ -233,13 +233,13 @@ void SketcherGui::makeTangentToEllipseviaConstructionLine(const Sketcher::Sketch
|
|||
Gui::Command::commitCommand();
|
||||
Gui::Command::updateActive();
|
||||
}
|
||||
/// Makes a tangency constraint using external construction line between
|
||||
/// Makes a simple tangency constraint using extra point + tangent via point
|
||||
/// geom1 => an arc of ellipse
|
||||
/// geom2 => any of an arc of ellipse, a circle, or an arc (of circle)
|
||||
/// NOTE: A command must be opened before calling this function, which this function
|
||||
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
||||
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
||||
void SketcherGui::makeTangentToArcOfEllipseviaConstructionLine(const Sketcher::SketchObject* Obj,
|
||||
void SketcherGui::makeTangentToArcOfEllipseviaNewPoint(const Sketcher::SketchObject* Obj,
|
||||
const Part::Geometry *geom1,
|
||||
const Part::Geometry *geom2,
|
||||
int geoId1,
|
||||
|
@ -268,38 +268,22 @@ void SketcherGui::makeTangentToArcOfEllipseviaConstructionLine(const Sketcher::S
|
|||
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
||||
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
||||
|
||||
Base::Vector3d perp = Base::Vector3d(direction.y,-direction.x);
|
||||
|
||||
Base::Vector3d endpoint = PoE+perp;
|
||||
|
||||
int currentgeoid= Obj->getHighestCurveIndex();
|
||||
|
||||
Gui::Command::openCommand("add tangent constraint");
|
||||
|
||||
try {
|
||||
|
||||
// Add a construction line
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
|
||||
Obj->getNameInDocument(),
|
||||
PoE.x,PoE.y,endpoint.x,endpoint.y); // create line for major axis
|
||||
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ",Obj->getNameInDocument(),currentgeoid+1);
|
||||
// Add a point
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
||||
Obj->getNameInDocument(), PoE.x,PoE.y);
|
||||
int GeoIdPoint = Obj->getHighestCurveIndex();
|
||||
|
||||
// Point on first object
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,Sketcher::start,geoId1); // constrain major axis
|
||||
Obj->getNameInDocument(),GeoIdPoint,Sketcher::start,geoId1); // constrain major axis
|
||||
// Point on second object
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,Sketcher::start,geoId2); // constrain major axis
|
||||
// tangent to first object
|
||||
Obj->getNameInDocument(),GeoIdPoint,Sketcher::start,geoId2); // constrain major axis
|
||||
// tangent via point
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,geoId1);
|
||||
// tangent to second object
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
Obj->getNameInDocument(),currentgeoid+1,geoId2);
|
||||
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
|
||||
Obj->getNameInDocument(), geoId1, geoId2 ,GeoIdPoint, Sketcher::start);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
|
@ -1363,13 +1347,22 @@ CmdSketcherConstrainPerpendicular::CmdSketcherConstrainPerpendicular()
|
|||
|
||||
void CmdSketcherConstrainPerpendicular::activated(int iMsg)
|
||||
{
|
||||
QString strBasicHelp =
|
||||
QObject::tr(
|
||||
"There is a number of ways this constraint can be applied.\n\n"
|
||||
"Accepted combinations: two curves; an endpoint and a curve; two endpoints; two curves and a point.",
|
||||
/*disambig.:*/ "perpendicular constraint");
|
||||
QString strError;
|
||||
|
||||
// get the selection
|
||||
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
||||
|
||||
// only one sketch with its subelements are allowed to be selected
|
||||
if (selection.size() != 1) {
|
||||
strError = QObject::tr("Select some geometry from the sketch.", "perpendicular constraint");
|
||||
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select two entities from the sketch."));
|
||||
strError+strBasicHelp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1377,184 +1370,66 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg)
|
|||
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
||||
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
||||
|
||||
if (SubNames.size() != 2) {
|
||||
if (SubNames.size() != 2 && SubNames.size() != 3) {
|
||||
strError = QObject::tr("Wrong number of selected objects!","perpendicular constraint");
|
||||
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select exactly two entities from the sketch."));
|
||||
strError+strBasicHelp);
|
||||
return;
|
||||
}
|
||||
|
||||
int GeoId1, GeoId2;
|
||||
Sketcher::PointPos PosId1, PosId2;
|
||||
int GeoId1, GeoId2, GeoId3;
|
||||
Sketcher::PointPos PosId1, PosId2, PosId3;
|
||||
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
||||
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
||||
|
||||
if (checkBothExternal(GeoId1, GeoId2))
|
||||
if (checkBothExternal(GeoId1, GeoId2)) //checkBothExternal displays error message
|
||||
return;
|
||||
|
||||
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // perpendicularity at common point
|
||||
|
||||
if (isSimpleVertex(Obj, GeoId1, PosId1) ||
|
||||
isSimpleVertex(Obj, GeoId2, PosId2)) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Cannot add a perpendicularity constraint at an unconnected point!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
|
||||
if ((PosId1 != Sketcher::start && PosId1 != Sketcher::end) ||
|
||||
(PosId2 != Sketcher::start && PosId2 != Sketcher::end) ||
|
||||
(geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
||||
geo1->getTypeId() != Part::GeomArcOfCircle::getClassTypeId()) ||
|
||||
(geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
||||
geo2->getTypeId() != Part::GeomArcOfCircle::getClassTypeId())) {
|
||||
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("The selected points should be end points of arcs and lines."));
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand("add perpendicular constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) ||
|
||||
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // connecting point
|
||||
if (isVertex(GeoId2,PosId2)) {
|
||||
if (SubNames.size() == 3) { //perpendicular via point
|
||||
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
||||
//let's sink the point to be GeoId3. We want to keep the order the two curves have been selected in.
|
||||
if ( isVertex(GeoId1, PosId1) ){
|
||||
std::swap(GeoId1,GeoId2);
|
||||
std::swap(PosId1,PosId2);
|
||||
}
|
||||
};
|
||||
if ( isVertex(GeoId2, PosId2) ){
|
||||
std::swap(GeoId2,GeoId3);
|
||||
std::swap(PosId2,PosId3);
|
||||
};
|
||||
|
||||
if (isSimpleVertex(Obj, GeoId1, PosId1)) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Cannot add a perpendicularity constraint at an unconnected point!"));
|
||||
return;
|
||||
}
|
||||
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
||||
|
||||
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
|
||||
if ((PosId1 != Sketcher::start && PosId1 != Sketcher::end) ||
|
||||
(geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
||||
geo1->getTypeId() != Part::GeomArcOfCircle::getClassTypeId())) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("The selected point should be an end point of an arc or line."));
|
||||
return;
|
||||
}
|
||||
else if (geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
||||
geo2->getTypeId() != Part::GeomArcOfCircle::getClassTypeId() &&
|
||||
geo2->getTypeId() != Part::GeomCircle::getClassTypeId()) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("The selected edge should be an arc, line or circle."));
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand("add perpendicularity constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,PosId1,GeoId2);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
else if (isEdge(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) { // simple perpendicularity between GeoId1 and GeoId2
|
||||
|
||||
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
|
||||
if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
||||
geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("One of the selected edges should be a line."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
||||
std::swap(GeoId1,GeoId2);
|
||||
|
||||
// GeoId2 is the line
|
||||
geo1 = Obj->getGeometry(GeoId1);
|
||||
geo2 = Obj->getGeometry(GeoId2);
|
||||
|
||||
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
||||
geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ) {
|
||||
|
||||
Base::Vector3d center;
|
||||
double majord;
|
||||
double minord;
|
||||
double phi;
|
||||
|
||||
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ){
|
||||
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo1);
|
||||
|
||||
center=ellipse->getCenter();
|
||||
majord=ellipse->getMajorRadius();
|
||||
minord=ellipse->getMinorRadius();
|
||||
phi=ellipse->getAngleXU();
|
||||
} else
|
||||
if( geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ){
|
||||
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo1);
|
||||
|
||||
center=aoe->getCenter();
|
||||
majord=aoe->getMajorRadius();
|
||||
minord=aoe->getMinorRadius();
|
||||
phi=aoe->getAngleXU();
|
||||
}
|
||||
|
||||
const Part::GeomLineSegment *line = static_cast<const Part::GeomLineSegment *>(geo2);
|
||||
|
||||
Base::Vector3d point1=line->getStartPoint();
|
||||
Base::Vector3d point2=line->getEndPoint();
|
||||
|
||||
Base::Vector3d direction=point1-center;
|
||||
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomally by the polar
|
||||
|
||||
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
||||
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
||||
|
||||
Base::Vector3d perp = Base::Vector3d(direction.y,-direction.x);
|
||||
|
||||
Base::Vector3d endpoint = PoE+perp;
|
||||
|
||||
int currentgeoid= Obj->getHighestCurveIndex();
|
||||
|
||||
openCommand("add perpendicular constraint");
|
||||
|
||||
try {
|
||||
|
||||
// Add a construction line
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
|
||||
selection[0].getFeatName(),
|
||||
PoE.x,PoE.y,endpoint.x,endpoint.y);
|
||||
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ",Obj->getNameInDocument(),currentgeoid+1);
|
||||
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Distance',%d,%f)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,direction.Length());
|
||||
|
||||
// Point on first object (ellipse, arc of ellipse)
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,Sketcher::start,GeoId1);
|
||||
// construction line tangent to ellipse/arcofellipse
|
||||
|
||||
try{
|
||||
//add missing point-on-object constraints
|
||||
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId1);
|
||||
};
|
||||
|
||||
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId2);
|
||||
};
|
||||
|
||||
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId1);
|
||||
};
|
||||
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,GeoId1);
|
||||
// Point on second object
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,Sketcher::start,GeoId2);
|
||||
|
||||
// line perpendicular to construction line
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,GeoId2);
|
||||
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,GeoId2,GeoId3,PosId3);
|
||||
} catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
QMessageBox::warning(Gui::getMainWindow(),
|
||||
QObject::tr("Error"),
|
||||
QString::fromLatin1(e.what()));
|
||||
Gui::Command::abortCommand();
|
||||
Gui::Command::updateActive();
|
||||
return;
|
||||
|
@ -1563,21 +1438,157 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg)
|
|||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
openCommand("add perpendicular constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,GeoId2);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
return;
|
||||
|
||||
};
|
||||
strError = QObject::tr("With 3 objects, there must be 2 curves and 1 point.", "tangent constraint");
|
||||
|
||||
} else if (SubNames.size() == 2) {
|
||||
|
||||
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { //endpoint-to-endpoint perpendicularity
|
||||
|
||||
if (isSimpleVertex(Obj, GeoId1, PosId1) ||
|
||||
isSimpleVertex(Obj, GeoId2, PosId2)) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Cannot add a perpendicularity constraint at an unconnected point!"));
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand("add perpendicular constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) ||
|
||||
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // endpoint-to-curve
|
||||
if (isVertex(GeoId2,PosId2)) {
|
||||
std::swap(GeoId1,GeoId2);
|
||||
std::swap(PosId1,PosId2);
|
||||
}
|
||||
|
||||
if (isSimpleVertex(Obj, GeoId1, PosId1)) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Cannot add a perpendicularity constraint at an unconnected point!"));
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand("add perpendicularity constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,PosId1,GeoId2);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
else if (isEdge(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) { // simple perpendicularity between GeoId1 and GeoId2
|
||||
|
||||
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
|
||||
if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
||||
geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("One of the selected edges should be a line."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
||||
std::swap(GeoId1,GeoId2);
|
||||
|
||||
// GeoId2 is the line
|
||||
geo1 = Obj->getGeometry(GeoId1);
|
||||
geo2 = Obj->getGeometry(GeoId2);
|
||||
|
||||
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
||||
geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ) {
|
||||
|
||||
Base::Vector3d center;
|
||||
double majord;
|
||||
double minord;
|
||||
double phi;
|
||||
|
||||
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ){
|
||||
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo1);
|
||||
|
||||
center=ellipse->getCenter();
|
||||
majord=ellipse->getMajorRadius();
|
||||
minord=ellipse->getMinorRadius();
|
||||
phi=ellipse->getAngleXU();
|
||||
} else
|
||||
if( geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ){
|
||||
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo1);
|
||||
|
||||
center=aoe->getCenter();
|
||||
majord=aoe->getMajorRadius();
|
||||
minord=aoe->getMinorRadius();
|
||||
phi=aoe->getAngleXU();
|
||||
}
|
||||
|
||||
const Part::GeomLineSegment *line = static_cast<const Part::GeomLineSegment *>(geo2);
|
||||
|
||||
Base::Vector3d point1=line->getStartPoint();
|
||||
Base::Vector3d point2=line->getEndPoint();
|
||||
|
||||
Base::Vector3d direction=point1-center;
|
||||
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomally by the polar
|
||||
|
||||
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
||||
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
||||
|
||||
openCommand("add perpendicular constraint");
|
||||
|
||||
try {
|
||||
// Add a point
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
||||
Obj->getNameInDocument(), PoE.x,PoE.y);
|
||||
int GeoIdPoint = Obj->getHighestCurveIndex();
|
||||
|
||||
// Point on first object (ellipse, arc of ellipse)
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoIdPoint,Sketcher::start,GeoId1);
|
||||
// Point on second object
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoIdPoint,Sketcher::start,GeoId2);
|
||||
|
||||
// add constraint: Perpendicular-via-point
|
||||
Gui::Command::doCommand(
|
||||
Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
|
||||
Obj->getNameInDocument(), GeoId1, GeoId2 ,GeoIdPoint, Sketcher::start);
|
||||
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
Gui::Command::updateActive();
|
||||
return;
|
||||
}
|
||||
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
openCommand("add perpendicular constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,GeoId2);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select exactly two entities from the sketch."));
|
||||
strError+strBasicHelp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1603,42 +1614,49 @@ CmdSketcherConstrainTangent::CmdSketcherConstrainTangent()
|
|||
eType = ForEdit;
|
||||
}
|
||||
|
||||
//helper function
|
||||
bool IsPointAlreadyOnCurve(int GeoIdCurve, int GeoIdPoint, Sketcher::PointPos PosIdPoint, Sketcher::SketchObject* Obj)
|
||||
{
|
||||
//TODO: make smarter
|
||||
Base::Vector3d p = Obj->getPoint(GeoIdPoint, PosIdPoint);
|
||||
return Obj->isPointOnCurve(GeoIdCurve, p.x, p.y);
|
||||
|
||||
}
|
||||
|
||||
void CmdSketcherConstrainTangent::activated(int iMsg)
|
||||
{
|
||||
QString strBasicHelp =
|
||||
QObject::tr(
|
||||
"There is a number of ways this constraint can be applied.\n\n"
|
||||
"Accepted combinations: two curves; an endpoint and a curve; two endpoints; two curves and a point.",
|
||||
/*disambig.:*/ "tangent constraint");
|
||||
|
||||
QString strError;
|
||||
// get the selection
|
||||
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
||||
|
||||
// only one sketch with its subelements are allowed to be selected
|
||||
if (selection.size() != 1) {
|
||||
strError = QObject::tr("Select some geometry from the sketch.", "tangent constraint");
|
||||
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select two entities from the sketch."));
|
||||
strError+strBasicHelp);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// get the needed lists and objects
|
||||
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
||||
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
||||
|
||||
if (SubNames.size() != 2 && SubNames.size() != 3)
|
||||
goto ExitWithMessage;
|
||||
if (SubNames.size() != 2 && SubNames.size() != 3){
|
||||
strError = QObject::tr("Wrong number of selected objects!","tangent constraint");
|
||||
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
strError+strBasicHelp);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
int GeoId1, GeoId2, GeoId3;
|
||||
Sketcher::PointPos PosId1, PosId2, PosId3;
|
||||
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
||||
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
||||
|
||||
if (checkBothExternal(GeoId1, GeoId2))
|
||||
if (checkBothExternal(GeoId1, GeoId2)) //checkBothExternal displays error message
|
||||
return;
|
||||
if (SubNames.size() == 3) {
|
||||
if (SubNames.size() == 3) { //tangent via point
|
||||
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
||||
//let's sink the point to be GeoId3. We want to keep the order the two curves have been selected in.
|
||||
if ( isVertex(GeoId1, PosId1) ){
|
||||
|
@ -1651,40 +1669,54 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
|
|||
};
|
||||
|
||||
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
||||
double ActAngle = 0.0;
|
||||
|
||||
openCommand("add angle constraint");
|
||||
openCommand("add tangent constraint");
|
||||
|
||||
try{
|
||||
//add missing point-on-object constraints
|
||||
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId1);
|
||||
};
|
||||
|
||||
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId2);
|
||||
};
|
||||
|
||||
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId1);
|
||||
};
|
||||
|
||||
//add missing point-on-object constraints
|
||||
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId1);
|
||||
};
|
||||
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId2);
|
||||
};
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,GeoId2,GeoId3,PosId3);
|
||||
} catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
QMessageBox::warning(Gui::getMainWindow(),
|
||||
QObject::tr("Error"),
|
||||
QString::fromLatin1(e.what()));
|
||||
Gui::Command::abortCommand();
|
||||
Gui::Command::updateActive();
|
||||
return;
|
||||
}
|
||||
|
||||
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId3,PosId3,GeoId1);
|
||||
};
|
||||
|
||||
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,GeoId2,GeoId3,PosId3);
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
|
||||
return;
|
||||
|
||||
};
|
||||
strError = QObject::tr("With 3 objects, there must be 2 curves and 1 point.", "tangent constraint");
|
||||
|
||||
} else if (SubNames.size() == 2) {
|
||||
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // tangency at common point
|
||||
|
||||
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // endpoint-to-endpoint tangency
|
||||
|
||||
if (isSimpleVertex(Obj, GeoId1, PosId1) ||
|
||||
isSimpleVertex(Obj, GeoId2, PosId2)) {
|
||||
|
@ -1693,97 +1725,6 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
|
|||
return;
|
||||
}
|
||||
|
||||
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
||||
|
||||
if( geom1 && geom2 &&
|
||||
( geom1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
||||
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() )){
|
||||
|
||||
if(geom1->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId())
|
||||
std::swap(GeoId1,GeoId2);
|
||||
|
||||
// GeoId1 is the arc of ellipse
|
||||
geom1 = Obj->getGeometry(GeoId1);
|
||||
geom2 = Obj->getGeometry(GeoId2);
|
||||
|
||||
if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
||||
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) {
|
||||
|
||||
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geom1);
|
||||
|
||||
Base::Vector3d center=aoe->getCenter();
|
||||
double majord=aoe->getMajorRadius();
|
||||
double minord=aoe->getMinorRadius();
|
||||
double phi=aoe->getAngleXU();
|
||||
|
||||
Base::Vector3d center2;
|
||||
|
||||
if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
||||
center2= (static_cast<const Part::GeomArcOfEllipse *>(geom2))->getCenter();
|
||||
else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
center2= (static_cast<const Part::GeomArcOfCircle *>(geom2))->getCenter();
|
||||
|
||||
Base::Vector3d direction=center2-center;
|
||||
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomally by the polar
|
||||
|
||||
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
||||
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
||||
|
||||
Base::Vector3d perp = Base::Vector3d(direction.y,-direction.x);
|
||||
|
||||
Base::Vector3d endpoint = PoE+perp;
|
||||
|
||||
int currentgeoid= Obj->getHighestCurveIndex();
|
||||
|
||||
openCommand("add tangent constraint");
|
||||
|
||||
try {
|
||||
// do points coincident
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2);
|
||||
|
||||
// Add a construction line
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
|
||||
selection[0].getFeatName(),
|
||||
PoE.x,PoE.y,endpoint.x,endpoint.y);
|
||||
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ",Obj->getNameInDocument(),currentgeoid+1);
|
||||
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Distance',%d,%f)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,direction.Length());
|
||||
|
||||
// Point on first object
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,Sketcher::start,GeoId1); // constrain major axis
|
||||
// Point on second object
|
||||
Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,Sketcher::start,GeoId2); // constrain major axis
|
||||
// tangent to first object
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,GeoId1);
|
||||
// tangent to second object
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
||||
selection[0].getFeatName(),currentgeoid+1,GeoId2);
|
||||
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
Gui::Command::updateActive();
|
||||
return;
|
||||
}
|
||||
|
||||
commitCommand();
|
||||
updateActive();
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
openCommand("add tangent constraint");
|
||||
Gui::Command::doCommand(
|
||||
Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d)) ",
|
||||
|
@ -1794,7 +1735,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
|
|||
return;
|
||||
}
|
||||
else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) ||
|
||||
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // tangency point
|
||||
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // endpoint-to-curve tangency
|
||||
if (isVertex(GeoId2,PosId2)) {
|
||||
std::swap(GeoId1,GeoId2);
|
||||
std::swap(PosId1,PosId2);
|
||||
|
@ -1836,8 +1777,8 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
|
|||
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
||||
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) {
|
||||
|
||||
Gui::Command::openCommand("add tangent constraint via construction element");
|
||||
makeTangentToEllipseviaConstructionLine(Obj,geom1,geom2,GeoId1,GeoId2);
|
||||
Gui::Command::openCommand("add tangent constraint point");
|
||||
makeTangentToEllipseviaNewPoint(Obj,geom1,geom2,GeoId1,GeoId2);
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
|
@ -1858,8 +1799,8 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
|
|||
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
||||
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) {
|
||||
|
||||
Gui::Command::openCommand("add tangent constraint via construction element");
|
||||
makeTangentToArcOfEllipseviaConstructionLine(Obj,geom1,geom2,GeoId1,GeoId2);
|
||||
Gui::Command::openCommand("add tangent constraint point");
|
||||
makeTangentToArcOfEllipseviaNewPoint(Obj,geom1,geom2,GeoId1,GeoId2);
|
||||
getSelection().clearSelection();
|
||||
return;
|
||||
}
|
||||
|
@ -1877,9 +1818,10 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
|
|||
}
|
||||
|
||||
}
|
||||
ExitWithMessage:
|
||||
|
||||
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select exactly two entities from the sketch."));
|
||||
strError+strBasicHelp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2082,13 +2024,14 @@ CmdSketcherConstrainAngle::CmdSketcherConstrainAngle()
|
|||
|
||||
void CmdSketcherConstrainAngle::activated(int iMsg)
|
||||
{
|
||||
//TODO: comprehensive messages, like in CmdSketcherConstrainTangent
|
||||
// get the selection
|
||||
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
||||
|
||||
// only one sketch with its subelements are allowed to be selected
|
||||
if (selection.size() != 1) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select vertexes from the sketch."));
|
||||
QObject::tr("Select only entities from the sketch."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2097,7 +2040,10 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
|
|||
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
||||
|
||||
if (SubNames.size() < 1 || SubNames.size() > 3) {
|
||||
goto MessageAndExit;
|
||||
//goto ExitWithMessage;
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select one or two lines from the sketch. Or select two edges and a point."));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -2253,7 +2199,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
|
|||
}
|
||||
};
|
||||
|
||||
MessageAndExit:
|
||||
//ExitWithMessage: //gotos cause compilation fails on linux due to jumping over initializations.
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select one or two lines from the sketch. Or select two edges and a point."));
|
||||
return;
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace SketcherGui {
|
|||
/// NOTE: A command must be opened before calling this function, which this function
|
||||
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
||||
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
||||
void makeTangentToEllipseviaConstructionLine(const Sketcher::SketchObject* Obj,
|
||||
void makeTangentToEllipseviaNewPoint(const Sketcher::SketchObject* Obj,
|
||||
const Part::Geometry *geom1,
|
||||
const Part::Geometry *geom2,
|
||||
int geoId1,
|
||||
|
@ -46,7 +46,7 @@ void makeTangentToEllipseviaConstructionLine(const Sketcher::SketchObject* Obj,
|
|||
/// NOTE: A command must be opened before calling this function, which this function
|
||||
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
||||
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
||||
void makeTangentToArcOfEllipseviaConstructionLine(const Sketcher::SketchObject* Obj,
|
||||
void makeTangentToArcOfEllipseviaNewPoint(const Sketcher::SketchObject* Obj,
|
||||
const Part::Geometry *geom1,
|
||||
const Part::Geometry *geom2,
|
||||
int geoId1,
|
||||
|
|
|
@ -463,7 +463,7 @@ void DrawSketchHandler::createAutoConstraints(const std::vector<AutoConstraint>
|
|||
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
||||
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) {
|
||||
// in all these cases an intermediate element is needed
|
||||
makeTangentToEllipseviaConstructionLine(Obj,geom1,geom2,geoId1,geoId2);
|
||||
makeTangentToEllipseviaNewPoint(Obj,geom1,geom2,geoId1,geoId2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ void DrawSketchHandler::createAutoConstraints(const std::vector<AutoConstraint>
|
|||
// in all these cases an intermediate element is needed
|
||||
// TODO: INSERT COMMON CODE HERE
|
||||
// in all these cases an intermediate element is needed
|
||||
makeTangentToArcOfEllipseviaConstructionLine(Obj,geom1,geom2,geoId1,geoId2);
|
||||
makeTangentToArcOfEllipseviaNewPoint(Obj,geom1,geom2,geoId1,geoId2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2415,7 +2415,7 @@ void ViewProviderSketch::drawConstraintIcons()
|
|||
break;
|
||||
case Perpendicular:
|
||||
// second icon is available only when there is no common point
|
||||
if ((*it)->FirstPos == Sketcher::none)
|
||||
if ((*it)->FirstPos == Sketcher::none && (*it)->Third == Constraint::GeoUndef)
|
||||
multipleIcons = true;
|
||||
break;
|
||||
case Equal:
|
||||
|
@ -3132,8 +3132,37 @@ Restart:
|
|||
|
||||
Base::Vector3d midpos1, dir1, norm1;
|
||||
Base::Vector3d midpos2, dir2, norm2;
|
||||
bool twoIcons = false;//a very local flag. It's set to true to indicate that the second dir+norm are valid and should be used
|
||||
|
||||
|
||||
if (Constr->Third != Constraint::GeoUndef || //perpty via point
|
||||
Constr->FirstPos != Sketcher::none) { //endpoint-to-curve or endpoint-to-endpoint perpty
|
||||
|
||||
int ptGeoId;
|
||||
Sketcher::PointPos ptPosId;
|
||||
do {//dummy loop to use break =) Maybe goto?
|
||||
ptGeoId = Constr->First;
|
||||
ptPosId = Constr->FirstPos;
|
||||
if (ptPosId != Sketcher::none) break;
|
||||
ptGeoId = Constr->Second;
|
||||
ptPosId = Constr->SecondPos;
|
||||
if (ptPosId != Sketcher::none) break;
|
||||
ptGeoId = Constr->Third;
|
||||
ptPosId = Constr->ThirdPos;
|
||||
if (ptPosId != Sketcher::none) break;
|
||||
assert(0);//no point found!
|
||||
} while (false);
|
||||
if (temp)
|
||||
midpos1 = edit->ActSketch.getPoint(ptGeoId, ptPosId);
|
||||
else
|
||||
midpos1 = getSketchObject()->getPoint(ptGeoId, ptPosId);
|
||||
|
||||
norm1 = edit->ActSketch.calculateNormalAtPoint(Constr->Second, midpos1.x, midpos1.y);
|
||||
norm1.Normalize();
|
||||
dir1 = norm1; dir1.RotateZ(-M_PI/2.0);
|
||||
|
||||
} else if (Constr->FirstPos == Sketcher::none) {
|
||||
|
||||
if (Constr->FirstPos == Sketcher::none) {
|
||||
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg1 = dynamic_cast<const Part::GeomLineSegment *>(geo1);
|
||||
midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2);
|
||||
|
@ -3147,7 +3176,7 @@ Restart:
|
|||
norm1 = Base::Vector3d(cos(midangle),sin(midangle),0);
|
||||
dir1 = Base::Vector3d(-norm1.y,norm1.x,0);
|
||||
midpos1 = arc->getCenter() + arc->getRadius() * norm1;
|
||||
} else if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
} else if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
const Part::GeomCircle *circle = dynamic_cast<const Part::GeomCircle *>(geo1);
|
||||
norm1 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0);
|
||||
dir1 = Base::Vector3d(-norm1.y,norm1.x,0);
|
||||
|
@ -3168,28 +3197,22 @@ Restart:
|
|||
norm2 = Base::Vector3d(cos(midangle),sin(midangle),0);
|
||||
dir2 = Base::Vector3d(-norm2.y,norm2.x,0);
|
||||
midpos2 = arc->getCenter() + arc->getRadius() * norm2;
|
||||
} else if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
} else if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
const Part::GeomCircle *circle = dynamic_cast<const Part::GeomCircle *>(geo2);
|
||||
norm2 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0);
|
||||
dir2 = Base::Vector3d(-norm2.y,norm2.x,0);
|
||||
midpos2 = circle->getCenter() + circle->getRadius() * norm2;
|
||||
} else
|
||||
break;
|
||||
twoIcons = true;
|
||||
|
||||
} else {
|
||||
if (temp)
|
||||
midpos1 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos);
|
||||
else
|
||||
midpos1 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos);
|
||||
norm1 = Base::Vector3d(0,1,0);
|
||||
dir1 = Base::Vector3d(1,0,0);
|
||||
}
|
||||
|
||||
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 2.5, edit->constrGroup->getChild(i));
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr);
|
||||
dynamic_cast<SoZoomTranslation *>(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relpos1.x, relpos1.y, 0);
|
||||
|
||||
if (Constr->FirstPos == Sketcher::none) {
|
||||
if (twoIcons) {
|
||||
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 2.5, edit->constrGroup->getChild(i));
|
||||
|
||||
Base::Vector3d secondPos = midpos2 - midpos1;
|
||||
|
|
Loading…
Reference in New Issue
Block a user