diff --git a/src/Mod/Sketcher/App/ConstraintPyImp.cpp b/src/Mod/Sketcher/App/ConstraintPyImp.cpp index b04d6ab7c..7dad2b759 100644 --- a/src/Mod/Sketcher/App/ConstraintPyImp.cpp +++ b/src/Mod/Sketcher/App/ConstraintPyImp.cpp @@ -138,15 +138,20 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) if (PyInt_Check(index_or_value)) { FirstPos = any_index; SecondIndex = PyInt_AsLong(index_or_value); - if (strcmp("Tangent", ConstraintType) == 0) { - this->getConstraintPtr()->Type = Tangent; - this->getConstraintPtr()->First = FirstIndex; - this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; - this->getConstraintPtr()->Second = SecondIndex; - return 0; + bool valid = false; + if (strcmp("Perpendicular", ConstraintType) == 0) { + this->getConstraintPtr()->Type = Perpendicular; + valid = true; } - if (strcmp("PointOnObject", ConstraintType) == 0) { + else if (strcmp("Tangent", ConstraintType) == 0) { + this->getConstraintPtr()->Type = Tangent; + valid = true; + } + else if (strcmp("PointOnObject", ConstraintType) == 0) { this->getConstraintPtr()->Type = PointOnObject; + valid = true; + } + if (valid) { this->getConstraintPtr()->First = FirstIndex; this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; this->getConstraintPtr()->Second = SecondIndex; @@ -212,6 +217,10 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->Type = Vertical; valid = true; } + else if (strcmp("Perpendicular", ConstraintType) == 0) { + this->getConstraintPtr()->Type = Perpendicular; + valid = true; + } else if (strcmp("Tangent", ConstraintType) == 0) { this->getConstraintPtr()->Type = Tangent; valid = true; diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 974ea38bc..97e26f1d8 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -474,7 +474,16 @@ int Sketch::addConstraint(const Constraint *constraint) rtn = addParallelConstraint(constraint->First,constraint->Second); break; case Perpendicular: - rtn = addPerpendicularConstraint(constraint->First,constraint->Second); + 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); + } break; case Tangent: if (constraint->SecondPos != none) // tangency at common point @@ -760,8 +769,13 @@ int Sketch::addParallelConstraint(int geoId1, int geoId2) return ConstraintsCounter; } +// simple perpendicularity constraint int Sketch::addPerpendicularConstraint(int geoId1, int geoId2) { + // accepts the following combinations: + // 1) Line1, Line2/Circle2/Arc2 + // 2) Circle1, Line2 (converted to case #1) + // 3) Arc1, Line2 (converted to case #1) geoId1 = checkGeoId(geoId1); geoId2 = checkGeoId(geoId2); @@ -778,17 +792,12 @@ int Sketch::addPerpendicularConstraint(int geoId1, int geoId2) } if (Geoms[geoId1].type == Line) { - GCS::Line &l = Lines[Geoms[geoId1].index]; - if (Geoms[geoId2].type == Arc) { - GCS::Arc &a = Arcs[Geoms[geoId2].index]; - //GCSsys.addConstraintPerpendicular(l, a); - Base::Console().Warning("Perpendicular constraints between lines and arcs are not implemented yet.\n"); - return -1; - } else if (Geoms[geoId2].type == Circle) { - GCS::Circle &c = Circles[Geoms[geoId2].index]; - //GCSsys.addConstraintPerpendicular(l, c); - Base::Console().Warning("Perpendicular constraints between lines and circles are not implemented yet.\n"); - return -1; + GCS::Line &l1 = Lines[Geoms[geoId1].index]; + if (Geoms[geoId2].type == Arc || Geoms[geoId2].type == Circle) { + GCS::Point &p2 = Points[Geoms[geoId2].midPointId]; + int tag = ++ConstraintsCounter; + GCSsys.addConstraintPointOnLine(p2, l1, tag); + return ConstraintsCounter; } } @@ -797,15 +806,182 @@ 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[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) { + 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 { + GCS::Circle &c2 = Circles[Geoms[geoId2].index]; + radius = c2.rad; + } + 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) { // accepts the following combinations: // 1) Line1, Line2/Circle2/Arc2 // 2) Circle1, Line2 (converted to case #1) - // Circle1, Circle2/Arc2 (not implemented yet) + // Circle1, Circle2/Arc2 // 3) Arc1, Line2 (converted to case #1) - // Arc1, Circle2/Arc2 (not implemented yet) + // Arc1, Circle2/Arc2 geoId1 = checkGeoId(geoId1); geoId2 = checkGeoId(geoId2); @@ -871,12 +1047,8 @@ int Sketch::addTangentConstraint(int geoId1, int geoId2) int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2) { // accepts the following combinations: - // 1) Line1, start/end/mid, Line2 - // 2) Line1, start/end/mid, Circle2 - // 3) Line1, start/end/mid, Arc2 - // 4) Arc1, start/end, Line2 - // 5) Arc1, start/end, Circle2 - // 6) Arc1, start/end, Arc2 + // 1) Line1, start/end, Line2/Circle2/Arc2 + // 2) Arc1, start/end, Line2/Circle2/Arc2 geoId1 = checkGeoId(geoId1); geoId2 = checkGeoId(geoId2); @@ -947,10 +1119,9 @@ int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2) int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2) { // accepts the following combinations: - // 1) Line1, start/end/mid, Line2, start/end/mid - // 2) Line1, start/end/mid, Arc2, start/end - // 3) Arc1, start/end, Line2, start/end/mid (converted to case #2) - // 4) Arc1, start/end, Arc2, start/end + // 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); @@ -983,14 +1154,7 @@ int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos 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.addConstraintP2PCoincident(p1, p2, tag); - GCSsys.addConstraintParallel(l1, l2, tag); - return ConstraintsCounter; - } - else if (Geoms[geoId2].type == Arc) { + if (Geoms[geoId2].type == Arc) { GCS::Arc &a2 = Arcs[Geoms[geoId2].index]; if (pos2 == start) { if (pos1 == start) { @@ -1003,12 +1167,6 @@ int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos GCSsys.addConstraintTangentLine2Arc(l1.p1, l1.p2, a2, tag); return ConstraintsCounter; } - else if (pos1 == mid) { // FIXME: coincidence with midpoint of line?? - int tag = ++ConstraintsCounter; - GCSsys.addConstraintP2PCoincident(p1, p2, tag); - GCSsys.addConstraintTangent(l1, a2, tag); - return ConstraintsCounter; - } } else if (pos2 == end) { if (pos1 == start) { @@ -1021,12 +1179,6 @@ int Sketch::addTangentConstraint(int geoId1, PointPos pos1, int geoId2, PointPos GCSsys.addConstraintTangentArc2Line(a2, l1.p2, l1.p1, tag); return ConstraintsCounter; } - else if (pos1 == mid) { // FIXME: coincidence with midpoint of line?? - int tag = ++ConstraintsCounter; - GCSsys.addConstraintP2PCoincident(p1, p2, tag); - GCSsys.addConstraintTangent(l1, a2, tag); - return ConstraintsCounter; - } } else return -1; diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index 8746ff226..4e45c6662 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -152,6 +152,8 @@ public: int addParallelConstraint(int geoIndex1, int geoIndex2); /// add a perpendicular constraint between two lines int addPerpendicularConstraint(int geoIndex1, int geoIndex2); + int addPerpendicularConstraint(int geoIndex1, PointPos pos1, int geoIndex2); + int addPerpendicularConstraint(int geoIndex1, PointPos pos1, int geoIndex2, PointPos pos2); /// add a tangency constraint between two geometries int addTangentConstraint(int geoIndex1, int geoIndex2); int addTangentConstraint(int geoIndex1, PointPos pos1, int geoIndex2); diff --git a/src/Mod/Sketcher/App/freegcs/GCS.cpp b/src/Mod/Sketcher/App/freegcs/GCS.cpp index 29705fde2..b0ee985fb 100644 --- a/src/Mod/Sketcher/App/freegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/freegcs/GCS.cpp @@ -350,6 +350,67 @@ int System::addConstraintPointOnArc(Point &p, Arc &a, int tagId) return addConstraintP2PDistance(p, a.center, a.rad, tagId); } +int System::addConstraintPerpendicularLine2Arc(Point &p1, Point &p2, Arc &a, + int tagId) +{ + addConstraintP2PCoincident(p2, a.start, tagId); + double dx = *(p2.x) - *(p1.x); + double dy = *(p2.y) - *(p1.y); + if (dx * cos(*(a.startAngle)) + dy * sin(*(a.startAngle)) > 0) + return addConstraintP2PAngle(p1, p2, a.startAngle, 0, tagId); + else + return addConstraintP2PAngle(p1, p2, a.startAngle, M_PI, tagId); +} + +int System::addConstraintPerpendicularArc2Line(Arc &a, Point &p1, Point &p2, + int tagId) +{ + addConstraintP2PCoincident(p1, a.end, tagId); + double dx = *(p2.x) - *(p1.x); + double dy = *(p2.y) - *(p1.y); + if (dx * cos(*(a.endAngle)) + dy * sin(*(a.endAngle)) > 0) + return addConstraintP2PAngle(p1, p2, a.endAngle, 0, tagId); + else + return addConstraintP2PAngle(p1, p2, a.endAngle, M_PI, tagId); +} + +int System::addConstraintPerpendicularCircle2Arc(Point ¢er, double *radius, + Arc &a, int tagId) +{ + addConstraintP2PDistance(a.start, center, radius, tagId); + double incr_angle = *(a.startAngle) < *(a.endAngle) ? M_PI/2 : -M_PI/2; + double tang_angle = *a.startAngle + incr_angle; + double dx = *(a.start.x) - *(center.x); + double dy = *(a.start.y) - *(center.y); + if (dx * cos(tang_angle) + dy * sin(tang_angle) > 0) + return addConstraintP2PAngle(center, a.start, a.startAngle, incr_angle, tagId); + else + return addConstraintP2PAngle(center, a.start, a.startAngle, -incr_angle, tagId); +} + +int System::addConstraintPerpendicularArc2Circle(Arc &a, Point ¢er, + double *radius, int tagId) +{ + addConstraintP2PDistance(a.end, center, radius, tagId); + double incr_angle = *(a.startAngle) < *(a.endAngle) ? -M_PI/2 : M_PI/2; + double tang_angle = *a.endAngle + incr_angle; + double dx = *(a.end.x) - *(center.x); + double dy = *(a.end.y) - *(center.y); + if (dx * cos(tang_angle) + dy * sin(tang_angle) > 0) + return addConstraintP2PAngle(center, a.end, a.endAngle, incr_angle, tagId); + else + return addConstraintP2PAngle(center, a.end, a.endAngle, -incr_angle, tagId); +} + +int System::addConstraintPerpendicularArc2Arc(Arc &a1, bool reverse1, + Arc &a2, bool reverse2, int tagId) +{ + Point &p1 = reverse1 ? a1.start : a1.end; + Point &p2 = reverse2 ? a2.end : a2.start; + addConstraintP2PCoincident(p1, p2, tagId); + return addConstraintPerpendicular(a1.center, p1, a2.center, p2, tagId); +} + int System::addConstraintTangent(Line &l, Circle &c, int tagId) { return addConstraintP2LDistance(c.center, l, c.rad, tagId); diff --git a/src/Mod/Sketcher/App/freegcs/GCS.h b/src/Mod/Sketcher/App/freegcs/GCS.h index 9df5a0123..844e9365e 100644 --- a/src/Mod/Sketcher/App/freegcs/GCS.h +++ b/src/Mod/Sketcher/App/freegcs/GCS.h @@ -115,6 +115,16 @@ namespace GCS int addConstraintArcRules(Arc &a, int tagId=0); int addConstraintPointOnCircle(Point &p, Circle &c, int tagId=0); int addConstraintPointOnArc(Point &p, Arc &a, int tagId=0); + int addConstraintPerpendicularLine2Arc(Point &p1, Point &p2, Arc &a, + int tagId=0); + int addConstraintPerpendicularArc2Line(Arc &a, Point &p1, Point &p2, + int tagId=0); + int addConstraintPerpendicularCircle2Arc(Point ¢er, double *radius, Arc &a, + int tagId=0); + int addConstraintPerpendicularArc2Circle(Arc &a, Point ¢er, double *radius, + int tagId=0); + int addConstraintPerpendicularArc2Arc(Arc &a1, bool reverse1, + Arc &a2, bool reverse2, int tagId=0); int addConstraintTangent(Line &l, Circle &c, int tagId=0); int addConstraintTangent(Line &l, Arc &a, int tagId=0); int addConstraintTangent(Circle &c1, Circle &c2, int tagId=0); diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 8ef99041f..fd795eb93 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1198,7 +1198,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) // 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 two lines from the sketch.")); + QObject::tr("Select two entities from the sketch.")); return; } @@ -1208,7 +1208,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) if (SubNames.size() != 2) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select exactly two lines from the sketch.")); + QObject::tr("Select exactly two entities from the sketch.")); return; } @@ -1216,37 +1216,102 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) getIdsFromName(SubNames[0], GeoId1, VtId1); getIdsFromName(SubNames[1], GeoId2, VtId2); - if (checkBothExternal(GeoId1, GeoId2)) - return; - else if (GeoId1 == Constraint::GeoUndef || GeoId2 == Constraint::GeoUndef) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select exactly two lines from the sketch.")); + Sketcher::PointPos PosId1,PosId2; + if (VtId1 >= 0 && VtId2 >= 0) { // perpendicularity at common point + Obj->getGeoVertexIndex(VtId1,GeoId1,PosId1); + Obj->getGeoVertexIndex(VtId2,GeoId2,PosId2); + + if (checkBothExternal(GeoId1, GeoId2)) + 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 ((VtId1 >= 0 && GeoId2 != Constraint::GeoUndef) || + (VtId2 >= 0 && GeoId1 != Constraint::GeoUndef)) { // VtId1 is a connecting point + if (VtId2 >= 0 && GeoId1 != Constraint::GeoUndef) { + VtId1 = VtId2; + VtId2 = -1; + GeoId2 = GeoId1; + GeoId1 = -1; + } + Obj->getGeoVertexIndex(VtId1,GeoId1,PosId1); - const Part::Geometry *geo1 = Obj->getGeometry(GeoId1); - const Part::Geometry *geo2 = Obj->getGeometry(GeoId2); + if (checkBothExternal(GeoId1, GeoId2)) + return; - if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() || - geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) { + 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; + } - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select exactly two lines from the sketch.")); + 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 (GeoId1 != Constraint::GeoUndef && GeoId2 != Constraint::GeoUndef) { // simple perpendicularity between GeoId1 and GeoId2 - // undo command open - openCommand("add perpendicular constraint"); - Gui::Command::doCommand( - Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ", - selection[0].getFeatName(),GeoId1,GeoId2); + if (checkBothExternal(GeoId1, GeoId2)) + return; - // finish the transaction and update - commitCommand(); - updateActive(); + 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; + } - // clear the selection (convenience) - getSelection().clearSelection(); + 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; + } + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Select exactly two entities from the sketch.")); + return; } bool CmdSketcherConstrainPerpendicular::isActive(void) @@ -1674,7 +1739,7 @@ void CmdSketcherConstrainEqual::activated(int iMsg) circSel = true; else { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select two or more edges of similar type")); + QObject::tr("Select two or more edges of similar type")); return; } @@ -1683,7 +1748,7 @@ void CmdSketcherConstrainEqual::activated(int iMsg) if (lineSel && (arcSel || circSel)) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select two or more edges of similar type")); + QObject::tr("Select two or more edges of similar type")); return; } @@ -1766,7 +1831,7 @@ void CmdSketcherConstrainSymmetric::activated(int iMsg) if ((GeoId1 < 0 && GeoId2 < 0) || (GeoId1 < 0 && GeoId3 < 0) || (GeoId2 < 0 && GeoId3 < 0)) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Cannot add a constraint between external geometries!")); + QObject::tr("Cannot add a constraint between external geometries!")); return; } diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 7dc3273ab..ee289f1d7 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -1497,7 +1497,9 @@ void ViewProviderSketch::drawConstraintIcons() break; case Perpendicular: icoType = QString::fromAscii("small/Constraint_Perpendicular_sm"); - index2 = 4; + // second icon is available only when there is no common point + if ((*it)->FirstPos == Sketcher::none) + index2 = 4; break; case Equal: icoType = QString::fromAscii("small/Constraint_EqualLength_sm"); @@ -1824,8 +1826,93 @@ Restart: } break; - case Parallel: case Perpendicular: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); + // get the geometry + const Part::Geometry *geo1 = GeoById(*geomlist, Constr->First); + const Part::Geometry *geo2 = GeoById(*geomlist, Constr->Second); + + Base::Vector3d midpos1, dir1, norm1; + Base::Vector3d midpos2, dir2, norm2; + + if (Constr->FirstPos == Sketcher::none) { + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg1 = dynamic_cast(geo1); + midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); + dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); + norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); + } else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = dynamic_cast(geo1); + double startangle, endangle, midangle; + arc->getRange(startangle, endangle); + midangle = (startangle + endangle)/2; + 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()) { + const Part::GeomCircle *circle = dynamic_cast(geo1); + norm1 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0); + dir1 = Base::Vector3d(-norm1.y,norm1.x,0); + midpos1 = circle->getCenter() + circle->getRadius() * norm1; + } else + break; + + if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg2 = dynamic_cast(geo2); + midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); + dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); + norm2 = Base::Vector3d(-dir2.y,dir2.x,0.); + } else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = dynamic_cast(geo2); + double startangle, endangle, midangle; + arc->getRange(startangle, endangle); + midangle = (startangle + endangle)/2; + 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()) { + const Part::GeomCircle *circle = dynamic_cast(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; + + } 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); + } + + // Get Current Scale Factor + float scale = dynamic_cast(sep->getChild(1))->getScaleFactor(); + + Base::Vector3d constrPos1 = midpos1 + (norm1 * 2.5 * scale); + constrPos1 = seekConstraintPosition(constrPos1, dir1, scale * 2.5, edit->constrGroup->getChild(i)); + + // Translate the Icon based on calculated position + Base::Vector3d relPos1 = (constrPos1 - midpos1) / scale ; // Relative Position of Icons to Midpoint1 + dynamic_cast(sep->getChild(1))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); + dynamic_cast(sep->getChild(1))->translation = SbVec3f(relPos1.x, relPos1.y, 0); + + if (Constr->FirstPos == Sketcher::none) { + Base::Vector3d constrPos2 = midpos2 + (norm2 * 2.5 * scale); + constrPos2 = seekConstraintPosition(constrPos2, dir2, 2.5 * scale, edit->constrGroup->getChild(i)); + + Base::Vector3d relPos2 = (constrPos2 - midpos2) / scale ; // Relative Position of Icons to Midpoint2 + Base::Vector3d secondPos = midpos2 - midpos1; + dynamic_cast(sep->getChild(3))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); + dynamic_cast(sep->getChild(3))->translation = SbVec3f(relPos2.x -relPos1.x, relPos2.y -relPos1.y, 0); + } + + } + break; + case Parallel: case Equal: { assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); @@ -1877,7 +1964,7 @@ Restart: norm2 = Base::Vector3d(cos(angle2),sin(angle2),0); dir2 = Base::Vector3d(-norm2.y,norm2.x,0); midpos2 += r2*norm2; - } else // Parallel or Perpendicular can only apply to a GeomLineSegment + } else // Parallel can only apply to a GeomLineSegment break; } else { const Part::GeomLineSegment *lineSeg1 = dynamic_cast(geo1);