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:
parent
68ec9e44f8
commit
43e8b30846
|
@ -46,7 +46,8 @@ enum ConstraintType {
|
|||
Equal = 12,
|
||||
PointOnObject = 13,
|
||||
Symmetric = 14,
|
||||
InternalAlignment = 15
|
||||
InternalAlignment = 15,
|
||||
SnellsLaw = 16
|
||||
};
|
||||
|
||||
enum InternalAlignmentType {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* XPM */
|
||||
static char * Constraint_SnellsLaw_sm_xpm[] = {
|
||||
"16 16 2 1",
|
||||
" c None",
|
||||
". c #EE2100",
|
||||
".... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
"................",
|
||||
" ...............",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" .... ",
|
||||
" "};
|
|
@ -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){
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user