Skip to content

Commit

Permalink
Merge pull request #10657 from Ondsel-Development/dim_angle_supplemen…
Browse files Browse the repository at this point in the history
…tary

Sketcher_Dimension: Enable the user to select what angle he wants.
  • Loading branch information
chennes committed Sep 19, 2023
2 parents fec8888 + 55420d9 commit 2702d3b
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 79 deletions.
61 changes: 61 additions & 0 deletions src/Mod/Sketcher/App/SketchObject.cpp
Expand Up @@ -660,6 +660,67 @@ int SketchObject::moveDatumsToEnd()
return 0;
}


void SketchObject::reverseAngleConstraintToSupplementary(Constraint* constr, int constNum)
{
std::swap(constr->First, constr->Second);
std::swap(constr->FirstPos, constr->SecondPos);
constr->FirstPos = (constr->FirstPos == Sketcher::PointPos::start) ? Sketcher::PointPos::end : Sketcher::PointPos::start;
double actAngle = constr->getValue();
constr->setValue(M_PI - actAngle);

// Edit the expression if any
if (constraintHasExpression(constNum)) {
std::string expression = getConstraintExpression(constNum);
if (expression.substr(0, 7) == "180 - (") {
expression = expression.substr(7, expression.size() - 8);
}
else {
expression = "180 - (" + expression + ")";
}
setConstraintExpression(constNum, expression);
}
}

bool SketchObject::constraintHasExpression(int constNum)
{
App::ObjectIdentifier path = Constraints.createPath(constNum);
auto info = getExpression(path);
if (info.expression) {
return true;
}
return false;
}

std::string SketchObject::getConstraintExpression(int constNum)
{
App::ObjectIdentifier path = Constraints.createPath(constNum);
auto info = getExpression(path);
if (info.expression) {
std::string expression = info.expression->toString();
return expression;
}

return "";
}

void SketchObject::setConstraintExpression(int constNum, std::string& newExpression)
{
App::ObjectIdentifier path = Constraints.createPath(constNum);
auto info = getExpression(path);
if (info.expression) {
try {
std::shared_ptr<App::Expression> expr(App::Expression::parse(this, newExpression));
// there is a bug in the SketchObject API because setExpression() is protected but public in DocumentObject
App::DocumentObject* base = this;
base->setExpression(path, expr);
}
catch (const Base::Exception&) {
Base::Console().Error("Failed to set constraint expression.");
}
}
}

int SketchObject::setVirtualSpace(int ConstrId, bool isinvirtualspace)
{
// no need to check input data validity as this is an sketchobject managed operation.
Expand Down
10 changes: 10 additions & 0 deletions src/Mod/Sketcher/App/SketchObject.h
Expand Up @@ -269,6 +269,16 @@ class SketcherExport SketchObject: public Part::Part2DObject
/// Move Dimensional constraints at the end of the properties array
int moveDatumsToEnd();

/// Change an angle constraint to its supplementary angle.
void reverseAngleConstraintToSupplementary(Constraint* constr, int constNum);

// Check if a constraint has an expression associated.
bool constraintHasExpression(int constNum);
// Get a constraint associated expression
std::string getConstraintExpression(int constNum);
// Set a constraint associated expression
void setConstraintExpression(int constNum, std::string& newExpression);

/// set the driving status of this constraint and solve
int setVirtualSpace(int ConstrId, bool isinvirtualspace);
/// set the driving status of a group of constraints at once
Expand Down
188 changes: 109 additions & 79 deletions src/Mod/Sketcher/Gui/ViewProviderSketch.cpp
Expand Up @@ -1580,13 +1580,13 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d& toPo
if (!isInEditMode())
return;

const std::vector<Sketcher::Constraint*>& constrlist =
getSketchObject()->Constraints.getValues();
Sketcher::SketchObject* obj = getSketchObject();
const std::vector<Sketcher::Constraint*>& constrlist = obj->Constraints.getValues();
Constraint* Constr = constrlist[constNum];

#ifdef FC_DEBUG
int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1;
int extGeoCount = getSketchObject()->getExternalGeometryCount();
int intGeoCount = obj->getHighestCurveIndex() + 1;
int extGeoCount = obj->getExternalGeometryCount();
#endif

// with memory allocation
Expand Down Expand Up @@ -1750,91 +1750,121 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d& toPo
}
}
else if (Constr->Type == Angle) {
moveAngleConstraint(constNum, toPos);
}

Base::Vector3d p0(0., 0., 0.);
double factor = 0.5;
if (Constr->Second != GeoEnum::GeoUndef) {// line to line angle
Base::Vector3d dir1, dir2;

if (Constr->Third == GeoEnum::GeoUndef) {// angle between two lines
const Part::Geometry* geo1 = GeoList::getGeometryFromGeoId(geomlist, Constr->First);
const Part::Geometry* geo2 =
GeoList::getGeometryFromGeoId(geomlist, Constr->Second);

if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId()
|| geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId())
return;
const Part::GeomLineSegment* lineSeg1 =
static_cast<const Part::GeomLineSegment*>(geo1);
const Part::GeomLineSegment* lineSeg2 =
static_cast<const Part::GeomLineSegment*>(geo2);

bool flip1 = (Constr->FirstPos == Sketcher::PointPos::end);
bool flip2 = (Constr->SecondPos == Sketcher::PointPos::end);

dir1 = (flip1 ? -1. : 1.) * (lineSeg1->getEndPoint() - lineSeg1->getStartPoint());
dir2 = (flip2 ? -1. : 1.) * (lineSeg2->getEndPoint() - lineSeg2->getStartPoint());
Base::Vector3d pnt1 = flip1 ? lineSeg1->getEndPoint() : lineSeg1->getStartPoint();
Base::Vector3d pnt2 = flip2 ? lineSeg2->getEndPoint() : lineSeg2->getStartPoint();

// line-line intersection
{
double det = dir1.x * dir2.y - dir1.y * dir2.x;
if ((det > 0 ? det : -det) < 1e-10)
return;// lines are parallel - constraint unmoveable (DeepSOIC: why?..)
double c1 = dir1.y * pnt1.x - dir1.x * pnt1.y;
double c2 = dir2.y * pnt2.x - dir2.x * pnt2.y;
double x = (dir1.x * c2 - dir2.x * c1) / det;
double y = (dir1.y * c2 - dir2.y * c1) / det;
// intersection point
p0 = Base::Vector3d(x, y, 0);

Base::Vector3d vec = Base::Vector3d(toPos.x, toPos.y, 0) - p0;
factor = factor * Base::sgn<double>((dir1 + dir2) * vec);
}
}
else {// angle-via-point
Base::Vector3d p = getSolvedSketch().getPoint(Constr->Third, Constr->ThirdPos);
p0 = Base::Vector3d(p.x, p.y, 0);
dir1 = getSolvedSketch().calculateNormalAtPoint(Constr->First, p.x, p.y);
dir1.RotateZ(-M_PI / 2);// convert to vector of tangency by rotating
dir2 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, p.x, p.y);
dir2.RotateZ(-M_PI / 2);

Base::Vector3d vec = Base::Vector3d(toPos.x, toPos.y, 0) - p0;
factor = factor * Base::sgn<double>((dir1 + dir2) * vec);
}
// delete the cloned objects
for (Part::Geometry* geomPtr : geomlist) {
if (geomPtr) {
delete geomPtr;
}
else if (Constr->First != GeoEnum::GeoUndef) {// line/arc angle
const Part::Geometry* geo = GeoList::getGeometryFromGeoId(geomlist, Constr->First);
}

if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment* lineSeg =
static_cast<const Part::GeomLineSegment*>(geo);
p0 = (lineSeg->getEndPoint() + lineSeg->getStartPoint()) / 2;
}
else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>(geo);
p0 = arc->getCenter();
draw(true, false);
}

void ViewProviderSketch::moveAngleConstraint(int constNum, const Base::Vector2d& toPos)
{
Sketcher::SketchObject* obj = getSketchObject();
const std::vector<Sketcher::Constraint*>& constrlist = obj->Constraints.getValues();
Constraint* constr = constrlist[constNum];

Base::Vector3d p0(0., 0., 0.);
double factor = 0.5;
if (constr->Second != GeoEnum::GeoUndef) {// line to line angle
if (constr->Third == GeoEnum::GeoUndef) {// angle between two lines
const Part::Geometry* geo1 = obj->getGeometry(constr->First);
const Part::Geometry* geo2 = obj->getGeometry(constr->Second);

if (!isLineSegment(*geo1) || !isLineSegment(*geo2)) {
return;
}
else {
const auto* lineSeg1 = static_cast<const Part::GeomLineSegment*>(geo1);
const auto* lineSeg2 = static_cast<const Part::GeomLineSegment*>(geo2);

Base::Vector2d l1[2], l2[2];
l1[0] = Base::Vector2d(lineSeg1->getStartPoint().x, lineSeg1->getStartPoint().y);
l1[1] = Base::Vector2d(lineSeg1->getEndPoint().x, lineSeg1->getEndPoint().y);
l2[0] = Base::Vector2d(lineSeg2->getStartPoint().x, lineSeg2->getStartPoint().y);
l2[1] = Base::Vector2d(lineSeg2->getEndPoint().x, lineSeg2->getEndPoint().y);

// First we will check if the angle needs to be reversed to its supplementary
bool flip1 = (constr->FirstPos == Sketcher::PointPos::end);
bool flip2 = (constr->SecondPos == Sketcher::PointPos::end);
Base::Vector2d p11 = flip1 ? l1[1] : l1[0];
Base::Vector2d p12 = flip1 ? l1[0] : l1[1];
Base::Vector2d p21 = flip2 ? l2[1] : l2[0];
Base::Vector2d p22 = flip2 ? l2[0] : l2[1];

// Get the intersection point in 2d of the two lines if possible
Base::Line2d line1(p11, p12);
Base::Line2d line2(p21, p22);
Base::Vector2d intersection = Base::Vector2d(0., 0.);
if (!line1.Intersect(line2, intersection)) {
return;
}
}
else
return;

Base::Vector3d vec = Base::Vector3d(toPos.x, toPos.y, 0) - p0;
Constr->LabelDistance = factor * vec.Length();
Base::Vector2d dir1 = p12 - p11;
Base::Vector2d dir2 = p22 - p21;

Base::Vector2d ap3 = intersection + dir1 + dir2;

auto isLeftOfLine = [](Base::Vector2d a, Base::Vector2d b, Base::Vector2d c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) > 0;
};

bool sign1 = isLeftOfLine(p11, p12, ap3);
bool sign2 = isLeftOfLine(p21, p22, ap3);

bool sign3 = isLeftOfLine(p11, p12, toPos);
bool sign4 = isLeftOfLine(p21, p22, toPos);

bool reverse = !(sign1 == sign3 && sign2 == sign4) && !(sign1 != sign3 && sign2 != sign4);

if (reverse) {
obj->reverseAngleConstraintToSupplementary(constr, constNum);

ap3 = intersection + dir1 - dir2; //- dir2 instead fo std::swap(dir1, dir2) and dir1 = -dir1
sign1 = isLeftOfLine(p11, p12, ap3);
sign2 = isLeftOfLine(p21, p22, ap3);
}

p0 = Base::Vector3d(intersection.x, intersection.y, 0.);
factor *= (sign1 == sign3 && sign2 == sign4) ? 1. : -1.;
}
else {// angle-via-point
Base::Vector3d p = getSolvedSketch().getPoint(constr->Third, constr->ThirdPos);
p0 = Base::Vector3d(p.x, p.y, 0);
Base::Vector3d dir1 = getSolvedSketch().calculateNormalAtPoint(constr->First, p.x, p.y);
dir1.RotateZ(-M_PI / 2);// convert to vector of tangency by rotating
Base::Vector3d dir2 = getSolvedSketch().calculateNormalAtPoint(constr->Second, p.x, p.y);
dir2.RotateZ(-M_PI / 2);

Base::Vector3d vec = Base::Vector3d(toPos.x, toPos.y, 0) - p0;
factor = factor * Base::sgn<double>((dir1 + dir2) * vec);
}
}
else if (constr->First != GeoEnum::GeoUndef) {// line/arc angle
const Part::Geometry* geo = obj->getGeometry(constr->First);

// delete the cloned objects
for (std::vector<Part::Geometry*>::const_iterator it = geomlist.begin(); it != geomlist.end();
++it)
if (*it)
delete *it;
if (isLineSegment(*geo)) {
const auto* lineSeg = static_cast<const Part::GeomLineSegment*>(geo);
p0 = (lineSeg->getEndPoint() + lineSeg->getStartPoint()) / 2;
}
else if (isArcOfCircle(*geo)) {
const auto* arc = static_cast<const Part::GeomArcOfCircle*>(geo);
p0 = arc->getCenter();
}
else {
return;
}
}
else {
return;
}

draw(true, false);
Base::Vector3d vec = Base::Vector3d(toPos.x, toPos.y, 0) - p0;
constr->LabelDistance = factor * vec.Length();
}

bool ViewProviderSketch::isSelectable() const
Expand Down
1 change: 1 addition & 0 deletions src/Mod/Sketcher/Gui/ViewProviderSketch.h
Expand Up @@ -755,6 +755,7 @@ class SketcherGuiExport ViewProviderSketch: public PartGui::ViewProvider2DObject
//@{
/// moves a selected constraint
void moveConstraint(int constNum, const Base::Vector2d& toPos);
void moveAngleConstraint(int constNum, const Base::Vector2d& toPos);

/// returns whether the sketch is in edit mode.
bool isInEditMode() const;
Expand Down

0 comments on commit 2702d3b

Please sign in to comment.