diff --git a/src/Mod/Sketcher/App/Constraint.h b/src/Mod/Sketcher/App/Constraint.h index 18adacbd2..e8bbfe67e 100644 --- a/src/Mod/Sketcher/App/Constraint.h +++ b/src/Mod/Sketcher/App/Constraint.h @@ -46,7 +46,8 @@ enum ConstraintType { Equal = 12, PointOnObject = 13, Symmetric = 14, - InternalAlignment = 15 + InternalAlignment = 15, + SnellsLaw = 16 }; enum InternalAlignmentType { diff --git a/src/Mod/Sketcher/App/ConstraintPyImp.cpp b/src/Mod/Sketcher/App/ConstraintPyImp.cpp index 6e0d80c1c..2b51b86fa 100644 --- a/src/Mod/Sketcher/App/ConstraintPyImp.cpp +++ b/src/Mod/Sketcher/App/ConstraintPyImp.cpp @@ -378,17 +378,34 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) } PyErr_Clear(); - if (PyArg_ParseTuple(args, "siiiiii", &ConstraintType, &FirstIndex, &FirstPos, &SecondIndex, &SecondPos, &ThirdIndex, &ThirdPos)) { - // ConstraintType, GeoIndex1, PosIndex1, GeoIndex2, PosIndex2, GeoIndex3, PosIndex3 - if (strcmp("Symmetric",ConstraintType) == 0 ) { - this->getConstraintPtr()->Type = Symmetric; - this->getConstraintPtr()->First = FirstIndex; - this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; - this->getConstraintPtr()->Second = SecondIndex; - this->getConstraintPtr()->SecondPos = (Sketcher::PointPos) SecondPos; - this->getConstraintPtr()->Third = ThirdIndex; - this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos) ThirdPos; - return 0; + if (PyArg_ParseTuple(args, "siiiiiO", &ConstraintType, &FirstIndex, &FirstPos, &SecondIndex, &SecondPos, &ThirdIndex, &index_or_value)) { + if (PyInt_Check(index_or_value)) { + ThirdPos = PyInt_AsLong(index_or_value); + // ConstraintType, GeoIndex1, PosIndex1, GeoIndex2, PosIndex2, GeoIndex3, PosIndex3 + if (strcmp("Symmetric",ConstraintType) == 0 ) { + this->getConstraintPtr()->Type = Symmetric; + this->getConstraintPtr()->First = FirstIndex; + this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; + this->getConstraintPtr()->Second = SecondIndex; + this->getConstraintPtr()->SecondPos = (Sketcher::PointPos) SecondPos; + this->getConstraintPtr()->Third = ThirdIndex; + this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos) ThirdPos; + return 0; + } + } + if (PyNumber_Check(index_or_value)) { // can be float or int + Value = PyFloat_AsDouble(index_or_value); + if (strcmp("SnellsLaw",ConstraintType) == 0 ) { + this->getConstraintPtr()->Type = SnellsLaw; + this->getConstraintPtr()->First = FirstIndex; + this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; + this->getConstraintPtr()->Second = SecondIndex; + this->getConstraintPtr()->SecondPos = (Sketcher::PointPos) SecondPos; + this->getConstraintPtr()->Third = ThirdIndex; + this->getConstraintPtr()->ThirdPos = none; + this->getConstraintPtr()->Value = Value; + return 0; + } } } @@ -436,6 +453,7 @@ std::string ConstraintPy::representation(void) const else result << "'AngleViaPoint'>"; break; + case SnellsLaw : result << "'SnellsLaw'>"; break; case InternalAlignment : switch(this->getConstraintPtr()->AlignmentType) { case Undef : result << "'InternalAlignment:Undef'>";break; diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 94d844b8c..f611f8b57 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -766,6 +766,13 @@ int Sketch::addConstraint(const Constraint *constraint) break; } break; + case SnellsLaw: + assert(constraint->ThirdPos==none); + rtn = addSnellsLawConstraint(constraint->First, constraint->FirstPos, + constraint->Second, constraint->SecondPos, + constraint->Third, + constraint->Value); + break; case None: break; } @@ -1642,6 +1649,69 @@ int Sketch::addSymmetricConstraint(int geoId1, PointPos pos1, int geoId2, PointP return -1; } +int Sketch::addSnellsLawConstraint(int geoIdRay1, PointPos posRay1, + int geoIdRay2, PointPos posRay2, + int geoIdBnd, + double n2divn1) +{ + + geoIdRay1 = checkGeoId(geoIdRay1); + geoIdRay2 = checkGeoId(geoIdRay2); + geoIdBnd = checkGeoId(geoIdBnd); + + if (Geoms[geoIdRay1].type == Point || + Geoms[geoIdRay2].type == Point){ + assert(0);//point is not a curve. Not applicable! + return -1; + } + + GCS::Curve* ray1 =getGCSCurveByGeoId(geoIdRay1); + GCS::Curve* ray2 =getGCSCurveByGeoId(geoIdRay2); + GCS::Curve* boundary =getGCSCurveByGeoId(geoIdBnd); + if (!ray1 || !ray2 || !boundary) { + assert(0); + Base::Console().Error("addSnellsLawConstraint: getGCSCurveByGeoId returned NULL!\n"); + return -1; + } + + int pointId1 = getPointId(geoIdRay1, posRay1); + int pointId2 = getPointId(geoIdRay2, posRay2); + if ( pointId1 < 0 || pointId1 >= int(Points.size()) || + pointId2 < 0 || pointId2 >= int(Points.size()) ){ + assert(0); + Base::Console().Error("addSnellsLawConstraint: point index out of range.\n"); + return -1; + } + GCS::Point &p1 = Points[pointId1]; + GCS::Point &p2 = Points[pointId2]; + + // add the parameters (refractive indexes) + FixParameters.push_back(new double(0.0)); + double *n1 = FixParameters[FixParameters.size()-1]; + FixParameters.push_back(new double(0.0)); + double *n2 = FixParameters[FixParameters.size()-1]; + + if ( abs(n2divn1) >= 1.0 ){ + *n2 = n2divn1; + *n1 = 1.0; + } else { + *n2 = 1.0; + *n1 = 1/n2divn1; + } + + int tag = -1; + //tag = Sketch::addPointOnObjectConstraint(geoIdRay1, posRay1, geoIdBnd);//increases ConstraintsCounter + tag = ++ConstraintsCounter; + //GCSsys.addConstraintP2PCoincident(p1, p2, tag); + GCSsys.addConstraintSnellsLaw(*ray1, *ray2, + *boundary, p1, + n1, n2, + posRay1==start, posRay2 == end, + tag); + return ConstraintsCounter; +} + + int Sketch::addInternalAlignmentEllipseMajorDiameter(int geoId1, int geoId2) { std::swap(geoId1, geoId2); diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index 8ee9d6934..60348cf0a 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -187,6 +187,11 @@ public: int addSymmetricConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2, int geoId3); /// add a symmetric constraint between three points, the last point is in the middle of the first two int addSymmetricConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2, int geoId3, PointPos pos3); + /// add a snell's law constraint + int addSnellsLawConstraint(int geoIdRay1, PointPos posRay1, + int geoIdRay2, PointPos posRay2, + int geoIdBnd, + double n2divn1); //@} /// Internal Alignment constraints diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 8800fae1b..8838e4cf6 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -196,7 +196,8 @@ int SketchObject::setDatum(int ConstrId, double Datum) type != Radius && type != Angle && type != Tangent && //for tangent, value==0 is autodecide, value==Pi/2 is external and value==-Pi/2 is internal - type != Perpendicular) + type != Perpendicular && + type != SnellsLaw) return -1; if ((type == Distance || type == Radius) && Datum <= 0) diff --git a/src/Mod/Sketcher/App/freegcs/Constraints.cpp b/src/Mod/Sketcher/App/freegcs/Constraints.cpp index fc5d08c58..99a0d3772 100644 --- a/src/Mod/Sketcher/App/freegcs/Constraints.cpp +++ b/src/Mod/Sketcher/App/freegcs/Constraints.cpp @@ -926,8 +926,8 @@ ConstraintPointOnEllipse::ConstraintPointOnEllipse(Point &p, Ellipse &e) pvec.push_back(p.y); pvec.push_back(e.center.x); pvec.push_back(e.center.y); - pvec.push_back(e.focus1X); - pvec.push_back(e.focus1Y); + pvec.push_back(e.focus1.x); + pvec.push_back(e.focus1.y); pvec.push_back(e.radmin); origpvec = pvec; rescale(); @@ -939,8 +939,8 @@ ConstraintPointOnEllipse::ConstraintPointOnEllipse(Point &p, ArcOfEllipse &a) pvec.push_back(p.y); pvec.push_back(a.center.x); pvec.push_back(a.center.y); - pvec.push_back(a.focus1X); - pvec.push_back(a.focus1Y); + pvec.push_back(a.focus1.x); + pvec.push_back(a.focus1.y); pvec.push_back(a.radmin); origpvec = pvec; rescale(); @@ -1030,8 +1030,8 @@ ConstraintEllipseTangentLine::ConstraintEllipseTangentLine(Line &l, Ellipse &e) pvec.push_back(l.p2.y); pvec.push_back(e.center.x); pvec.push_back(e.center.y); - pvec.push_back(e.focus1X); - pvec.push_back(e.focus1Y); + pvec.push_back(e.focus1.x); + pvec.push_back(e.focus1.y); pvec.push_back(e.radmin); origpvec = pvec; rescale(); @@ -1045,8 +1045,8 @@ ConstraintEllipseTangentLine::ConstraintEllipseTangentLine(Line &l, ArcOfEllipse pvec.push_back(l.p2.y); pvec.push_back(a.center.x); pvec.push_back(a.center.y); - pvec.push_back(a.focus1X); - pvec.push_back(a.focus1Y); + pvec.push_back(a.focus1.x); + pvec.push_back(a.focus1.y); pvec.push_back(a.radmin); origpvec = pvec; rescale(); @@ -1354,8 +1354,8 @@ ConstraintInternalAlignmentPoint2Ellipse::ConstraintInternalAlignmentPoint2Ellip pvec.push_back(p1.y); pvec.push_back(e.center.x); pvec.push_back(e.center.y); - pvec.push_back(e.focus1X); - pvec.push_back(e.focus1Y); + pvec.push_back(e.focus1.x); + pvec.push_back(e.focus1.y); pvec.push_back(e.radmin); origpvec = pvec; rescale(); @@ -1368,8 +1368,8 @@ ConstraintInternalAlignmentPoint2Ellipse::ConstraintInternalAlignmentPoint2Ellip pvec.push_back(p1.y); pvec.push_back(a.center.x); pvec.push_back(a.center.y); - pvec.push_back(a.focus1X); - pvec.push_back(a.focus1Y); + pvec.push_back(a.focus1.x); + pvec.push_back(a.focus1.y); pvec.push_back(a.radmin); origpvec = pvec; rescale(); @@ -1803,13 +1803,13 @@ ConstraintEqualMajorAxesEllipse:: ConstraintEqualMajorAxesEllipse(Ellipse &e1, E { pvec.push_back(e1.center.x); pvec.push_back(e1.center.y); - pvec.push_back(e1.focus1X); - pvec.push_back(e1.focus1Y); + pvec.push_back(e1.focus1.x); + pvec.push_back(e1.focus1.y); pvec.push_back(e1.radmin); pvec.push_back(e2.center.x); pvec.push_back(e2.center.y); - pvec.push_back(e2.focus1X); - pvec.push_back(e2.focus1Y); + pvec.push_back(e2.focus1.x); + pvec.push_back(e2.focus1.y); pvec.push_back(e2.radmin); origpvec = pvec; rescale(); @@ -1819,13 +1819,13 @@ ConstraintEqualMajorAxesEllipse:: ConstraintEqualMajorAxesEllipse(ArcOfEllipse & { pvec.push_back(a1.center.x); pvec.push_back(a1.center.y); - pvec.push_back(a1.focus1X); - pvec.push_back(a1.focus1Y); + pvec.push_back(a1.focus1.x); + pvec.push_back(a1.focus1.y); pvec.push_back(a1.radmin); pvec.push_back(e2.center.x); pvec.push_back(e2.center.y); - pvec.push_back(e2.focus1X); - pvec.push_back(e2.focus1Y); + pvec.push_back(e2.focus1.x); + pvec.push_back(e2.focus1.y); pvec.push_back(e2.radmin); origpvec = pvec; rescale(); @@ -1835,13 +1835,13 @@ ConstraintEqualMajorAxesEllipse:: ConstraintEqualMajorAxesEllipse(ArcOfEllipse & { pvec.push_back(a1.center.x); pvec.push_back(a1.center.y); - pvec.push_back(a1.focus1X); - pvec.push_back(a1.focus1Y); + pvec.push_back(a1.focus1.x); + pvec.push_back(a1.focus1.y); pvec.push_back(a1.radmin); pvec.push_back(a2.center.x); pvec.push_back(a2.center.y); - pvec.push_back(a2.focus1X); - pvec.push_back(a2.focus1Y); + pvec.push_back(a2.focus1.x); + pvec.push_back(a2.focus1.y); pvec.push_back(a2.radmin); origpvec = pvec; rescale(); @@ -1948,8 +1948,8 @@ ConstraintEllipticalArcRangeToEndPoints::ConstraintEllipticalArcRangeToEndPoints pvec.push_back(angle_t); pvec.push_back(a.center.x); pvec.push_back(a.center.y); - pvec.push_back(a.focus1X); - pvec.push_back(a.focus1Y); + pvec.push_back(a.focus1.x); + pvec.push_back(a.focus1.y); pvec.push_back(a.radmin); origpvec = pvec; rescale(); @@ -2184,7 +2184,7 @@ double ConstraintEllipticalArcRangeToEndPoints::maxStep(MAP_pD_D &dir, double li return lim; } -// L2LAngle +// ConstraintAngleViaPoint ConstraintAngleViaPoint::ConstraintAngleViaPoint(Curve &acrv1, Curve &acrv2, Point p, double* angle) { pvec.push_back(angle); @@ -2229,11 +2229,11 @@ double ConstraintAngleViaPoint::error() { if (pvecChangedFlag) ReconstructGeomPointers(); double ang=*angle(); - Vector2D n1 = crv1->CalculateNormal(poa); - Vector2D n2 = crv2->CalculateNormal(poa); + DeriVector2 n1 = crv1->CalculateNormal(poa); + DeriVector2 n2 = crv2->CalculateNormal(poa); //rotate n1 by angle - Vector2D n1r (n1.x*cos(ang) - n1.y*sin(ang), n1.x*sin(ang) + n1.y*cos(ang) ); + DeriVector2 n1r (n1.x*cos(ang) - n1.y*sin(ang), n1.x*sin(ang) + n1.y*cos(ang) ); //calculate angle between n1r and n2. Since we have rotated the n1, the angle is the error function. //for our atan2, y is a dot product (n2) * (n1r rotated ccw by 90 degrees). @@ -2257,13 +2257,123 @@ double ConstraintAngleViaPoint::grad(double *param) if (pvecChangedFlag) ReconstructGeomPointers(); if (param == angle()) deriv += -1.0; - Vector2D n1 = crv1->CalculateNormal(poa); - Vector2D n2 = crv2->CalculateNormal(poa); + DeriVector2 n1 = crv1->CalculateNormal(poa, param); + DeriVector2 n2 = crv2->CalculateNormal(poa, param); + deriv -= ( (-n1.dx)*n1.y / pow(n1.length(),2) + n1.dy*n1.x / pow(n1.length(),2) ); + deriv += ( (-n2.dx)*n2.y / pow(n2.length(),2) + n2.dy*n2.x / pow(n2.length(),2) ); - Vector2D dn1 = crv1->CalculateNormal(poa, param); - Vector2D dn2 = crv2->CalculateNormal(poa, param); - deriv -= ( (-dn1.x)*n1.y / pow(n1.length(),2) + dn1.y*n1.x / pow(n1.length(),2) ); - deriv += ( (-dn2.x)*n2.y / pow(n2.length(),2) + dn2.y*n2.x / pow(n2.length(),2) ); + +//use numeric for testing +#if 0 + double const eps = 0.00001; + double oldparam = *param; + double v0 = this->error(); + *param += eps; + double vr = this->error(); + *param = oldparam - eps; + double vl = this->error(); + *param = oldparam; + //If not nasty, real derivative should be between left one and right one + double numretl = (v0-vl)/eps; + double numretr = (vr-v0)/eps; + assert(deriv <= std::max(numretl,numretr) ); + assert(deriv >= std::min(numretl,numretr) ); +#endif + + return scale * deriv; +} + +//ConstraintSnell + +ConstraintSnell::ConstraintSnell(Curve &ray1, Curve &ray2, Curve &boundary, Point p, double* n1, double* n2, bool flipn1, bool flipn2) +{ + pvec.push_back(n1); + pvec.push_back(n2); + pvec.push_back(p.x); + pvec.push_back(p.y); + ray1.PushOwnParams(pvec); + ray2.PushOwnParams(pvec); + boundary.PushOwnParams(pvec); + this->ray1 = ray1.Copy(); + this->ray2 = ray2.Copy(); + this->boundary = boundary.Copy(); + origpvec = pvec; + pvecChangedFlag=true; + + this->flipn1 = flipn1; + this->flipn2 = flipn2; + + rescale(); +} +ConstraintSnell::~ConstraintSnell() +{ + delete ray1; ray1 = 0; + delete ray2; ray2 = 0; + delete boundary; boundary = 0; +} + +void ConstraintSnell::ReconstructGeomPointers() +{ + int cnt=0; + cnt++; cnt++;//skip n1, n2 - we have an inline function for that + poa.x = pvec[cnt]; cnt++; + poa.y = pvec[cnt]; cnt++; + ray1->ReconstructOnNewPvec(pvec,cnt); + ray2->ReconstructOnNewPvec(pvec,cnt); + boundary->ReconstructOnNewPvec(pvec,cnt); + pvecChangedFlag=false; +} + +ConstraintType ConstraintSnell::getTypeId() +{ + return Snell; +} + +void ConstraintSnell::rescale(double coef) +{ + scale = coef * 1.; +} + +//error and gradient combined. Values are returned through pointers. +void ConstraintSnell::errorgrad(double *err, double *grad, double* param) +{ + if (pvecChangedFlag) ReconstructGeomPointers(); + DeriVector2 tang1 = ray1->CalculateNormal(poa, param).rotate90cw().getNormalized(); + DeriVector2 tang2 = ray2->CalculateNormal(poa, param).rotate90cw().getNormalized(); + DeriVector2 tangB = boundary->CalculateNormal(poa, param).rotate90cw().getNormalized(); + double sin1, dsin1, sin2, dsin2; + sin1 = tang1.scalarProd(tangB, &dsin1);//sinus of angle of incidence + sin2 = tang2.scalarProd(tangB, &dsin2); + if (flipn1) {sin1 = -sin1; dsin1 = -dsin1;} + if (flipn2) {sin2 = -sin2; dsin2 = -dsin2;} + + double dn1 = (param == n1()) ? 1.0 : 0.0; + double dn2 = (param == n2()) ? 1.0 : 0.0; + if (err) + *err = *n1()*sin1 - *n2()*sin2; + if (grad) + *grad = dn1*sin1 + *n1()*dsin1 - dn2*sin2 - *n2()*dsin2; +} + +double ConstraintSnell::error() +{ + double err; + errorgrad(&err, 0, 0); + return scale * err; +} + +double ConstraintSnell::grad(double *param) +{ + + //first of all, check that we need to compute anything. + int i; + for( i=0 ; isetTag(tagId); + return addConstraint(constr); +} + int System::addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, InternalAlignmentType alignmentType, int tagId) { Constraint *constr = new ConstraintInternalAlignmentPoint2Ellipse(e, p1, alignmentType); diff --git a/src/Mod/Sketcher/App/freegcs/GCS.h b/src/Mod/Sketcher/App/freegcs/GCS.h index 4c1de001e..76b2325fd 100644 --- a/src/Mod/Sketcher/App/freegcs/GCS.h +++ b/src/Mod/Sketcher/App/freegcs/GCS.h @@ -172,6 +172,11 @@ namespace GCS int addConstraintEqualRadius(Arc &a1, Arc &a2, int tagId=0); int addConstraintP2PSymmetric(Point &p1, Point &p2, Line &l, int tagId=0); int addConstraintP2PSymmetric(Point &p1, Point &p2, Point &p, int tagId=0); + int addConstraintSnellsLaw(Curve &ray1, Curve &ray2, + Curve &boundary, Point p, + double* n1, double* n2, + bool flipn1, bool flipn2, + int tagId); // internal alignment constraints int addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, InternalAlignmentType alignmentType, int tagId=0); diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index f5fdc1651..851ce1824 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -2476,6 +2476,138 @@ bool CmdSketcherConstrainSymmetric::isActive(void) return isCreateConstraintActive( getActiveGuiDocument() ); } +DEF_STD_CMD_A(CmdSketcherConstrainSnellsLaw); + +CmdSketcherConstrainSnellsLaw::CmdSketcherConstrainSnellsLaw() + :Command("Sketcher_ConstrainSnellsLaw") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("Constrain refraction (Snell's law')"); + sToolTipText = QT_TR_NOOP("Create a refraction law (Snell's law) constraint between two endpoints of rays and an edge as an interface."); + sWhatsThis = "Sketcher_ConstrainSnellsLaw"; + sStatusTip = sToolTipText; + sPixmap = "Constraint_SnellsLaw"; + sAccel = ""; + eType = ForEdit; +} + +void CmdSketcherConstrainSnellsLaw::activated(int iMsg) +{ + QString strHelp = QObject::tr("Select two endpoints of lines to act as rays, and" + " an edge representing a boundary. The first" + " selected point corresponds to index n1, second" + " - to n2, and datum value sets the ratio n2/n1.", + "Constraint_SnellsLaw"); + QString strError; + + const char dmbg[] = "Constraint_SnellsLaw"; + + try{ + // get the selection + std::vector selection = getSelection().getSelectionEx(); + Sketcher::SketchObject* Obj = dynamic_cast(selection[0].getObject()); + + // only one sketch with its subelements are allowed to be selected + if (selection.size() != 1) { + strError = QObject::tr("Selected objects are not just geometry from one sketch.", dmbg); + throw(Base::Exception("")); + } + + // get the needed lists and objects + const std::vector &SubNames = selection[0].getSubNames(); + + if (SubNames.size() != 3) { + strError = QObject::tr("Number of selected objects is not 3 (is %1).", dmbg).arg(SubNames.size()); + throw(Base::Exception("")); + } + + int GeoId1, GeoId2, GeoId3; + Sketcher::PointPos PosId1, PosId2, PosId3; + getIdsFromName(SubNames[0], Obj, GeoId1, PosId1); + getIdsFromName(SubNames[1], Obj, GeoId2, PosId2); + getIdsFromName(SubNames[2], Obj, GeoId3, PosId3); + + //sink the egde to be the last item + if (isEdge(GeoId1,PosId1) ) { + std::swap(GeoId1,GeoId2); + std::swap(PosId1,PosId2); + } + if (isEdge(GeoId2,PosId2) ) { + std::swap(GeoId2,GeoId3); + std::swap(PosId2,PosId3); + } + + //a bunch of validity checks + if ((GeoId1 < 0 && GeoId2 < 0 && GeoId3 < 0)) { + strError = QObject::tr("Cannot add a constraint between external geometries!", dmbg); + throw(Base::Exception("")); + } + + if (!(isVertex(GeoId1,PosId1) && !isSimpleVertex(Obj, GeoId1, PosId1) && + isVertex(GeoId2,PosId2) && !isSimpleVertex(Obj, GeoId2, PosId2) && + isEdge(GeoId3,PosId3) )) { + strError = QObject::tr("Incompatible geometry is selected!", dmbg); + throw(Base::Exception("")); + }; + + //the essence. + //Unlike other constraints, we'll ask for a value immediately. + QDialog dlg(Gui::getMainWindow()); + Ui::InsertDatum ui_Datum; + ui_Datum.setupUi(&dlg); + dlg.setWindowTitle(EditDatumDialog::tr("Refractive index ratio", dmbg)); + ui_Datum.label->setText(EditDatumDialog::tr("Ratio n2/n1:", dmbg)); + Base::Quantity init_val; + init_val.setUnit(Base::Unit()); + init_val.setValue(0.0); + + ui_Datum.labelEdit->setValue(init_val); + ui_Datum.labelEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/SketcherRefrIndexRatio")); + ui_Datum.labelEdit->setToLastUsedValue(); + ui_Datum.labelEdit->selectNumber(); + + if (dlg.exec() != QDialog::Accepted) return; + ui_Datum.labelEdit->pushToHistory(); + + Base::Quantity newQuant = ui_Datum.labelEdit->value(); + double n2divn1 = newQuant.getValue(); + + //add constraint + openCommand("add Snell's law constraint"); + + if (! IsPointAlreadyOnCurve(GeoId2,GeoId1,PosId1,Obj)) + Gui::Command::doCommand( + Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ", + selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2); + + if (! IsPointAlreadyOnCurve(GeoId3,GeoId1,PosId1,Obj)) + Gui::Command::doCommand( + Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ", + selection[0].getFeatName(),GeoId1,PosId1,GeoId3); + + Gui::Command::doCommand( + Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('SnellsLaw',%d,%d,%d,%d,%d,%.12f)) ", + selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2,GeoId3,n2divn1); + + commitCommand(); + updateActive(); + + // clear the selection (convenience) + getSelection().clearSelection(); + } catch (Base::Exception &e) { + if (strError.isEmpty()) strError = QString::fromLatin1(e.what()); + if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n")); + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Error"), strError + strHelp); + } +} + +bool CmdSketcherConstrainSnellsLaw::isActive(void) +{ + return isCreateConstraintActive( getActiveGuiDocument() ); +} + + DEF_STD_CMD_A(CmdSketcherConstrainInternalAlignment); CmdSketcherConstrainInternalAlignment::CmdSketcherConstrainInternalAlignment() @@ -2902,5 +3034,7 @@ void CreateSketcherCommandsConstraints(void) rcCmdMgr.addCommand(new CmdSketcherConstrainEqual()); rcCmdMgr.addCommand(new CmdSketcherConstrainPointOnObject()); rcCmdMgr.addCommand(new CmdSketcherConstrainSymmetric()); + rcCmdMgr.addCommand(new CmdSketcherConstrainSnellsLaw()); rcCmdMgr.addCommand(new CmdSketcherConstrainInternalAlignment()); + } diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp index ea5f741d6..45f8cf28f 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp @@ -75,7 +75,8 @@ void EditDatumDialog::exec(bool atCursor) Constr->Type == Sketcher::DistanceX || Constr->Type == Sketcher::DistanceY || Constr->Type == Sketcher::Radius || - Constr->Type == Sketcher::Angle) { + Constr->Type == Sketcher::Angle || + Constr->Type == Sketcher::SnellsLaw) { if (sketch->hasConflicts()) { QMessageBox::critical(qApp->activeWindow(), QObject::tr("Distance constraint"), @@ -106,6 +107,11 @@ void EditDatumDialog::exec(bool atCursor) ui_ins_datum.label->setText(tr("Radius:")); ui_ins_datum.labelEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/SketcherLength")); } + else if (Constr->Type == Sketcher::SnellsLaw) { + dlg.setWindowTitle(tr("Refractive index ratio", "Constraint_SnellsLaw")); + ui_ins_datum.label->setText(tr("Ratio n2/n1:", "Constraint_SnellsLaw")); + ui_ins_datum.labelEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/SketcherRefrIndexRatio")); + } else { dlg.setWindowTitle(tr("Insert length")); init_val.setUnit(Base::Unit::Length); @@ -134,7 +140,7 @@ void EditDatumDialog::exec(bool atCursor) if (dlg.exec()) { Base::Quantity newQuant = ui_ins_datum.labelEdit->value(); - if (newQuant.isQuantity()) { + if (newQuant.isQuantity() || (Constr->Type == Sketcher::SnellsLaw && newQuant.isDimensionless())) { // save the value for the history ui_ins_datum.labelEdit->pushToHistory(); diff --git a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc index 27cb1e05f..416828ee3 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -123,6 +123,7 @@ icons/small/Constraint_PointOnStart_sm.xpm icons/small/Constraint_PointToObject_sm.xpm icons/small/Constraint_Radius_sm.xpm + icons/small/Constraint_SnellsLaw_sm.xpm icons/small/Constraint_Symmetric_sm.xpm icons/small/Constraint_TangentToEnd_sm.xpm icons/small/Constraint_TangentToStart_sm.xpm diff --git a/src/Mod/Sketcher/Gui/Resources/icons/small/Constraint_SnellsLaw_sm.xpm b/src/Mod/Sketcher/Gui/Resources/icons/small/Constraint_SnellsLaw_sm.xpm new file mode 100644 index 000000000..a3945ad81 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/small/Constraint_SnellsLaw_sm.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * Constraint_SnellsLaw_sm_xpm[] = { +"16 16 2 1", +" c None", +". c #EE2100", +".... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +"................", +" ...............", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" "}; diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp index 9c5acab89..aad748f36 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp @@ -297,7 +297,8 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemActivated(QListWidgetI it->Type == Sketcher::DistanceX || it->Type == Sketcher::DistanceY || it->Type == Sketcher::Radius || - it->Type == Sketcher::Angle) { + it->Type == Sketcher::Angle || + it->Type == Sketcher::SnellsLaw) { EditDatumDialog *editDatumDialog = new EditDatumDialog(this->sketchView, it->ConstraintNbr); editDatumDialog->exec(false); @@ -328,6 +329,18 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemChanged(QListWidgetIte case Sketcher::Angle: unitStr = Base::Quantity(Base::toDegrees(std::abs(v->Value)),Base::Unit::Angle).getUserString(); break; + case Sketcher::SnellsLaw: + { + double n1 = 1.0; + double n2 = 1.0; + if(abs(v->Value)>=1) { + n2 = v->Value; + } else { + n1 = 1/v->Value; + } + unitStr = QString::fromLatin1("%1/%2").arg(n2).arg(n1); + } + break; default: break; } @@ -362,6 +375,7 @@ void TaskSketcherConstrains::slotConstraintsChanged(void) QIcon equal( Gui::BitmapFactory().pixmap("Constraint_EqualLength") ); QIcon pntoo( Gui::BitmapFactory().pixmap("Constraint_PointOnObject") ); QIcon symm ( Gui::BitmapFactory().pixmap("Constraint_Symmetric") ); + QIcon snell ( Gui::BitmapFactory().pixmap("Constraint_SnellsLaw") ); QIcon iaellipseminoraxis ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_MinorAxis") ); QIcon iaellipsemajoraxis ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_MajorAxis") ); QIcon iaellipsefocus1 ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_Focus1") ); @@ -467,6 +481,23 @@ void TaskSketcherConstrains::slotConstraintsChanged(void) ui->listWidgetConstraints->addItem(item); } break; + case Sketcher::SnellsLaw: + if (Filter<3 || !(*it)->Name.empty()) { + ConstraintItem* item = new ConstraintItem(snell,name,i-1,(*it)->Type); + + double v = (*it)->Value; + double n1 = 1.0; + double n2 = 1.0; + if(abs(v)>=1) { + n2 = v; + } else { + n1 = 1/v; + } + name = QString::fromLatin1("%1 (%2/%3)").arg(name).arg(n2).arg(n1); + item->setData(Qt::UserRole, name); + ui->listWidgetConstraints->addItem(item); + } + break; case Sketcher::InternalAlignment: if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) switch((*it)->AlignmentType){ diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 82fc5d846..c157b61b3 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -920,7 +920,8 @@ void ViewProviderSketch::editDoubleClicked(void) Constr->Type == Sketcher::DistanceX || Constr->Type == Sketcher::DistanceY || Constr->Type == Sketcher::Radius || - Constr->Type == Sketcher::Angle) { + Constr->Type == Sketcher::Angle || + Constr->Type == Sketcher::SnellsLaw ) { // Coin's SoIdleSensor causes problems on some platform while Qt seems to work properly (#0001517) EditDatumDialog *editDatumDialog = new EditDatumDialog(this, *it); @@ -2323,7 +2324,9 @@ QString ViewProviderSketch::iconTypeFromConstraint(Constraint *constraint) case Equal: return QString::fromAscii("small/Constraint_EqualLength_sm"); case Symmetric: - return QString::fromAscii("small/Constraint_Symmetric_sm"); + return QString::fromAscii("small/Constraint_Symmetric_sm"); + case SnellsLaw: + return QString::fromAscii("small/Constraint_SnellsLaw_sm"); default: return QString(); } @@ -3448,12 +3451,14 @@ Restart: break; case PointOnObject: case Tangent: + case SnellsLaw: { assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); Base::Vector3d pos, relPos; if ( Constr->Type == PointOnObject || + Constr->Type == SnellsLaw || (Constr->Type == Tangent && Constr->Third != Constraint::GeoUndef) || //Tangency via point (Constr->Type == Tangent && Constr->FirstPos != Sketcher::none) //endpoint-to-curve or endpoint-to-endpoint tangency ) { @@ -3897,6 +3902,7 @@ void ViewProviderSketch::rebuildConstraintsVisual(void) break; case PointOnObject: case Tangent: + case SnellsLaw: { // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 sep->addChild(mat); @@ -3950,7 +3956,7 @@ void ViewProviderSketch::rebuildConstraintsVisual(void) } break; default: - edit->vConstrType.push_back(None); + edit->vConstrType.push_back((*it)->Type); } edit->constrGroup->addChild(sep); diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index 966c621a1..baec88933 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -202,6 +202,7 @@ inline void SketcherAddWorkbenchConstraints(Gui::MenuItem& cons){ << "Sketcher_ConstrainDistance" << "Sketcher_ConstrainRadius" << "Sketcher_ConstrainAngle" + << "Sketcher_ConstrainSnellsLaw" << "Sketcher_ConstrainInternalAlignment"; } @@ -222,7 +223,8 @@ inline void SketcherAddWorkbenchConstraints(Gui::ToolBarItem& << "Sketcher_ConstrainDistanceY" << "Sketcher_ConstrainDistance" << "Sketcher_ConstrainRadius" - << "Sketcher_ConstrainAngle"; + << "Sketcher_ConstrainAngle" + << "Sketcher_ConstrainSnellsLaw"; } template