OpticConstraints: Adding Snell's law

Fix AngleViaPoint to support new derivative calculation technique.

OpticConstraints: Adding Snell's law. Fix AngleViaPoint to support new derivative calculation technique.

Snell's law constraint added to GCS, but not yet exposed and cannot be
tested.
Since the way CalculateNormal() returns derivatives had changed,
AngleViaPoint constraint needed modifications. Nothing serious.

OpticConstraints: SnellsLaw progress

Addable through python. Fix math. Some quick-and-dirty visual stuff to
get rid of hangs and to see the constraint in action.

OpticConstraints: SnellsLaw: flipping logic fix

OpticConstraints: SnellsLaw progress

Added toolbar button. Allowed editing a datum by doubleclick. New error
message approach during constraint creation.

OpticConstraints: SnellsLaw

OpticConstraints: SnellsLaw: list label improvement

OpticConstraints: SnellsLaw: fix after rebase

OpticConstraints: SnellsLaw: expose helper constraints

Snell's law internally is made of three constraints: point-on-object,
coincident and the Snell's sin/sin. They were all buried under one UI
constraint. Exposing them allows to construct reflection and
birefringence on the point (attempting to do so used to result in
redundant constraints and was often not functional at all).
This commit breaks compatibility with older files.

OpticConstraints: SnellsLaw: small refactor of math

Placing the duplicated code of error and gradient calculation into a
private method.

OpticConstraints: SnellsLaw: fix datum edit unit

OpticConstraints: SnellsLaw: fix datum edit bug

After previous fix, the dimensionless value was not accepted (the
constraint's value did not change, the changes were ignored).
This commit is contained in:
DeepSOIC 2014-12-11 23:30:58 +03:00 committed by wmayer
parent 68ec9e44f8
commit 43e8b30846
16 changed files with 510 additions and 57 deletions

View File

@ -46,7 +46,8 @@ enum ConstraintType {
Equal = 12,
PointOnObject = 13,
Symmetric = 14,
InternalAlignment = 15
InternalAlignment = 15,
SnellsLaw = 16
};
enum InternalAlignmentType {

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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 ; i<pvec.size() ; i++ ){
if ( param == pvec[i] ) break;
};
if ( i == pvec.size() ) return 0.0;
double deriv;
errorgrad(0, &deriv, param);
//use numeric for testing

View File

@ -52,7 +52,8 @@ namespace GCS
InternalAlignmentPoint2Ellipse = 15,
EqualMajorAxesEllipse = 16,
EllipticalArcRangeToEndPoints = 17,
AngleViaPoint = 18
AngleViaPoint = 18,
Snell = 19
};
enum InternalAlignmentType {
@ -456,6 +457,36 @@ namespace GCS
virtual double grad(double *);
};
class ConstraintSnell : public Constraint //snell's law angles constrainer. Point needs to lie on all three curves to be constraied.
{
private:
inline double* n1() { return pvec[0]; };
inline double* n2() { return pvec[1]; };
Curve* ray1;
Curve* ray2;
Curve* boundary;
//These pointers hold copies of the curves that were passed on
// constraint creation. The curves must be deleted upon destruction of
// the constraint. It is necessary to have copies, since messing with
// original objects that were passed is a very bad idea (but messing is
// necessary, because we need to support redirectParams()/revertParams
// functions.
//The pointers in the curves need to be reconstructed if pvec was redirected
// (test pvecChangedFlag variable before use!)
Point poa;//poa=point of refraction //needs to be reconstructed if pvec was redirected/reverted. The point is easily shallow-copied by C++, so no pointer type here and no delete is necessary.
bool flipn1, flipn2;
void ReconstructGeomPointers(); //writes pointers in pvec to the parameters of crv1, crv2 and poa
void errorgrad(double* err, double* grad, double *param); //error and gradient combined. Values are returned through pointers.
public:
//n1dn2 = n1 divided by n2. from n1 to n2. flipn1 = true instructs to flip ray1's tangent
ConstraintSnell(Curve &ray1, Curve &ray2, Curve &boundary, Point p, double* n1, double* n2, bool flipn1, bool flipn2);
~ConstraintSnell();
virtual ConstraintType getTypeId();
virtual void rescale(double coef=1.);
virtual double error();
virtual double grad(double *);
};
} //namespace GCS

View File

@ -754,6 +754,17 @@ int System::addConstraintP2PSymmetric(Point &p1, Point &p2, Point &p, int tagId)
return addConstraintPointOnLine(p, p1, p2, tagId);
}
int System::addConstraintSnellsLaw(Curve &ray1, Curve &ray2,
Curve &boundary, Point p,
double *n1, double *n2,
bool flipn1, bool flipn2,
int tagId)
{
Constraint *constr = new ConstraintSnell(ray1,ray2,boundary,p,n1,n2,flipn1,flipn2);
constr->setTag(tagId);
return addConstraint(constr);
}
int System::addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, InternalAlignmentType alignmentType, int tagId)
{
Constraint *constr = new ConstraintInternalAlignmentPoint2Ellipse(e, p1, alignmentType);

View File

@ -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);

View File

@ -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<Gui::SelectionObject> selection = getSelection().getSelectionEx();
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(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<std::string> &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());
}

View File

@ -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();

View File

@ -123,6 +123,7 @@
<file>icons/small/Constraint_PointOnStart_sm.xpm</file>
<file>icons/small/Constraint_PointToObject_sm.xpm</file>
<file>icons/small/Constraint_Radius_sm.xpm</file>
<file>icons/small/Constraint_SnellsLaw_sm.xpm</file>
<file>icons/small/Constraint_Symmetric_sm.xpm</file>
<file>icons/small/Constraint_TangentToEnd_sm.xpm</file>
<file>icons/small/Constraint_TangentToStart_sm.xpm</file>

View File

@ -0,0 +1,21 @@
/* XPM */
static char * Constraint_SnellsLaw_sm_xpm[] = {
"16 16 2 1",
" c None",
". c #EE2100",
".... ",
" .... ",
" .... ",
" .... ",
" .... ",
" .... ",
" .... ",
"................",
" ...............",
" .... ",
" .... ",
" .... ",
" .... ",
" .... ",
" .... ",
" "};

View File

@ -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<double>(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){

View File

@ -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);

View File

@ -202,6 +202,7 @@ inline void SketcherAddWorkbenchConstraints<Gui::MenuItem>(Gui::MenuItem& cons){
<< "Sketcher_ConstrainDistance"
<< "Sketcher_ConstrainRadius"
<< "Sketcher_ConstrainAngle"
<< "Sketcher_ConstrainSnellsLaw"
<< "Sketcher_ConstrainInternalAlignment";
}
@ -222,7 +223,8 @@ inline void SketcherAddWorkbenchConstraints<Gui::ToolBarItem>(Gui::ToolBarItem&
<< "Sketcher_ConstrainDistanceY"
<< "Sketcher_ConstrainDistance"
<< "Sketcher_ConstrainRadius"
<< "Sketcher_ConstrainAngle";
<< "Sketcher_ConstrainAngle"
<< "Sketcher_ConstrainSnellsLaw";
}
template <typename T>