Sketcher: Add circle to circle distance constraint

This commit is contained in:
flachyjoe
2023-03-13 21:39:32 +01:00
committed by abdullahtahiriyo
parent cc49ba60ee
commit e2a13763e9
13 changed files with 350 additions and 20 deletions

View File

@@ -224,14 +224,6 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
if (PyNumber_Check(index_or_value)) { // can be float or int
SecondIndex = any_index;
Value = PyFloat_AsDouble(index_or_value);
//if (strcmp("Distance",ConstraintType) == 0) {
// this->getConstraintPtr()->Type = Distance;
// this->getConstraintPtr()->First = FirstIndex;
// this->getConstraintPtr()->Second = SecondIndex;
// this->getConstraintPtr()->Value = Value;
// return 0;
//}
//else
if (strcmp("Angle",ConstraintType) == 0) {
if (PyObject_TypeCheck(index_or_value, &(Base::QuantityPy::Type))) {
Base::Quantity q = *(static_cast<Base::QuantityPy*>(index_or_value)->getQuantityPtr());
@@ -244,6 +236,13 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
this->getConstraintPtr()->setValue(Value);
return 0;
}
else if (strcmp("Distance",ConstraintType) == 0) {
this->getConstraintPtr()->Type = Distance;
this->getConstraintPtr()->First = FirstIndex;
this->getConstraintPtr()->Second = SecondIndex;
this->getConstraintPtr()->setValue(Value);
return 0;
}
else if (strcmp("DistanceX",ConstraintType) == 0) {
FirstPos = SecondIndex;
SecondIndex = -1;
@@ -481,7 +480,7 @@ std::string ConstraintPy::representation() const
case Coincident : result << "'Coincident'>";break;
case Horizontal : result << "'Horizontal' (" << getConstraintPtr()->First << ")>";break;
case Vertical : result << "'Vertical' (" << getConstraintPtr()->First << ")>";break;
case Block : result << "'Block' (" << getConstraintPtr()->First << ")>";break;
case Block : result << "'Block' (" << getConstraintPtr()->First << ")>";break;
case Radius : result << "'Radius'>";break;
case Diameter : result << "'Diameter'>";break;
case Weight : result << "'Weight'>";break;

View File

@@ -296,6 +296,10 @@ std::string PythonConverter::process(const Sketcher::Constraint * constraint)
return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %f)") %
constr->First % constr->getValue());
}
else if(constr->FirstPos == Sketcher::PointPos::none){
return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %i, %f)") %
constr->First % constr->Second % constr->getValue());
}
else if(constr->SecondPos == Sketcher::PointPos::none){
return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %i, %i, %f)") %
constr->First % static_cast<int>(constr->FirstPos) % constr->Second % constr->getValue());

View File

@@ -1797,6 +1797,20 @@ int Sketch::addConstraint(const Constraint *constraint)
constraint->Second,constraint->SecondPos,
c.value,c.driving);
}
else if (constraint->FirstPos == PointPos::none &&
constraint->SecondPos == PointPos::none &&
constraint->Second != GeoEnum::GeoUndef &&
constraint->Third == GeoEnum::GeoUndef) { // circle to circle, circle to arc, etc.
c.value = new double(constraint->getValue());
if(c.driving)
FixParameters.push_back(c.value);
else {
Parameters.push_back(c.value);
DrivenParameters.push_back(c.value);
}
rtn = addDistanceConstraint(constraint->First, constraint->Second,c.value,c.driving);
}
else if (constraint->Second != GeoEnum::GeoUndef) {
if (constraint->FirstPos != PointPos::none) { // point to line distance
c.value = new double(constraint->getValue());
@@ -1806,8 +1820,7 @@ int Sketch::addConstraint(const Constraint *constraint)
Parameters.push_back(c.value);
DrivenParameters.push_back(c.value);
}
rtn = addDistanceConstraint(constraint->First,constraint->FirstPos,
constraint->Second,c.value,c.driving);
rtn = addDistanceConstraint(constraint->First,constraint->FirstPos,constraint->Second,c.value,c.driving);
}
}
else {// line length
@@ -2740,6 +2753,19 @@ int Sketch::addDistanceConstraint(int geoId1, PointPos pos1, int geoId2, PointPo
return -1;
}
// circle-circle offset distance constraint
int Sketch::addDistanceConstraint(int geoId1, int geoId2, double * value, bool driving)
{
if ((Geoms[geoId1].type == Circle) && (Geoms[geoId2].type == Circle)) {
GCS::Circle &c1 = Circles[Geoms[geoId1].index];
GCS::Circle &c2 = Circles[Geoms[geoId2].index];
int tag = ++ConstraintsCounter;
GCSsys.addConstraintC2CDistance(c1, c2, value, tag, driving);
return ConstraintsCounter;
}
return -1;
}
int Sketch::addRadiusConstraint(int geoId, double * value, bool driving)
{
geoId = checkGeoId(geoId);

View File

@@ -276,6 +276,15 @@ public:
* Parameters array, as the case may be.
*/
int addDistanceConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2, double * value, bool driving = true);
/**
* add a length or distance constraint
*
* double * value is a pointer to double allocated in the heap, containing the
* constraint value and already inserted into either the FixParameters or
* Parameters array, as the case may be.
*/
int addDistanceConstraint(int geoId1, int geoId2, double * value, bool driving = true);
/// add a parallel constraint between two lines
int addParallelConstraint(int geoId1, int geoId2);
/// add a perpendicular constraint between two lines

View File

@@ -2585,5 +2585,109 @@ double ConstraintEqualLineLength::grad(double *param)
return deriv*scale;
}
// ConstraintC2CDistance
ConstraintC2CDistance::ConstraintC2CDistance(Circle &c1, Circle &c2, double *d)
{
this->d = d;
pvec.push_back(d);
this->c1 = c1;
this->c1.PushOwnParams(pvec);
this->c2 = c2;
this->c2.PushOwnParams(pvec);
origpvec = pvec;
pvecChangedFlag = true;
rescale();
}
void ConstraintC2CDistance::ReconstructGeomPointers()
{
int i=0;
i++; // skip the first parameter as there is the inline function distance for it
c1.ReconstructOnNewPvec(pvec, i);
c2.ReconstructOnNewPvec(pvec, i);
pvecChangedFlag = false;
}
ConstraintType ConstraintC2CDistance::getTypeId()
{
return C2CDistance;
}
void ConstraintC2CDistance::rescale(double coef)
{
scale = coef * 1;
}
void ConstraintC2CDistance::errorgrad(double *err, double *grad, double *param)
{
if (pvecChangedFlag) ReconstructGeomPointers();
DeriVector2 ct1 (c1.center, param);
DeriVector2 ct2 (c2.center, param);
DeriVector2 vector_ct12 = ct1.subtr(ct2);
double length_ct12, dlength_ct12;
length_ct12 = vector_ct12.length(dlength_ct12);
// outer case (defined as the centers of the circles are outside the center of the other circles)
// it may well be that the circles intersect.
if( length_ct12 >= *c1.rad &&
length_ct12 >= *c2.rad ) {
if (err) {
*err = length_ct12 - (*c2.rad + *c1.rad + *distance());
}
else if (grad) {
double drad = (param == c2.rad || param == c1.rad || param == distance())?-1.0:0.0;
*grad = dlength_ct12 + drad;
}
}
else {
double * bigradius = (*c1.rad >= *c2.rad)?c1.rad:c2.rad;
double * smallradius = (*c1.rad >= *c2.rad)?c2.rad:c1.rad;
double smallspan = *smallradius + length_ct12 + *distance();
if (err) {
*err = *bigradius - smallspan;
}
else if (grad) {
double drad = 0.0;
if(param == bigradius) {
drad = 1.0;
}
else if(param == smallradius) {
drad = -1.0;
}
else if(param == distance()) {
drad = (*distance()<0.)?1.0:-1.0;
}
*grad = -dlength_ct12 + drad;
}
}
}
double ConstraintC2CDistance::error()
{
double err;
errorgrad(&err,nullptr,nullptr);
return scale * err;
}
double ConstraintC2CDistance::grad(double *param)
{
if ( findParamInPvec(param) == -1 )
return 0.0;
double deriv;
errorgrad(nullptr, &deriv, param);
return deriv*scale;
}
} //namespace GCS

View File

@@ -72,7 +72,8 @@ namespace GCS
CenterOfGravity = 26,
WeightedLinearCombination = 27,
SlopeAtBSplineKnot = 28,
PointOnBSpline = 29
PointOnBSpline = 29,
C2CDistance = 30
};
enum InternalAlignmentType {
@@ -745,6 +746,22 @@ namespace GCS
double grad(double *) override;
};
class ConstraintC2CDistance : public Constraint
{
private:
Circle c1;
Circle c2;
double *d;
inline double* distance() { return pvec[0]; }
void ReconstructGeomPointers(); //writes pointers in pvec to the parameters of c1, c2
void errorgrad(double* err, double* grad, double *param); //error and gradient combined. Values are returned through pointers.
public:
ConstraintC2CDistance(Circle &c1, Circle &c2, double *d);
ConstraintType getTypeId() override;
void rescale(double coef=1.) override;
double error() override;
double grad(double *) override;
};
} //namespace GCS

View File

@@ -817,6 +817,14 @@ int System::addConstraintTangentAtBSplineKnot(BSpline &b, Line &l, unsigned int
return addConstraint(constr);
}
int System::addConstraintC2CDistance(Circle &c1, Circle &c2, double *dist, int tagId, bool driving)
{
Constraint *constr = new ConstraintC2CDistance(c1, c2, dist);
constr->setTag(tagId);
constr->setDriving(driving);
return addConstraint(constr);
}
// derived constraints
int System::addConstraintP2PCoincident(Point &p1, Point &p2, int tagId, bool driving)

View File

@@ -315,6 +315,8 @@ namespace GCS
bool flipn1, bool flipn2,
int tagId, bool driving = true);
int addConstraintC2CDistance(Circle &c1, Circle &c2, double *dist, int tagId, bool driving = true);
// internal alignment constraints
int addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, InternalAlignmentType alignmentType, int tagId=0, bool driving = true);
int addConstraintInternalAlignmentEllipseMajorDiameter(Ellipse &e, Point &p1, Point &p2, int tagId=0, bool driving = true);

View File

@@ -2150,7 +2150,7 @@ CmdSketcherConstrainDistance::CmdSketcherConstrainDistance()
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Constrain distance");
sToolTipText = QT_TR_NOOP("Fix a length of a line or the distance between a line and a vertex");
sToolTipText = QT_TR_NOOP("Fix a length of a line or the distance between a line and a vertex or between two circles");
sWhatsThis = "Sketcher_ConstrainDistance";
sStatusTip = sToolTipText;
sPixmap = "Constraint_Length";
@@ -2160,7 +2160,8 @@ CmdSketcherConstrainDistance::CmdSketcherConstrainDistance()
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex},
{SelEdge}, {SelExternalEdge},
{SelVertex, SelEdgeOrAxis}, {SelRoot, SelEdge},
{SelVertex, SelExternalEdge}, {SelRoot, SelExternalEdge}};
{SelVertex, SelExternalEdge}, {SelRoot, SelExternalEdge},
{SelEdge, SelEdge}};
}
void CmdSketcherConstrainDistance::activated(int iMsg)
@@ -2289,6 +2290,55 @@ void CmdSketcherConstrainDistance::activated(int iMsg)
return;
}
}
else if (isEdge(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) { // circle to circle distance
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
if (geom1->getTypeId() == Part::GeomCircle::getClassTypeId()
&& geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ) {
auto circleSeg1 = static_cast<const Part::GeomCircle*>(geom1);
double radius1 = circleSeg1->getRadius();
Base::Vector3d center1 = circleSeg1->getCenter();
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geom2);
double radius2 = circleSeg2->getRadius();
Base::Vector3d center2 = circleSeg2->getCenter();
double ActDist = 0.;
Base::Vector3d intercenter = center1 - center2;
double intercenterdistance = intercenter.Length();
if( intercenterdistance >= radius1 &&
intercenterdistance >= radius2 ) {
ActDist = intercenterdistance - radius1 - radius2;
}
else {
double bigradius = std::max(radius1,radius2);
double smallradius = std::min(radius1,radius2);
ActDist = bigradius - smallradius - intercenterdistance;
}
openCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle distance constraint"));
Gui::cmdAppObjectArgs(selection[0].getObject(),
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f)) ",
GeoId1,GeoId2,ActDist);
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
Gui::cmdAppObjectArgs(selection[0].getObject(),
"setDriving(%i,%s)",
ConStr.size()-1,"False");
finishDatumConstraint (this, Obj, false);
}
else
finishDatumConstraint (this, Obj, true);
return;
}
}
else if (isEdge(GeoId1,PosId1)) { // line length
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
Gui::TranslatedNotification(Obj,
@@ -2327,7 +2377,7 @@ void CmdSketcherConstrainDistance::activated(int iMsg)
Gui::TranslatedNotification(Obj,
QObject::tr("Wrong selection"),
QObject::tr("Select exactly one line or one point and one line or two points from the sketch."));
QObject::tr("Select exactly one line or one point and one line or two points or two circles from the sketch."));
return;
}
@@ -2416,6 +2466,9 @@ void CmdSketcherConstrainDistance::applyConstraint(std::vector<SelIdPair> &selSe
else
finishDatumConstraint (this, Obj, true);
}
else if (geom->getTypeId() == Part::GeomCircle::getClassTypeId()) {
// allow this selection but do nothing as it needs 2 circles
}
else {
Gui::TranslatedNotification(Obj,
QObject::tr("Wrong selection"),
@@ -2460,6 +2513,61 @@ void CmdSketcherConstrainDistance::applyConstraint(std::vector<SelIdPair> &selSe
return;
}
case 8: // {SelEdge, SelEdge}
{
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
if (geom1->getTypeId() == Part::GeomCircle::getClassTypeId()
&& geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ) { // circle to circle distance
auto circleSeg1 = static_cast<const Part::GeomCircle*>(geom1);
double radius1 = circleSeg1->getRadius();
Base::Vector3d center1 = circleSeg1->getCenter();
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geom2);
double radius2 = circleSeg2->getRadius();
Base::Vector3d center2 = circleSeg2->getCenter();
double ActDist = 0.;
Base::Vector3d intercenter = center1 - center2;
double intercenterdistance = intercenter.Length();
if( intercenterdistance >= radius1 &&
intercenterdistance >= radius2 ) {
ActDist = intercenterdistance - radius1 - radius2;
}
else {
double bigradius = std::max(radius1,radius2);
double smallradius = std::min(radius1,radius2);
ActDist = bigradius - smallradius - intercenterdistance;
}
openCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle distance constraint"));
Gui::cmdAppObjectArgs(Obj,
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f)) ",
GeoId1,GeoId2,ActDist);
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)",
ConStr.size()-1,"False");
finishDatumConstraint (this, Obj, false);
}
else
finishDatumConstraint (this, Obj, true);
return;
} else {
Gui::TranslatedNotification(Obj,
QObject::tr("Wrong selection"),
QObject::tr("Select exactly one line or one point and one line or two points or two circles from the sketch."));
}
}
default:
break;
}

View File

@@ -62,6 +62,7 @@
#include "SoZoomTranslation.h"
#include "ViewProviderSketch.h"
#include "ViewProviderSketchCoinAttorney.h"
#include "Utils.h"
using namespace SketcherGui;
@@ -654,24 +655,33 @@ Restart:
if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
pnt2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos);
} else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance
} else if (Constr->Second != GeoEnum::GeoUndef) {
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->Second);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // point to line distance
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
Base::Vector3d l2p1 = lineSeg->getStartPoint();
Base::Vector3d l2p2 = lineSeg->getEndPoint();
// calculate the projection of p1 onto line2
pnt2.ProjectToLine(pnt1-l2p1, l2p2-l2p1);
pnt2 += pnt1;
} else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { // circle to circle distance
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circleSeg1 = static_cast<const Part::GeomCircle*>(geo1);
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geo);
GetCirclesMinimalDistance(circleSeg1, circleSeg2, pnt1, pnt2);
}
} else
break;
} else if (Constr->FirstPos != Sketcher::PointPos::none) {
pnt2 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
} else if (Constr->First != GeoEnum::GeoUndef) {
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // segment distance
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
pnt1 = lineSeg->getStartPoint();
pnt2 = lineSeg->getEndPoint();

View File

@@ -327,6 +327,39 @@ double SketcherGui::GetPointAngle(const Base::Vector2d& p1, const Base::Vector2d
return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2 * M_PI;
}
// Set the two points on circles at minimal distance
// in concentric case set points on relative X axis
void SketcherGui::GetCirclesMinimalDistance(const Part::GeomCircle *circle1, const Part::GeomCircle *circle2, Base::Vector3d &point1, Base::Vector3d &point2)
{
double radius1 = circle1->getRadius();
double radius2 = circle2->getRadius();
point1 = circle1->getCenter();
point2 = circle2->getCenter();
Base::Vector3d v = point2 - point1;
double length = v.Length();
if (length == 0) { //concentric case
point1.x += radius1;
point2.x += radius2;
} else {
v = v.Normalize();
if (length <= std::max(radius1, radius2)){ //inner case
if (radius1 > radius2){
point1 += v * radius1;
point2 += v * radius2;
} else {
point1 += -v * radius1;
point2 += -v * radius2;
}
} else { //outer case
point1 += v * radius1;
point2 += -v * radius2;
}
}
}
void SketcherGui::ActivateHandler(Gui::Document* doc, DrawSketchHandler* handler)
{
std::unique_ptr<DrawSketchHandler> ptr(handler);

View File

@@ -119,6 +119,9 @@ inline bool isEdge(int GeoId, Sketcher::PointPos PosId)
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
double GetPointAngle (const Base::Vector2d &p1, const Base::Vector2d &p2);
// Set the two points on circles at minimal distance
void GetCirclesMinimalDistance(const Part::GeomCircle *circle1, const Part::GeomCircle *circle2, Base::Vector3d &point1, Base::Vector3d &point2);
void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler);
/// Returns if a sketch is in edit mode

View File

@@ -1416,16 +1416,23 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo
if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance
p1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos);
p2 = getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos);
} else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance
} else if (Constr->Second != GeoEnum::GeoUndef) {
p1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos);
const Part::Geometry *geo = GeoList::getGeometryFromGeoId (geomlist, Constr->Second);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // point to line distance
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
Base::Vector3d l2p1 = lineSeg->getStartPoint();
Base::Vector3d l2p2 = lineSeg->getEndPoint();
// calculate the projection of p1 onto line2
p2.ProjectToLine(p1-l2p1, l2p2-l2p1);
p2 += p1;
} else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { // circle to circle distance
const Part::Geometry *geo1 = GeoList::getGeometryFromGeoId (geomlist, Constr->First);
if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circleSeg1 = static_cast<const Part::GeomCircle *>(geo1);
const Part::GeomCircle *circleSeg2 = static_cast<const Part::GeomCircle *>(geo);
GetCirclesMinimalDistance(circleSeg1, circleSeg2, p1, p2);
}
} else
return;
} else if (Constr->FirstPos != Sketcher::PointPos::none) {