From d3b0cf771fd5e38581202f6107d6099af5185a86 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Sat, 1 Nov 2014 15:12:18 +0100 Subject: [PATCH] Sketcher: Ellipse implementation enhancements - ArcOfEllipse enhancement: Tangency ArcOfEllipse to ArcOfEllipse or ArcOfCircle by selecting end/starting points... - Minor bug corrections (Thanks DeepSOIC) - ExposeInternalGeometry python command - DeleteUnusedInternalGeometry python command - On deletion of an Ellipse/ArcOfEllipse all further unconstrained internal geometry is also deleted. - This cleans up the code by eliminating code repetition in the creation methods. - Major bug fix for autoconstraints for ellipse and arc of ellipse creation (for both creation methods) - Major bug fix Start and Endpoint constraints of an arc of ellipse where not taking into account that Sketcher arcs are always CCW, so they have to be exchanged if we convert a CW arc into a CCW arc. Sketcher: General bug fix: Tangency wrongly suggested What? ===== - On creation of a shape autoconstraints are suggested. - Tangent autoconstraint was suggested even with lines perpendicular to the tangency direction Reproduce ========= - Make a circle on the origin and move the mouse along the X axis, it will suggest a tangency that is impossible - Click on the axis and no circle will be created Solution ======== - The SeekConstraint now can use the parameter dir to give a direction that is substantially perpendicular to the expected tangency, so that if an object having a direction (a line) is hit, a tangency will not be suggested if within around 6 degrees of being parallel. - Additionally, if such a line is an X,Y axis of the sketch, tangency will only be suggested if the direction is within 6 degrees of being perpendicular (i.e. it is almost tangent already while sketching). - This difference is due to the fact that an X or Y axis can not "move" to meet the object under creation, whereas a line can. --- src/Mod/Sketcher/App/Sketch.cpp | 2 +- src/Mod/Sketcher/App/SketchObject.cpp | 272 +++++++++++++- src/Mod/Sketcher/App/SketchObject.h | 8 +- src/Mod/Sketcher/App/SketchObjectPy.xml | 10 + src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 34 ++ src/Mod/Sketcher/Gui/CMakeLists.txt | 1 + src/Mod/Sketcher/Gui/CommandConstraints.cpp | 394 +++++++++++++------- src/Mod/Sketcher/Gui/CommandConstraints.h | 58 +++ src/Mod/Sketcher/Gui/CommandCreateGeo.cpp | 206 +++++----- src/Mod/Sketcher/Gui/DrawSketchHandler.cpp | 143 ++++++- 10 files changed, 846 insertions(+), 282 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/CommandConstraints.h diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 23752b56d5..ee3306a217 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -2307,7 +2307,7 @@ int Sketch::initMove(int geoId, PointPos pos, bool fine) GCS::Point ¢er = Points[Geoms[geoId].midPointId]; GCS::Point p0,p1; - if (pos == mid | pos == none) { + if (pos == mid || pos == none) { MoveParameters.resize(2); // cx,cy p0.x = &MoveParameters[0]; p0.y = &MoveParameters[1]; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index f992bb598e..4c8f75d3a7 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -355,6 +355,8 @@ int SketchObject::delGeometry(int GeoId) if (GeoId < 0 || GeoId >= int(vals.size())) return -1; + this->DeleteUnusedInternalGeometry(GeoId); + std::vector< Part::Geometry * > newVals(vals); newVals.erase(newVals.begin()+GeoId); @@ -1358,17 +1360,7 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) delConstraintOnPoint(GeoId, end, false); Part::GeomArcOfEllipse *aoe1 = dynamic_cast(geomlist[GeoId]); aoe1->setRange(startAngle, startAngle + theta1); - Sketcher::Constraint *newConstr = new Sketcher::Constraint(); - newConstr->Type = constrType; - newConstr->First = GeoId; - newConstr->FirstPos = end; - newConstr->Second = GeoId1; - - if (constrType == Sketcher::Coincident) - newConstr->SecondPos = secondPos; - - addConstraint(newConstr); - delete newConstr; + return 0; } } @@ -1378,6 +1370,264 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) return -1; } +int SketchObject::ExposeInternalGeometry(int GeoId) +{ + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + const Part::Geometry *geo = getGeometry(GeoId); + // Only for supported types + if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + // First we search what has to be restored + bool major=false; + bool minor=false; + bool focus1=false; + bool focus2=false; + + int majorelementindex=-1; + int minorelementindex=-1; + int focus1elementindex=-1; + int focus2elementindex=-1; + + const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues(); + + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); + it != vals.end(); ++it) { + if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId) + { + switch((*it)->AlignmentType){ + case Sketcher::EllipseMajorDiameter: + major=true; + majorelementindex=(*it)->First; + break; + case Sketcher::EllipseMinorDiameter: + minor=true; + minorelementindex=(*it)->First; + break; + case Sketcher::EllipseFocus1: + focus1=true; + focus1elementindex=(*it)->First; + break; + case Sketcher::EllipseFocus2: + focus2=true; + focus2elementindex=(*it)->First; + break; + } + } + } + + int currentgeoid= getHighestCurveIndex(); + int incrgeo= 0; + + Base::Vector3d center; + double majord; + double minord; + double phi; + + if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + const Part::GeomEllipse *ellipse = static_cast(geo); + + center=ellipse->getCenter(); + majord=ellipse->getMajorRadius(); + minord=ellipse->getMinorRadius(); + phi=ellipse->getAngleXU(); + } + else { + const Part::GeomArcOfEllipse *aoe = static_cast(geo); + + center=aoe->getCenter(); + majord=aoe->getMajorRadius(); + minord=aoe->getMinorRadius(); + phi=aoe->getAngleXU(); + } + + Base::Vector3d majorpositiveend = center + majord * Base::Vector3d(cos(phi),sin(phi),0); + Base::Vector3d majornegativeend = center - majord * Base::Vector3d(cos(phi),sin(phi),0); + Base::Vector3d minorpositiveend = center + minord * Base::Vector3d(-sin(phi),cos(phi),0); + Base::Vector3d minornegativeend = center - minord * Base::Vector3d(-sin(phi),cos(phi),0); + + double df= sqrt(majord*majord-minord*minord); + + Base::Vector3d focus1P = center + df * Base::Vector3d(cos(phi),sin(phi),0); + Base::Vector3d focus2P = center - df * Base::Vector3d(cos(phi),sin(phi),0); + + if(!major) + { + Part::GeomLineSegment *lmajor = new Part::GeomLineSegment(); + lmajor->setPoints(majorpositiveend,majornegativeend); + + this->addGeometry(lmajor); // create line for major axis + this->setConstruction(currentgeoid+incrgeo+1,true); + delete lmajor; + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseMajorDiameter; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->Second = GeoId; + + addConstraint(newConstr); + delete newConstr; + incrgeo++; + } + if(!minor) + { + Part::GeomLineSegment *lminor = new Part::GeomLineSegment(); + lminor->setPoints(minorpositiveend,minornegativeend); + + this->addGeometry(lminor); // create line for major axis + this->setConstruction(currentgeoid+incrgeo+1,true); + delete lminor; + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseMinorDiameter; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->Second = GeoId; + + addConstraint(newConstr); + delete newConstr; + incrgeo++; + } + if(!focus1) + { + Part::GeomPoint *pf1 = new Part::GeomPoint(); + pf1->setPoint(focus1P); + this->addGeometry(pf1); + delete pf1; + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseFocus1; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->FirstPos = Sketcher::start; + newConstr->Second = GeoId; + + addConstraint(newConstr); + delete newConstr; + incrgeo++; + } + if(!focus2) + { + Part::GeomPoint *pf2 = new Part::GeomPoint(); + pf2->setPoint(focus2P); + this->addGeometry(pf2); + delete pf2; + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseFocus2; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->FirstPos = Sketcher::start; + newConstr->Second = GeoId; + + addConstraint(newConstr); + delete newConstr; + } + + return incrgeo; //number of added elements + } + else + return -1; // not supported type +} + +int SketchObject::DeleteUnusedInternalGeometry(int GeoId) +{ + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + const Part::Geometry *geo = getGeometry(GeoId); + // Only for supported types + if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + // First we search what has to be deleted + bool major=false; + bool minor=false; + bool focus1=false; + bool focus2=false; + + int majorelementindex=-1; + int minorelementindex=-1; + int focus1elementindex=-1; + int focus2elementindex=-1; + + const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues(); + + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); + it != vals.end(); ++it) { + if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId) + { + switch((*it)->AlignmentType){ + case Sketcher::EllipseMajorDiameter: + major=true; + majorelementindex=(*it)->First; + break; + case Sketcher::EllipseMinorDiameter: + minor=true; + minorelementindex=(*it)->First; + break; + case Sketcher::EllipseFocus1: + focus1=true; + focus1elementindex=(*it)->First; + break; + case Sketcher::EllipseFocus2: + focus2=true; + focus2elementindex=(*it)->First; + break; + } + } + } + + // Hide unused geometry here + int majorconstraints=0; // number of constraints associated to the geoid of the major axis + int minorconstraints=0; + int focus1constraints=0; + int focus2constraints=0; + + int decrgeo=0; + + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); + it != vals.end(); ++it) { + + if((*it)->Second == majorelementindex || (*it)->First == majorelementindex || (*it)->Third == majorelementindex) + majorconstraints++; + else if((*it)->Second == minorelementindex || (*it)->First == minorelementindex || (*it)->Third == minorelementindex) + minorconstraints++; + else if((*it)->Second == focus1elementindex || (*it)->First == focus1elementindex || (*it)->Third == focus1elementindex) + focus1constraints++; + else if((*it)->Second == focus2elementindex || (*it)->First == focus2elementindex || (*it)->Third == focus2elementindex) + focus2constraints++; + } + + std::vector delgeometries; + + // those with less than 2 constraints must be removed + if(focus2constraints<2) + delgeometries.push_back(focus2elementindex); + + if(focus1constraints<2) + delgeometries.push_back(focus1elementindex); + + if(minorconstraints<2) + delgeometries.push_back(minorelementindex); + + if(majorconstraints<2) + delgeometries.push_back(majorelementindex); + + std::sort(delgeometries.begin(), delgeometries.end()); // indices over an erased element get automatically updated!! + + if(delgeometries.size()>0) + { + for (std::vector::reverse_iterator it=delgeometries.rbegin(); it!=delgeometries.rend(); ++it) { + delGeometry(*it); + } + } + + return delgeometries.size(); //number of deleted elements + } + else + return -1; // not supported type +} + int SketchObject::addExternal(App::DocumentObject *Obj, const char* SubName) { // so far only externals to the support of the sketch diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index c04859fb55..560bab9bfb 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2008 * + * Copyright (c) J�rgen Riegel (juergen.riegel@web.de) 2008 * * * * This file is part of the FreeCAD CAx development system. * * * @@ -47,6 +47,7 @@ public: Part ::PropertyGeometryList Geometry; Sketcher::PropertyConstraintList Constraints; App ::PropertyLinkSubList ExternalGeometry; + const char* ss; /** @name methods overide Feature */ //@{ /// recalculate the Feature @@ -127,6 +128,10 @@ public: /// trim a curve int trim(int geoId, const Base::Vector3d& point); + /// Exposes all internal geometry of an object supporting internal geometry + int ExposeInternalGeometry(int GeoId); + /// Deletes all unused (not further constrained) internal geometry + int DeleteUnusedInternalGeometry(int GeoId); /// retrieves for a Vertex number the corresponding GeoId and PosId void getGeoVertexIndex(int VertexId, int &GeoId, PointPos &PosId) const; @@ -158,6 +163,7 @@ public: virtual int getAxisCount(void) const; /// retrieves an axis iterating through the construction lines of the sketch (indices start at 0) virtual Base::Axis getAxis(int axId) const; + const char*& it(int arg1, int arg2); protected: /// get called by the container when a property has changed diff --git a/src/Mod/Sketcher/App/SketchObjectPy.xml b/src/Mod/Sketcher/App/SketchObjectPy.xml index 3d94b91c96..e8f1c489e0 100644 --- a/src/Mod/Sketcher/App/SketchObjectPy.xml +++ b/src/Mod/Sketcher/App/SketchObjectPy.xml @@ -112,6 +112,16 @@ trim a curve with a given id at a given reference point + + + Exposes all internal geometry of an object supporting internal geometry + + + + + Deletes all unused (not further constrained) internal geometry + + Number of Constraints in this sketch diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 44126c8b2a..fabea7da2e 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -596,6 +596,40 @@ PyObject* SketchObjectPy::trim(PyObject *args) } +PyObject* SketchObjectPy::ExposeInternalGeometry(PyObject *args) +{ + int GeoId; + + if (!PyArg_ParseTuple(args, "i", &GeoId)) + return 0; + + if (this->getSketchObjectPtr()->ExposeInternalGeometry(GeoId)==-1) { + std::stringstream str; + str << "Object does not support internal geometry: " << GeoId; + PyErr_SetString(PyExc_ValueError, str.str().c_str()); + return 0; + } + + Py_Return; +} + +PyObject* SketchObjectPy::DeleteUnusedInternalGeometry(PyObject *args) +{ + int GeoId; + + if (!PyArg_ParseTuple(args, "i", &GeoId)) + return 0; + + if (this->getSketchObjectPtr()->DeleteUnusedInternalGeometry(GeoId)==-1) { + std::stringstream str; + str << "Object does not support internal geometry: " << GeoId; + PyErr_SetString(PyExc_ValueError, str.str().c_str()); + return 0; + } + + Py_Return; +} + Py::Int SketchObjectPy::getConstraintCount(void) const { return Py::Int(this->getSketchObjectPtr()->Constraints.getSize()); diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index 15fcbedfde..ad044b052e 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -62,6 +62,7 @@ SET(SketcherGui_SRCS AppSketcherGuiPy.cpp Command.cpp CommandCreateGeo.cpp + CommandConstraints.h CommandConstraints.cpp CommandSketcherTools.cpp CommandAlterGeometry.cpp diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 86e33c741b..9732335191 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -42,6 +42,7 @@ #include "ViewProviderSketch.h" #include "ui_InsertDatum.h" #include "EditDatumDialog.h" +#include "CommandConstraints.h" using namespace std; using namespace SketcherGui; @@ -154,6 +155,162 @@ bool isSimpleVertex(const Sketcher::SketchObject* Obj, int GeoId, PointPos PosId return false; } +/// Makes a tangency constraint using external construction line between +/// 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, + const Part::Geometry *geom1, + const Part::Geometry *geom2, + int geoId1, + int geoId2 + ) +{ + const Part::GeomEllipse *ellipse = static_cast(geom1); + + Base::Vector3d center=ellipse->getCenter(); + double majord=ellipse->getMajorRadius(); + double minord=ellipse->getMinorRadius(); + double phi=ellipse->getAngleXU(); + + Base::Vector3d center2; + + if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() ) + center2= (static_cast(geom2))->getCenter(); + else if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) + center2= (static_cast(geom2))->getCenter(); + else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId()) + center2= (static_cast(geom2))->getCenter(); + else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) + center2= (static_cast(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(); + + 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); + + // 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 + // 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 + 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); + + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + Gui::Command::abortCommand(); + Gui::Command::updateActive(); + return; + } + + Gui::Command::commitCommand(); + Gui::Command::updateActive(); +} +/// Makes a tangency constraint using external construction line between +/// 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, + const Part::Geometry *geom1, + const Part::Geometry *geom2, + int geoId1, + int geoId2 + ) +{ + const Part::GeomArcOfEllipse *aoe = static_cast(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(geom2))->getCenter(); + else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId()) + center2= (static_cast(geom2))->getCenter(); + else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) + center2= (static_cast(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(); + + 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); + + // 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 + // 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 + 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); + + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + Gui::Command::abortCommand(); + Gui::Command::updateActive(); + return; + } + + Gui::Command::commitCommand(); + Gui::Command::updateActive(); +} + namespace SketcherGui { struct SketchSelection{ @@ -1374,6 +1531,9 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) 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)) ", @@ -1480,6 +1640,97 @@ void CmdSketcherConstrainTangent::activated(int iMsg) QObject::tr("Cannot add a tangency constraint at an unconnected point!")); 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(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(geom2))->getCenter(); + else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) + center2= (static_cast(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( @@ -1533,82 +1784,11 @@ void CmdSketcherConstrainTangent::activated(int iMsg) geom2->getTypeId() == Part::GeomCircle::getClassTypeId() || geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) { - const Part::GeomEllipse *ellipse = static_cast(geom1); - - Base::Vector3d center=ellipse->getCenter(); - double majord=ellipse->getMajorRadius(); - double minord=ellipse->getMinorRadius(); - double phi=ellipse->getAngleXU(); - - Base::Vector3d center2; - - if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() ) - center2= (static_cast(geom2))->getCenter(); - else if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) - center2= (static_cast(geom2))->getCenter(); - else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId()) - center2= (static_cast(geom2))->getCenter(); - else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) - center2= (static_cast(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 { - //construct the point - /*Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))", - Obj->getNameInDocument(), - PoE.x,PoE.y);*/ - - // 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); // create line for major axis - - Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ",Obj->getNameInDocument(),currentgeoid+1); - - // 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(); + Gui::Command::openCommand("add tangent constraint via construction element"); + makeTangentToEllipseviaConstructionLine(Obj,geom1,geom2,GeoId1,GeoId2); getSelection().clearSelection(); return; - } - + } } if( geom1 && geom2 && @@ -1626,70 +1806,8 @@ void CmdSketcherConstrainTangent::activated(int iMsg) geom2->getTypeId() == Part::GeomCircle::getClassTypeId() || geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) { - const Part::GeomArcOfEllipse *aoe = static_cast(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(geom2))->getCenter(); - else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId()) - center2= (static_cast(geom2))->getCenter(); - else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) - center2= (static_cast(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 { - - // 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); // create line for major axis - - Gui::Command::doCommand(Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ",Obj->getNameInDocument(),currentgeoid+1); - - // 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(); + Gui::Command::openCommand("add tangent constraint via construction element"); + makeTangentToArcOfEllipseviaConstructionLine(Obj,geom1,geom2,GeoId1,GeoId2); getSelection().clearSelection(); return; } diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.h b/src/Mod/Sketcher/Gui/CommandConstraints.h new file mode 100644 index 0000000000..8e8d3f635c --- /dev/null +++ b/src/Mod/Sketcher/Gui/CommandConstraints.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (c) 2014 Abdullah.tahiri.yo@gmail.com * + * * + * 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 * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_CommandConstraints_H +#define SKETCHERGUI_CommandConstraints_H + +namespace SketcherGui { + +// These functions are declared here to promote code reuse from other modules + +/// Makes a tangency constraint using external construction line between +/// 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 makeTangentToEllipseviaConstructionLine(const Sketcher::SketchObject* Obj, + const Part::Geometry *geom1, + const Part::Geometry *geom2, + int geoId1, + int geoId2 + ); +/// Makes a tangency constraint using external construction line between +/// 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 makeTangentToArcOfEllipseviaConstructionLine(const Sketcher::SketchObject* Obj, + const Part::Geometry *geom1, + const Part::Geometry *geom2, + int geoId1, + int geoId2 + ); + +} +#endif // SKETCHERGUI_DrawSketchHandler_H + diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 8d7bf1c485..16161d8959 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -1709,7 +1709,7 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(EditCurve); - if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f), + if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0], AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -1928,7 +1928,8 @@ public: if (method == PERIAPSIS_APOAPSIS_B) { if (mode == STATUS_SEEK_PERIAPSIS) { setPositionText(onSketchPos); - if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) { // TODO: ellipse prio 1 + if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f), + AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr1); return; } @@ -1945,6 +1946,11 @@ public: sketchgui->drawEdit(editCurve); // Suggestions for ellipse and curves are disabled because many tangent constraints // need an intermediate point or line. + if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f), + AutoConstraint::CURVE)) { + renderSuggestConstraintsCursor(sugConstr2); + return; + } } else if (mode == STATUS_SEEK_B) { solveEllipse(onSketchPos); approximateEllipse(); @@ -1955,6 +1961,11 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(editCurve); + if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.f,0.f), + AutoConstraint::CURVE)) { + renderSuggestConstraintsCursor(sugConstr3); + return; + } } } else { // method is CENTER_PERIAPSIS_B if (mode == STATUS_SEEK_CENTROID) { @@ -1974,6 +1985,11 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(editCurve); + if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centroid, + AutoConstraint::CURVE)) { + renderSuggestConstraintsCursor(sugConstr2); + return; + } } else if ((mode == STATUS_SEEK_A) || (mode == STATUS_SEEK_B)) { solveEllipse(onSketchPos); approximateEllipse(); @@ -1984,6 +2000,11 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(editCurve); + if (seekAutoConstraint(sugConstr3, onSketchPos, onSketchPos - centroid, + AutoConstraint::CURVE)) { + renderSuggestConstraintsCursor(sugConstr3); + return; + } } } applyCursor(); @@ -2037,7 +2058,7 @@ public: return true; } protected: - std::vector sugConstr1; + std::vector sugConstr1, sugConstr2, sugConstr3; private: SelectMode mode; /// the method of constructing the ellipse @@ -2344,7 +2365,6 @@ private: octave << "plot(centroid(1), centroid(2), \"b.\", \"markersize\", 5);\n"; octave << "plot(f(1), f(2), \"c.\", \"markersize\", 5);\n"; octave << "plot(fPrime(1), fPrime(2), \"m.\", \"markersize\", 5);\n"; - octave << "n = [periapsis(1) - f(1), periapsis(2) - f(2)];\n"; octave << "h = quiver(f(1),f(2),n(1),n(2), 0);\n"; octave << "set (h, \"maxheadsize\", 0.1);\n\n"; @@ -2490,67 +2510,10 @@ private: currentgeoid++; try { - // create line for major axis Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addGeometry(Part.Line" - "(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - periapsis.fX,periapsis.fY, - apoapsis.fX,apoapsis.fY); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+1); - - // constrain major axis - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addConstraint(Sketcher.Constraint" - "('InternalAlignment:EllipseMajorDiameter',%d,%d)) ", - sketchgui->getObject()->getNameInDocument(), - currentgeoid+1,currentgeoid); - - // create line for minor axis - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addGeometry(Part.Line" - "(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - positiveB.fX,positiveB.fY, - negativeB.fX,negativeB.fY); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+2); - - // constrain minor axis - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addConstraint(Sketcher.Constraint" - "('InternalAlignment:EllipseMinorDiameter',%d,%d)) ", - sketchgui->getObject()->getNameInDocument(), - currentgeoid+2,currentgeoid); - - // create point for focus - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - f.fX,f.fY); - - // constrain focus - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addConstraint(Sketcher.Constraint" - "('InternalAlignment:EllipseFocus1',%d,%d,%d)) ", - sketchgui->getObject()->getNameInDocument(), - currentgeoid+3,Sketcher::start,currentgeoid); - - // create point for second focus - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - fPrime.fX,fPrime.fY); - - // constrain second focus - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.addConstraint(Sketcher.Constraint" - "('InternalAlignment:EllipseFocus2',%d,%d,%d)) ", - sketchgui->getObject()->getNameInDocument(), - currentgeoid+4,Sketcher::start,currentgeoid); + "App.ActiveDocument.%s.ExposeInternalGeometry(%d)", + sketchgui->getObject()->getNameInDocument(), + currentgeoid); } catch (const Base::Exception& e) { Base::Console().Error("%s\n", e.what()); @@ -2562,10 +2525,35 @@ private: Gui::Command::commitCommand(); Gui::Command::updateActive(); - // add auto constraints for the center point - if (sugConstr1.size() > 0) { - createAutoConstraints(sugConstr1, currentgeoid, Sketcher::mid); - sugConstr1.clear(); + if (method == CENTER_PERIAPSIS_B) { + // add auto constraints for the center point + if (sugConstr1.size() > 0) { + createAutoConstraints(sugConstr1, currentgeoid, Sketcher::mid); + sugConstr1.clear(); + } + if (sugConstr2.size() > 0) { + createAutoConstraints(sugConstr2, currentgeoid, Sketcher::none); + sugConstr2.clear(); + } + if (sugConstr3.size() > 0) { + createAutoConstraints(sugConstr3, currentgeoid, Sketcher::none); + sugConstr3.clear(); + } + } + + if (method == PERIAPSIS_APOAPSIS_B) { + if (sugConstr1.size() > 0) { + createAutoConstraints(sugConstr1, currentgeoid, Sketcher::none); + sugConstr1.clear(); + } + if (sugConstr2.size() > 0) { + createAutoConstraints(sugConstr2, currentgeoid, Sketcher::none); + sugConstr2.clear(); + } + if (sugConstr3.size() > 0) { + createAutoConstraints(sugConstr3, currentgeoid, Sketcher::none); + sugConstr3.clear(); + } } // delete the temp construction curve from the sketch @@ -2720,11 +2708,11 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(EditCurve); - if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f), + if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centerPoint, AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr2); return; - } + } } else if (Mode==STATUS_SEEK_Third) { // angle between the major axis of the ellipse and the X axis @@ -2751,8 +2739,7 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(EditCurve); - if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.f,0.f), - AutoConstraint::CURVE)) { + if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr3); return; } @@ -2791,8 +2778,7 @@ public: setPositionText(onSketchPos, text); sketchgui->drawEdit(EditCurve); - if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2D(0.f,0.f), - AutoConstraint::CURVE)) { + if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2D(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr4); return; } @@ -2849,11 +2835,14 @@ public: double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ; arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2; + bool isOriginalArcCCW=true; + if (arcAngle > 0) endAngle = startAngle + arcAngle; else { endAngle = startAngle; startAngle += arcAngle; + isOriginalArcCCW=false; } Base::Vector2D majAxisDir,minAxisDir,minAxisPoint,majAxisPoint; @@ -2883,19 +2872,7 @@ public: phi-=M_PI/2; double t=a; a=b; b=t;//swap a,b } - - Base::Vector3d center = Base::Vector3d(centerPoint.fX,centerPoint.fY,0); - - Base::Vector3d majorpositiveend = center + a * Base::Vector3d(cos(phi),sin(phi),0); - Base::Vector3d majornegativeend = center - a * Base::Vector3d(cos(phi),sin(phi),0); - Base::Vector3d minorpositiveend = center + b * Base::Vector3d(-sin(phi),cos(phi),0); - Base::Vector3d minornegativeend = center - b * Base::Vector3d(-sin(phi),cos(phi),0); - - double cf = sqrt( abs(a*a - b*b) );//using abs, avoided using different formula for a>b/agetObject()->getNameInDocument(), - majorpositiveend.x,majorpositiveend.y,majornegativeend.x,majornegativeend.y); // create line for major axis - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+1); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMajorDiameter',%d,%d)) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+1,currentgeoid); // constrain major axis - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - minorpositiveend.x,minorpositiveend.y,minornegativeend.x,minornegativeend.y); // create line for minor axis - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.toggleConstruction(%d) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+2); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMinorDiameter',%d,%d)) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+2,currentgeoid); // constrain minor axis - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - focus1P.x,focus1P.y); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus1',%d,%d,%d)) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+3,Sketcher::start,currentgeoid); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))", - sketchgui->getObject()->getNameInDocument(), - focus2P.x,focus2P.y); - - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus2',%d,%d,%d)) ", - sketchgui->getObject()->getNameInDocument(),currentgeoid+4,Sketcher::start,currentgeoid); + Gui::Command::doCommand(Gui::Command::Doc, + "App.ActiveDocument.%s.ExposeInternalGeometry(%d)", + sketchgui->getObject()->getNameInDocument(), + currentgeoid); } catch (const Base::Exception& e) { Base::Console().Error("%s\n", e.what()); @@ -2959,15 +2907,27 @@ public: // add auto constraints for the center point if (sugConstr1.size() > 0) { - createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::mid); + createAutoConstraints(sugConstr1, currentgeoid, Sketcher::mid); sugConstr1.clear(); } - // add suggested constraints for circumference + // add suggested constraints for arc if (sugConstr2.size() > 0) { - //createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::none); + createAutoConstraints(sugConstr2, currentgeoid, Sketcher::none); sugConstr2.clear(); } + + // add suggested constraints for start of arc + if (sugConstr3.size() > 0) { + createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::start:Sketcher::end); + sugConstr3.clear(); + } + + // add suggested constraints for start of arc + if (sugConstr4.size() > 0) { + createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::end:Sketcher::start); + sugConstr4.clear(); + } EditCurve.clear(); sketchgui->drawEdit(EditCurve); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index b6bdcbc914..5485ec1315 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ #include "DrawSketchHandler.h" #include "ViewProviderSketch.h" +#include "CommandConstraints.h" using namespace SketcherGui; @@ -138,6 +140,8 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested if (!sketchgui->Autoconstraints.getValue()) return 0; // If Autoconstraints property is not set quit + Base::Vector3d hitShapeDir = Base::Vector3d(0,0,0); // direction of hit shape (if it is a line, the direction of the line) + // Get Preselection int preSelPnt = sketchgui->getPreselectPoint(); int preSelCrv = sketchgui->getPreselectCurve(); @@ -146,16 +150,29 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested Sketcher::PointPos PosId = Sketcher::none; if (preSelPnt != -1) sketchgui->getSketchObject()->getGeoVertexIndex(preSelPnt, GeoId, PosId); - else if (preSelCrv != -1) + else if (preSelCrv != -1){ GeoId = preSelCrv; + const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId); + + if(geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + const Part::GeomLineSegment *line = static_cast(geom); + hitShapeDir= line->getEndPoint()-line->getStartPoint(); + } + + } else if (preSelCrs == 0) { // root point GeoId = -1; PosId = Sketcher::start; } - else if (preSelCrs == 1) // x axis + else if (preSelCrs == 1){ // x axis GeoId = -1; - else if (preSelCrs == 2) // y axis + hitShapeDir = Base::Vector3d(1,0,0); + + } + else if (preSelCrs == 2){ // y axis GeoId = -2; + hitShapeDir = Base::Vector3d(0,1,0); + } if (GeoId != Constraint::GeoUndef) { // Currently only considers objects in current Sketcher @@ -171,12 +188,25 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested constr.Type = Sketcher::PointOnObject; else if (type == AutoConstraint::CURVE && PosId == Sketcher::none) constr.Type = Sketcher::Tangent; + + if(constr.Type == Sketcher::Tangent && Dir.Length() > 1e-8 && hitShapeDir.Length() > 1e-8) { // We are hitting a line and have hitting vector information + Base::Vector3d dir3d = Base::Vector3d(Dir.fX,Dir.fY,0); + double cosangle=dir3d.Normalize()*hitShapeDir.Normalize(); + + // the angle between the line and the hitting direction are over around 6 degrees (it is substantially parallel) + // or if it is an sketch axis (that can not move to accomodate to the shape), then only if it is around 6 degrees with the normal (around 84 degrees) + if(abs(cosangle) < 0.995f || ((GeoId==-1 || GeoId==-2) && abs(cosangle) < 0.1)) + suggestedConstraints.push_back(constr); + + + return suggestedConstraints.size(); + } if (constr.Type != Sketcher::None) suggestedConstraints.push_back(constr); } - - if (Dir.Length() < 1e-8) + + if (Dir.Length() < 1e-8 || type == AutoConstraint::CURVE) // Direction not set so return; return suggestedConstraints.size(); @@ -263,8 +293,8 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested Base::Vector3d focus1PMirrored = focus1P + 2*distancetoline*norm; // mirror of focus1 with respect to the line double error = abs((focus1PMirrored-focus2P).Length() - 2*a); - - if ( error< tangDeviation ) { + + if ( error< tangDeviation) { tangId = i; tangDeviation = error; } @@ -297,7 +327,52 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested tangDeviation = projDist; } } - } + } else if ((*it)->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + const Part::GeomArcOfEllipse *aoe = dynamic_cast((*it)); + + Base::Vector3d center = aoe->getCenter(); + + double a = aoe->getMajorRadius(); + double b = aoe->getMinorRadius(); + double phi = aoe->getAngleXU(); + + double cf = sqrt(a*a - b*b); + + Base::Vector3d focus1P = center + cf * Base::Vector3d(cos(phi),sin(phi),0); + Base::Vector3d focus2P = center - cf * Base::Vector3d(cos(phi),sin(phi),0); + + Base::Vector3d norm = Base::Vector3d(Dir.fY,-Dir.fX).Normalize(); + + double distancetoline = norm*(tmpPos - focus1P); // distance focus1 to line + + Base::Vector3d focus1PMirrored = focus1P + 2*distancetoline*norm; // mirror of focus1 with respect to the line + + double error = abs((focus1PMirrored-focus2P).Length() - 2*a); + + if ( error< tangDeviation ) { + tangId = i; + tangDeviation = error; + } + + if (error < tangDeviation) { + double startAngle, endAngle; + aoe->getRange(startAngle, endAngle); + + double angle = Base::fmod( + atan2(-aoe->getMajorRadius()*((tmpPos.x-center.x)*sin(aoe->getAngleXU())-(tmpPos.y-center.y)*cos(aoe->getAngleXU())), + aoe->getMinorRadius()*((tmpPos.x-center.x)*cos(aoe->getAngleXU())+(tmpPos.y-center.y)*sin(aoe->getAngleXU())) + )- startAngle, 2.f*M_PI); + + while(angle < startAngle) + angle += 2*D_PI; // Bring it to range of arc + + // if the point is on correct side of arc + if (angle <= endAngle) { // Now need to check only one side + tangId = i; + tangDeviation = error; + } + } + } } if (tangId != Constraint::GeoUndef) { @@ -364,6 +439,58 @@ void DrawSketchHandler::createAutoConstraints(const std::vector ); } break; case Sketcher::Tangent: { + Sketcher::SketchObject* Obj = dynamic_cast(sketchgui->getObject()); + + const Part::Geometry *geom1 = Obj->getGeometry(geoId1); + const Part::Geometry *geom2 = Obj->getGeometry(it->GeoId); + + int geoId2 = it->GeoId; + + // ellipse tangency support using construction elements (lines) + if( geom1 && geom2 && + ( geom1->getTypeId() == Part::GeomEllipse::getClassTypeId() || + geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() )){ + + if(geom1->getTypeId() != Part::GeomEllipse::getClassTypeId()) + std::swap(geoId1,geoId2); + + // geoId1 is the ellipse + geom1 = Obj->getGeometry(geoId1); + geom2 = Obj->getGeometry(geoId2); + + if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() || + geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || + 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); + return; + } + } + + // arc of ellipse tangency support using external elements + 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::GeomCircle::getClassTypeId() || + geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) { + // 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); + return; + } + } + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%i, %i)) " ,sketchgui->getObject()->getNameInDocument() ,geoId1, it->GeoId