From 70972b3926f281d3150c05afcd4220ffbcc6ac92 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 3 Dec 2024 18:06:41 +0100 Subject: [PATCH] Sketcher: Group dragging --- src/Mod/Sketcher/App/Sketch.cpp | 665 +++++++++----------- src/Mod/Sketcher/App/Sketch.h | 2 + src/Mod/Sketcher/App/SketchObject.cpp | 20 +- src/Mod/Sketcher/App/SketchObject.h | 31 +- src/Mod/Sketcher/App/SketchObjectPy.xml | 16 + src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 52 ++ src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 604 +++++++++--------- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 43 +- 8 files changed, 740 insertions(+), 693 deletions(-) diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index a12383efe2..06e02aa395 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -4765,307 +4765,260 @@ int Sketch::internalSolve(std::string& solvername, int level) return ret; } -int Sketch::initMove(int geoId, PointPos pos, bool fine) +int Sketch::initMove(std::vector moved, bool fine) { - isFine = fine; - - geoId = checkGeoId(geoId); - - clearTemporaryConstraints(); - - // don't try to move sketches that contain conflicting constraints if (hasConflicts()) { + // don't try to move sketches that contain conflicting constraints isInitMove = false; return -1; } + isFine = fine; - if (Geoms[geoId].type == Point) { - if (pos == PointPos::start) { - GCS::Point& point = Points[Geoms[geoId].startPointId]; - GCS::Point p0; - MoveParameters.resize(2); // px,py - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *point.x; - *p0.y = *point.y; - GCSsys.addConstraintP2PCoincident(p0, point, GCS::DefaultTemporaryConstraint); - } - } - else if (Geoms[geoId].type == Line) { - if (pos == PointPos::start || pos == PointPos::end) { - MoveParameters.resize(2); // x,y - GCS::Point p0; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - if (pos == PointPos::start) { - GCS::Point& p = Points[Geoms[geoId].startPointId]; - *p0.x = *p.x; - *p0.y = *p.y; - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - else if (pos == PointPos::end) { - GCS::Point& p = Points[Geoms[geoId].endPointId]; - *p0.x = *p.x; - *p0.y = *p.y; - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - } - else if (pos == PointPos::none || pos == PointPos::mid) { - MoveParameters.resize(4); // x1,y1,x2,y2 - GCS::Point p1, p2; - p1.x = &MoveParameters[0]; - p1.y = &MoveParameters[1]; - p2.x = &MoveParameters[2]; - p2.y = &MoveParameters[3]; - GCS::Line& l = Lines[Geoms[geoId].index]; - *p1.x = *l.p1.x; - *p1.y = *l.p1.y; - *p2.x = *l.p2.x; - *p2.y = *l.p2.y; - GCSsys.addConstraintP2PCoincident(p1, l.p1, GCS::DefaultTemporaryConstraint); - GCSsys.addConstraintP2PCoincident(p2, l.p2, GCS::DefaultTemporaryConstraint); - } - } - else if (Geoms[geoId].type == Circle) { - GCS::Point& center = Points[Geoms[geoId].midPointId]; - GCS::Point p0, p1; - if (pos == PointPos::mid) { - MoveParameters.resize(2); // cx,cy - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y; - GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); - } - else if (pos == PointPos::none) { - // bool pole = GeometryFacade::isInternalType(Geoms[geoId].geo, - // InternalType::BSplineControlPoint); - MoveParameters.resize(4); // x,y,cx,cy - For poles blocking the center - GCS::Circle& c = Circles[Geoms[geoId].index]; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y + *c.rad; - GCSsys.addConstraintPointOnCircle(p0, c, GCS::DefaultTemporaryConstraint); - p1.x = &MoveParameters[2]; - p1.y = &MoveParameters[3]; - *p1.x = *center.x; - *p1.y = *center.y; - int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); - GCSsys.rescaleConstraint(i - 1, 0.01); - GCSsys.rescaleConstraint(i, 0.01); - } - } - else if (Geoms[geoId].type == Ellipse) { + clearTemporaryConstraints(); - GCS::Point& center = Points[Geoms[geoId].midPointId]; - GCS::Point p0, p1; - if (pos == PointPos::mid || pos == PointPos::none) { - MoveParameters.resize(2); // cx,cy - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y; + MoveParameters.clear(); - GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); - } - } - else if (Geoms[geoId].type == ArcOfEllipse) { - - GCS::Point& center = Points[Geoms[geoId].midPointId]; - GCS::Point p0, p1; - if (pos == PointPos::mid || pos == PointPos::none) { - MoveParameters.resize(2); // cx,cy - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y; - GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); - } - else if (pos == PointPos::start || pos == PointPos::end) { - - MoveParameters.resize(4); // x,y,cx,cy - if (pos == PointPos::start || pos == PointPos::end) { - GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] - : Points[Geoms[geoId].endPointId]; - ; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *p.x; - *p0.y = *p.y; - - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - - p1.x = &MoveParameters[2]; - p1.y = &MoveParameters[3]; - *p1.x = *center.x; - *p1.y = *center.y; - - int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); - GCSsys.rescaleConstraint(i - 1, 0.01); - GCSsys.rescaleConstraint(i, 0.01); - } - } - else if (Geoms[geoId].type == ArcOfHyperbola) { - - GCS::Point& center = Points[Geoms[geoId].midPointId]; - GCS::Point p0, p1; - if (pos == PointPos::mid || pos == PointPos::none) { - MoveParameters.resize(2); // cx,cy - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y; - - GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); - } - else if (pos == PointPos::start || pos == PointPos::end) { - - MoveParameters.resize(4); // x,y,cx,cy - if (pos == PointPos::start || pos == PointPos::end) { - GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] - : Points[Geoms[geoId].endPointId]; - ; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *p.x; - *p0.y = *p.y; - - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - p1.x = &MoveParameters[2]; - p1.y = &MoveParameters[3]; - *p1.x = *center.x; - *p1.y = *center.y; - - int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); - GCSsys.rescaleConstraint(i - 1, 0.01); - GCSsys.rescaleConstraint(i, 0.01); - } - } - else if (Geoms[geoId].type == ArcOfParabola) { - - GCS::Point& center = Points[Geoms[geoId].midPointId]; - GCS::Point p0, p1; - if (pos == PointPos::mid || pos == PointPos::none) { - MoveParameters.resize(2); // cx,cy - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y; - - GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); - } - else if (pos == PointPos::start || pos == PointPos::end) { - - MoveParameters.resize(4); // x,y,cx,cy - if (pos == PointPos::start || pos == PointPos::end) { - GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] - : Points[Geoms[geoId].endPointId]; - ; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *p.x; - *p0.y = *p.y; - - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - p1.x = &MoveParameters[2]; - p1.y = &MoveParameters[3]; - *p1.x = *center.x; - *p1.y = *center.y; - - int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); - GCSsys.rescaleConstraint(i - 1, 0.01); - GCSsys.rescaleConstraint(i, 0.01); - } - } - else if (Geoms[geoId].type == BSpline) { - if (pos == PointPos::start || pos == PointPos::end) { - MoveParameters.resize(2); // x,y - GCS::Point p0; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - if (pos == PointPos::start) { - GCS::Point& p = Points[Geoms[geoId].startPointId]; - *p0.x = *p.x; - *p0.y = *p.y; - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - else if (pos == PointPos::end) { - GCS::Point& p = Points[Geoms[geoId].endPointId]; - *p0.x = *p.x; - *p0.y = *p.y; - GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); - } - } - else if (pos == PointPos::none || pos == PointPos::mid) { + // We need to reserve enough size in the vec or the dynamic resizing + // (emplace_back in the for loop below) will trigger reallocation. + // Which will corrupt pointers we're storing. + size_t reserveSize = 0; + for (auto& pair : moved) { + int geoId = checkGeoId(pair.GeoId); + Sketcher::PointPos pos = pair.Pos; + if (Geoms[geoId].type == BSpline && (pos == PointPos::none || pos == PointPos::mid)) { GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; - MoveParameters.resize(bsp.poles.size() * 2); // x0,y0,x1,y1,....xp,yp - - int mvindex = 0; - for (std::vector::iterator it = bsp.poles.begin(); it != bsp.poles.end(); - it++, mvindex++) { - GCS::Point p1; - p1.x = &MoveParameters[mvindex]; - mvindex++; - p1.y = &MoveParameters[mvindex]; - - *p1.x = *(*it).x; - *p1.y = *(*it).y; - - GCSsys.addConstraintP2PCoincident(p1, (*it), GCS::DefaultTemporaryConstraint); - } + reserveSize += bsp.poles.size() * 2; + } + else { + reserveSize += 6; // 6 is the max case for all other cases. } } - else if (Geoms[geoId].type == Arc) { - GCS::Point& center = Points[Geoms[geoId].midPointId]; - GCS::Point p0, p1; - if (pos == PointPos::mid) { - MoveParameters.resize(2); // cx,cy - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y; - GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + MoveParameters.reserve(reserveSize); + + for (auto& pair : moved) { + int geoId = checkGeoId(pair.GeoId); + Sketcher::PointPos pos = pair.Pos; + + if (Geoms[geoId].type == Point) { + if (pos == PointPos::start) { + GCS::Point& point = Points[Geoms[geoId].startPointId]; + GCS::Point p0; + p0.x = &MoveParameters.emplace_back(*point.x); + p0.y = &MoveParameters.emplace_back(*point.y); + GCSsys.addConstraintP2PCoincident(p0, point, GCS::DefaultTemporaryConstraint); + } } - else if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::none) { - MoveParameters.resize(4); // x,y,cx,cy + else if (Geoms[geoId].type == Line) { if (pos == PointPos::start || pos == PointPos::end) { - GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] - : Points[Geoms[geoId].endPointId]; - ; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *p.x; - *p0.y = *p.y; + GCS::Point p0; + GCS::Point& p = pos == PointPos::start ? Points[Geoms[geoId].startPointId] + : Points[Geoms[geoId].endPointId]; + p0.x = &MoveParameters.emplace_back(*p.x); + p0.y = &MoveParameters.emplace_back(*p.y); GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); } + else if (pos == PointPos::none || pos == PointPos::mid) { + GCS::Point p1, p2; + GCS::Line& l = Lines[Geoms[geoId].index]; + p1.x = &MoveParameters.emplace_back(*l.p1.x); + p1.y = &MoveParameters.emplace_back(*l.p1.y); + p2.x = &MoveParameters.emplace_back(*l.p2.x); + p2.y = &MoveParameters.emplace_back(*l.p2.y); + GCSsys.addConstraintP2PCoincident(p1, l.p1, GCS::DefaultTemporaryConstraint); + GCSsys.addConstraintP2PCoincident(p2, l.p2, GCS::DefaultTemporaryConstraint); + } + } + else if (Geoms[geoId].type == Circle) { + GCS::Point& center = Points[Geoms[geoId].midPointId]; + GCS::Point p0, p1; + if (pos == PointPos::mid) { + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y); + GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + } else if (pos == PointPos::none) { - GCS::Arc& a = Arcs[Geoms[geoId].index]; - p0.x = &MoveParameters[0]; - p0.y = &MoveParameters[1]; - *p0.x = *center.x; - *p0.y = *center.y + *a.rad; - GCSsys.addConstraintPointOnArc(p0, a, GCS::DefaultTemporaryConstraint); + // bool pole = GeometryFacade::isInternalType(Geoms[geoId].geo, + // InternalType::BSplineControlPoint); + GCS::Circle& c = Circles[Geoms[geoId].index]; + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y + *c.rad); + GCSsys.addConstraintPointOnCircle(p0, c, GCS::DefaultTemporaryConstraint); + p1.x = &MoveParameters.emplace_back(*center.x); + p1.y = &MoveParameters.emplace_back(*center.y); + int i = + GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); + GCSsys.rescaleConstraint(i - 1, 0.01); + GCSsys.rescaleConstraint(i, 0.01); + } + } + else if (Geoms[geoId].type == Ellipse) { + if (pos == PointPos::mid || pos == PointPos::none) { + GCS::Point& center = Points[Geoms[geoId].midPointId]; + GCS::Point p0; + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y); + GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + } + } + else if (Geoms[geoId].type == ArcOfEllipse) { + + GCS::Point& center = Points[Geoms[geoId].midPointId]; + GCS::Point p0, p1; + if (pos == PointPos::mid || pos == PointPos::none) { + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y); + GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + } + else if (pos == PointPos::start || pos == PointPos::end) { + if (pos == PointPos::start || pos == PointPos::end) { + GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] + : Points[Geoms[geoId].endPointId]; + + p0.x = &MoveParameters.emplace_back(*p.x); + p0.y = &MoveParameters.emplace_back(*p.y); + GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); + } + + p1.x = &MoveParameters.emplace_back(*center.x); + p1.y = &MoveParameters.emplace_back(*center.y); + + int i = + GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); + GCSsys.rescaleConstraint(i - 1, 0.01); + GCSsys.rescaleConstraint(i, 0.01); + } + } + else if (Geoms[geoId].type == ArcOfHyperbola) { + GCS::Point& center = Points[Geoms[geoId].midPointId]; + GCS::Point p0, p1; + if (pos == PointPos::mid || pos == PointPos::none) { + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y); + GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + } + else if (pos == PointPos::start || pos == PointPos::end) { + GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] + : Points[Geoms[geoId].endPointId]; + p0.x = &MoveParameters.emplace_back(*p.x); + p0.y = &MoveParameters.emplace_back(*p.y); + GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); + p1.x = &MoveParameters.emplace_back(*center.x); + p1.y = &MoveParameters.emplace_back(*center.y); + int i = + GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); + GCSsys.rescaleConstraint(i - 1, 0.01); + GCSsys.rescaleConstraint(i, 0.01); + } + } + else if (Geoms[geoId].type == ArcOfParabola) { + GCS::Point& center = Points[Geoms[geoId].midPointId]; + GCS::Point p0, p1; + if (pos == PointPos::mid || pos == PointPos::none) { + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y); + GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + } + else if (pos == PointPos::start || pos == PointPos::end) { + GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] + : Points[Geoms[geoId].endPointId]; + p0.x = &MoveParameters.emplace_back(*p.x); + p0.y = &MoveParameters.emplace_back(*p.y); + GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); + p1.x = &MoveParameters.emplace_back(*center.x); + p1.y = &MoveParameters.emplace_back(*center.y); + int i = + GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); + GCSsys.rescaleConstraint(i - 1, 0.01); + GCSsys.rescaleConstraint(i, 0.01); + } + } + else if (Geoms[geoId].type == BSpline) { + if (pos == PointPos::start || pos == PointPos::end) { + GCS::Point p0; + GCS::Point& p = pos == PointPos::start ? Points[Geoms[geoId].startPointId] + : Points[Geoms[geoId].endPointId]; + p0.x = &MoveParameters.emplace_back(*p.x); + p0.y = &MoveParameters.emplace_back(*p.y); + GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); + } + else if (pos == PointPos::none || pos == PointPos::mid) { + GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; + for (auto pole : bsp.poles) { + GCS::Point p1; + p1.x = &MoveParameters.emplace_back(*pole.x); + p1.y = &MoveParameters.emplace_back(*pole.y); + GCSsys.addConstraintP2PCoincident(p1, pole, GCS::DefaultTemporaryConstraint); + } + } + } + else if (Geoms[geoId].type == Arc) { + GCS::Point& center = Points[Geoms[geoId].midPointId]; + GCS::Point p0, p1; + if (pos == PointPos::mid) { + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y); + GCSsys.addConstraintP2PCoincident(p0, center, GCS::DefaultTemporaryConstraint); + } + else if (pos == PointPos::none && moved.size() > 1) { + // When group dragging, arcs should move without modification. + GCS::Point p2; + GCS::Point& sp = Points[Geoms[geoId].startPointId]; + GCS::Point& ep = Points[Geoms[geoId].endPointId]; + p0.x = &MoveParameters.emplace_back(*sp.x); + p0.y = &MoveParameters.emplace_back(*sp.y); + GCSsys.addConstraintP2PCoincident(p0, sp, GCS::DefaultTemporaryConstraint); + + p2.x = &MoveParameters.emplace_back(*ep.x); + p2.y = &MoveParameters.emplace_back(*ep.y); + GCSsys.addConstraintP2PCoincident(p2, ep, GCS::DefaultTemporaryConstraint); + + p1.x = &MoveParameters.emplace_back(*center.x); + p1.y = &MoveParameters.emplace_back(*center.y); + int i = + GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); + GCSsys.rescaleConstraint(i - 2, 0.01); + GCSsys.rescaleConstraint(i - 1, 0.01); + GCSsys.rescaleConstraint(i, 0.01); + } + else if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::none) { + if (pos == PointPos::start || pos == PointPos::end) { + GCS::Point& p = (pos == PointPos::start) ? Points[Geoms[geoId].startPointId] + : Points[Geoms[geoId].endPointId]; + p0.x = &MoveParameters.emplace_back(*p.x); + p0.y = &MoveParameters.emplace_back(*p.y); + GCSsys.addConstraintP2PCoincident(p0, p, GCS::DefaultTemporaryConstraint); + } + else if (pos == PointPos::none) { + GCS::Arc& a = Arcs[Geoms[geoId].index]; + p0.x = &MoveParameters.emplace_back(*center.x); + p0.y = &MoveParameters.emplace_back(*center.y + *a.rad); + GCSsys.addConstraintPointOnArc(p0, a, GCS::DefaultTemporaryConstraint); + } + + p1.x = &MoveParameters.emplace_back(*center.x); + p1.y = &MoveParameters.emplace_back(*center.y); + int i = + GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); + GCSsys.rescaleConstraint(i - 1, 0.01); + GCSsys.rescaleConstraint(i, 0.01); } - p1.x = &MoveParameters[2]; - p1.y = &MoveParameters[3]; - *p1.x = *center.x; - *p1.y = *center.y; - int i = GCSsys.addConstraintP2PCoincident(p1, center, GCS::DefaultTemporaryConstraint); - GCSsys.rescaleConstraint(i - 1, 0.01); - GCSsys.rescaleConstraint(i, 0.01); } } + InitParameters = MoveParameters; GCSsys.initSolution(); isInitMove = true; + return 0; } +int Sketch::initMove(int geoId, PointPos pos, bool fine) +{ + std::vector moved = {GeoElementId(geoId, pos)}; + return initMove(moved, fine); +} + void Sketch::resetInitMove() { isInitMove = false; @@ -5140,17 +5093,15 @@ int Sketch::initBSplinePieceMove(int geoId, return 0; } -int Sketch::movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative) +int Sketch::movePoint(std::vector moved, Base::Vector3d toPoint, bool relative) { - geoId = checkGeoId(geoId); - - // don't try to move sketches that contain conflicting constraints if (hasConflicts()) { + // don't try to move sketches that contain conflicting constraints return -1; } if (!isInitMove) { - initMove(geoId, pos); + initMove(moved); initToPoint = toPoint; moveStep = 0; } @@ -5162,7 +5113,7 @@ int Sketch::movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool rela else { // I am getting too far away from the original solution so reinit the solution if ((toPoint - initToPoint).Length() > 20 * moveStep) { - initMove(geoId, pos); + initMove(moved); initToPoint = toPoint; } } @@ -5170,92 +5121,82 @@ int Sketch::movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool rela } if (relative) { - for (int i = 0; i < int(MoveParameters.size() - 1); i += 2) { + for (size_t i = 0; i < MoveParameters.size() - 1; i += 2) { MoveParameters[i] = InitParameters[i] + toPoint.x; MoveParameters[i + 1] = InitParameters[i + 1] + toPoint.y; } } - else if (Geoms[geoId].type == Point) { - if (pos == PointPos::start) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == Line) { - if (pos == PointPos::start || pos == PointPos::end) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - else if (pos == PointPos::none || pos == PointPos::mid) { - double dx = (InitParameters[2] - InitParameters[0]) / 2; - double dy = (InitParameters[3] - InitParameters[1]) / 2; - MoveParameters[0] = toPoint.x - dx; - MoveParameters[1] = toPoint.y - dy; - MoveParameters[2] = toPoint.x + dx; - MoveParameters[3] = toPoint.y + dy; - } - } - else if (Geoms[geoId].type == Circle) { - if (pos == PointPos::mid || pos == PointPos::none) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == Arc) { - if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid - || pos == PointPos::none) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == Ellipse) { - if (pos == PointPos::mid || pos == PointPos::none) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == ArcOfEllipse) { - if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid - || pos == PointPos::none) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == ArcOfHyperbola) { - if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid - || pos == PointPos::none) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == ArcOfParabola) { - if (pos == PointPos::start || pos == PointPos::end || pos == PointPos::mid - || pos == PointPos::none) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - } - else if (Geoms[geoId].type == BSpline) { - if (pos == PointPos::start || pos == PointPos::end) { - MoveParameters[0] = toPoint.x; - MoveParameters[1] = toPoint.y; - } - else if (pos == PointPos::none || pos == PointPos::mid) { - GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; - - double cx = 0, cy = 0; // geometric center - for (int i = 0; i < int(InitParameters.size() - 1); i += 2) { - cx += InitParameters[i]; - cy += InitParameters[i + 1]; + else { + size_t i = 0; + for (auto& pair : moved) { + if (i >= MoveParameters.size()) { + break; } + int geoId = checkGeoId(pair.GeoId); + Sketcher::PointPos pos = pair.Pos; - cx /= bsp.poles.size(); - cy /= bsp.poles.size(); + if (Geoms[geoId].type == Point) { + if (pos == PointPos::start) { + MoveParameters[i] = toPoint.x; + MoveParameters[i + 1] = toPoint.y; + i += 2; + } + } + else if (Geoms[geoId].type == Line) { + if (pos == PointPos::start || pos == PointPos::end) { + MoveParameters[i] = toPoint.x; + MoveParameters[i + 1] = toPoint.y; + i += 2; + } + else if (pos == PointPos::none || pos == PointPos::mid) { + double dx = (InitParameters[i + 2] - InitParameters[i]) * 0.5; + double dy = (InitParameters[i + 3] - InitParameters[i + 1]) * 0.5; + MoveParameters[i] = toPoint.x - dx; + MoveParameters[i + 1] = toPoint.y - dy; + MoveParameters[i + 2] = toPoint.x + dx; + MoveParameters[i + 3] = toPoint.y + dy; + i += 4; + } + } + else if (Geoms[geoId].type == Circle || Geoms[geoId].type == Ellipse) { + if (pos == PointPos::mid || pos == PointPos::none) { + MoveParameters[i] = toPoint.x; + MoveParameters[i + 1] = toPoint.y; + i += 2; + } + } + else if (Geoms[geoId].type == Arc || Geoms[geoId].type == ArcOfEllipse + || Geoms[geoId].type == ArcOfHyperbola || Geoms[geoId].type == ArcOfParabola) { + MoveParameters[i] = toPoint.x; + MoveParameters[i + 1] = toPoint.y; + i += 2; + } + else if (Geoms[geoId].type == BSpline) { + if (pos == PointPos::start || pos == PointPos::end) { + MoveParameters[i] = toPoint.x; + MoveParameters[i + 1] = toPoint.y; + i += 2; + } + else if (pos == PointPos::none || pos == PointPos::mid) { + GCS::BSpline& bsp = BSplines[Geoms[geoId].index]; - for (int i = 0; i < int(MoveParameters.size() - 1); i += 2) { + double cx = 0, cy = 0; // geometric center + for (size_t j = 0; j < bsp.poles.size() * 2; j += 2) { + cx += InitParameters[i + j]; + cy += InitParameters[i + j + 1]; + j += 2; + } - MoveParameters[i] = toPoint.x + InitParameters[i] - cx; - MoveParameters[i + 1] = toPoint.y + InitParameters[i + 1] - cy; + cx /= bsp.poles.size(); + cy /= bsp.poles.size(); + + for (size_t j = 0; j < bsp.poles.size() * 2; j += 2) { + MoveParameters[i + j] = toPoint.x + InitParameters[i + j] - cx; + MoveParameters[i + j + 1] = toPoint.y + InitParameters[i + j + 1] - cy; + j += 2; + } + i += bsp.poles.size() * 2; + } } } } @@ -5263,6 +5204,12 @@ int Sketch::movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool rela return solve(); } +int Sketch::movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative) +{ + std::vector moved = {GeoElementId(geoId, pos)}; + return movePoint(moved, toPoint, relative); +} + int Sketch::setDatum(int /*constrId*/, double /*value*/) { return -1; diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index 0ffff0ed26..79a9f237cb 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -161,6 +161,7 @@ public: /** initializes a point (or curve) drag by setting the current * sketch status as a reference */ + int initMove(std::vector moved, bool fine = true); int initMove(int geoId, PointPos pos, bool fine = true); /** Initializes a B-spline piece drag by setting the current @@ -184,6 +185,7 @@ public: * a condition for satisfying the new point location! * The relative flag permits moving relatively to the current position */ + int movePoint(std::vector moved, Base::Vector3d toPoint, bool relative = false); int movePoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative = false); /** diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index bbf2d0370b..9e4e723d6d 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -1340,9 +1340,10 @@ int SketchObject::diagnoseAdditionalConstraints( return lastDoF; } -int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toPoint, bool relative, +int SketchObject::movePoint(std::vector moved, const Base::Vector3d& toPoint, bool relative, bool updateGeoBeforeMoving) { + // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); @@ -1369,7 +1370,7 @@ int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toP return -1; // move the point and solve - lastSolverStatus = solvedSketch.movePoint(GeoId, PosId, toPoint, relative); + lastSolverStatus = solvedSketch.movePoint(moved, toPoint, relative); // moving the point can not result in a conflict that we did not have // or a redundancy that we did not have before, or a change of DoF @@ -1378,10 +1379,10 @@ int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toP std::vector geomlist = solvedSketch.extractGeometry(); Geometry.setValues(geomlist); // Constraints.acceptGeometry(getCompleteGeometry()); - for (std::vector::iterator it = geomlist.begin(); it != geomlist.end(); - ++it) { - if (*it) - delete *it; + for (auto* geo : geomlist) { + if (geo){ + delete geo; + } } } @@ -1390,6 +1391,13 @@ int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toP return lastSolverStatus; } +int SketchObject::movePoint(int geoId, PointPos pos, const Base::Vector3d& toPoint, bool relative, + bool updateGeoBeforeMoving) +{ + std::vector moved = { GeoElementId(geoId, pos) }; + return movePoint(moved, toPoint, relative, updateGeoBeforeMoving); +} + template <> Base::Vector3d SketchObject::getPointForGeometry<>(const Part::GeomPoint *geomPoint, PointPos PosId) { diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 551022761d..a0cf073571 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -352,6 +352,10 @@ public: /// toggle the driving status of this constraint int toggleVirtualSpace(int ConstrId); /// move this point to a new location and solve + int movePoint(std::vector moved, + const Base::Vector3d& toPoint, + bool relative = false, + bool updateGeoBeforeMoving = false); int movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toPoint, @@ -687,6 +691,8 @@ public: /* Solver exposed interface */ } /// Forwards a request for a temporary initMove to the solver using the current sketch state as /// a reference (enables dragging) + + inline int initTemporaryMove(std::vector moved, bool fine = true); inline int initTemporaryMove(int geoId, PointPos pos, bool fine = true); /// Forwards a request for a temporary initBSplinePieceMove to the solver using the current /// sketch state as a reference (enables dragging) @@ -698,6 +704,9 @@ public: /* Solver exposed interface */ * state as a reference (enables dragging). NOTE: A temporary move operation must always be * preceded by a initTemporaryMove() operation. */ + inline int moveTemporaryPoint(std::vector moved, + Base::Vector3d toPoint, + bool relative = false); inline int moveTemporaryPoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative = false); /// forwards a request to update an extension of a geometry of the solver to the solver. @@ -1061,16 +1070,19 @@ private: mutable std::map internalElementMap; }; -inline int SketchObject::initTemporaryMove(int geoId, PointPos pos, bool fine /*=true*/) +inline int SketchObject::initTemporaryMove(std::vector moved, bool fine /*=true*/) { - // if a previous operation did not update the geometry (including geometry extensions) - // or constraints (including any deleted pointer, as in renameConstraint) of the solver, - // here we update them before starting a temporary operation. if (solverNeedsUpdate) { solve(); } - return solvedSketch.initMove(geoId, pos, fine); + return solvedSketch.initMove(moved, fine); +} + +inline int SketchObject::initTemporaryMove(int geoId, PointPos pos, bool fine /*=true*/) +{ + std::vector moved = {GeoElementId(geoId, pos)}; + return initTemporaryMove(moved, fine); } inline int SketchObject::initTemporaryBSplinePieceMove(int geoId, @@ -1088,12 +1100,19 @@ inline int SketchObject::initTemporaryBSplinePieceMove(int geoId, return solvedSketch.initBSplinePieceMove(geoId, pos, firstPoint, fine); } +inline int SketchObject::moveTemporaryPoint(std::vector moved, + Base::Vector3d toPoint, + bool relative /*=false*/) +{ + return solvedSketch.movePoint(moved, toPoint, relative); +} inline int SketchObject::moveTemporaryPoint(int geoId, PointPos pos, Base::Vector3d toPoint, bool relative /*=false*/) { - return solvedSketch.movePoint(geoId, pos, toPoint, relative); + std::vector moved = {GeoElementId(geoId, pos)}; + return moveTemporaryPoint(moved, toPoint, relative); } diff --git a/src/Mod/Sketcher/App/SketchObjectPy.xml b/src/Mod/Sketcher/App/SketchObjectPy.xml index b47a995cbc..c3a1ebd860 100644 --- a/src/Mod/Sketcher/App/SketchObjectPy.xml +++ b/src/Mod/Sketcher/App/SketchObjectPy.xml @@ -528,6 +528,22 @@ setLabelDistance(constraintIndex:int, value:float) + + + + moveGeometries(Geos,Vector,[relative]) - move given points and curves + to another location. + It moves the specified points and curves to the given location by adding some + temporary weak constraints and solve the sketch. + This method is mostly used to allow the user to drag some portions of the sketch + in real time by e.g. the mouse and it works only for underconstrained portions of + the sketch. + The argument 'relative', if present, states if the new location is given + relatively to the current one. For group dragging this is enforced. + Geos is a vector of pairs of geoId and posId. + + + diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 67550b5cb5..bdbcdd49f4 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -1167,6 +1167,58 @@ PyObject* SketchObjectPy::setLabelDistance(PyObject* args) Py_Return; } +PyObject* SketchObjectPy::moveGeometries(PyObject* args) +{ + PyObject* pyList; + PyObject* pcObj; + int relative = 0; + + // Parse arguments: list of pairs, Base::VectorPy, optional relative flag + if (!PyArg_ParseTuple(args, + "O!O!|i", + &PyList_Type, + &pyList, // List of pairs (geoId, pointPos) + &(Base::VectorPy::Type), + &pcObj, // Target vector + &relative)) { // Optional relative flag + return nullptr; + } + + // Convert Python list to std::vector + std::vector moved; + Py_ssize_t listSize = PyList_Size(pyList); + + for (Py_ssize_t i = 0; i < listSize; ++i) { + PyObject* pyPair = PyList_GetItem(pyList, i); // Borrowed reference + + if (!PyTuple_Check(pyPair) || PyTuple_Size(pyPair) != 2) { + PyErr_SetString(PyExc_ValueError, "List must contain pairs (geoId, pointPos)."); + return nullptr; + } + + int geoId = PyLong_AsLong(PyTuple_GetItem(pyPair, 0)); + int pointPos = PyLong_AsLong(PyTuple_GetItem(pyPair, 1)); + + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "Invalid geoId or pointPos in the list."); + return nullptr; + } + + moved.emplace_back(GeoElementId(geoId, static_cast(pointPos))); + } + + // Convert Python vector to Base::Vector3d + Base::Vector3d v1 = static_cast(pcObj)->value(); + + // Call the C++ method + if (this->getSketchObjectPtr()->movePoint(moved, v1, (relative > 0))) { + PyErr_SetString(PyExc_ValueError, "Failed to move geometries."); + return nullptr; + } + + Py_RETURN_NONE; +} + PyObject* SketchObjectPy::movePoint(PyObject* args) { PyObject* pcObj; diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 4f1938f61e..3aaf19fa1d 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -715,8 +715,8 @@ void ViewProviderSketch::ensureFocus() void ViewProviderSketch::preselectAtPoint(Base::Vector2d point) { if (Mode != STATUS_SELECT_Point && Mode != STATUS_SELECT_Edge - && Mode != STATUS_SELECT_Constraint && Mode != STATUS_SKETCH_DragPoint - && Mode != STATUS_SKETCH_DragCurve && Mode != STATUS_SKETCH_DragConstraint + && Mode != STATUS_SELECT_Constraint && Mode != STATUS_SKETCH_Drag + && Mode != STATUS_SKETCH_DragConstraint && Mode != STATUS_SKETCH_UseRubberBand) { Gui::MDIView* mdi = this->getActiveView(); @@ -766,24 +766,9 @@ bool ViewProviderSketch::keyPressed(bool pressed, int key) } return true; } - if (isInEditMode() && drag.isDragCurveValid()) { + if (isInEditMode() && !drag.Dragged.empty()) { if (!pressed) { - getSketchObject()->movePoint( - drag.DragCurve, Sketcher::PointPos::none, Base::Vector3d(0, 0, 0), true); - drag.DragCurve = Drag::InvalidCurve; - resetPositionText(); - Mode = STATUS_NONE; - } - return true; - } - if (isInEditMode() && drag.isDragPointValid()) { - if (!pressed) { - int GeoId; - Sketcher::PointPos PosId; - getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); - getSketchObject()->movePoint(GeoId, PosId, Base::Vector3d(0, 0, 0), true); - drag.DragPoint = Drag::InvalidPoint; - resetPositionText(); + commitDragMove(drag.xInit, drag.yInit); Mode = STATUS_NONE; } return true; @@ -1053,111 +1038,8 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe } Mode = STATUS_NONE; return true; - case STATUS_SKETCH_DragPoint: - if (drag.isDragPointValid()) { - int GeoId; - Sketcher::PointPos PosId; - - getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); - - if (GeoId != Sketcher::GeoEnum::GeoUndef - && PosId != Sketcher::PointPos::none) { - getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Drag Point")); - try { - Gui::cmdAppObjectArgs(getObject(), - "movePoint(%d,%d,App.Vector(%f,%f,0),%d)", - GeoId, - static_cast(PosId), - x - drag.xInit, - y - drag.yInit, - 0); - - getDocument()->commitCommand(); - - tryAutoRecomputeIfNotSolve(getSketchObject()); - } - catch (const Base::Exception& e) { - getDocument()->abortCommand(); - Base::Console().DeveloperError( - "ViewProviderSketch", "Drag point: %s\n", e.what()); - } - } - setPreselectPoint(drag.DragPoint); - drag.DragPoint = Drag::InvalidPoint; - } - resetPositionText(); - Mode = STATUS_NONE; - return true; - case STATUS_SKETCH_DragCurve: - if (drag.isDragCurveValid()) { - const Part::Geometry* geo = getSketchObject()->getGeometry(drag.DragCurve); - if (geo->is() - || geo->is() - || geo->is() - || geo->is() - || geo->is() - || geo->is() - || geo->is() - || geo->is()) { - getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Drag Curve")); - - auto geo = getSketchObject()->getGeometry(drag.DragCurve); - auto gf = GeometryFacade::getFacade(geo); - - Base::Vector3d vec(x - drag.xInit, y - drag.yInit, 0); - - // BSpline weights have a radius corresponding to the weight value - // However, in order for them proportional to the B-Spline size, - // the scenograph has a size scalefactor times the weight - // This code normalizes the information sent to the solver. - if (gf->getInternalType() == InternalType::BSplineControlPoint) { - auto circle = static_cast(geo); - Base::Vector3d center = circle->getCenter(); - - Base::Vector3d dir = vec - center; - - double scalefactor = 1.0; - - if (circle->hasExtension( - SketcherGui::ViewProviderSketchGeometryExtension:: - getClassTypeId())) { - auto vpext = std::static_pointer_cast< - const SketcherGui::ViewProviderSketchGeometryExtension>( - circle - ->getExtension( - SketcherGui::ViewProviderSketchGeometryExtension:: - getClassTypeId()) - .lock()); - - scalefactor = vpext->getRepresentationFactor(); - } - - vec = center + dir / scalefactor; - } - - try { - Gui::cmdAppObjectArgs(getObject(), - "movePoint(%d,%d,App.Vector(%f,%f,0),%d)", - drag.DragCurve, - static_cast(Sketcher::PointPos::none), - vec.x, - vec.y, - drag.relative ? 1 : 0); - - getDocument()->commitCommand(); - - tryAutoRecomputeIfNotSolve(getSketchObject()); - } - catch (const Base::Exception& e) { - getDocument()->abortCommand(); - Base::Console().DeveloperError( - "ViewProviderSketch", "Drag curve: %s\n", e.what()); - } - } - preselection.PreselectCurve = drag.DragCurve; - drag.DragCurve = Drag::InvalidCurve; - } - resetPositionText(); + case STATUS_SKETCH_Drag: + commitDragMove(x, y); Mode = STATUS_NONE; return true; case STATUS_SKETCH_DragConstraint: @@ -1313,8 +1195,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe generateContextMenu(); return true; } - case STATUS_SKETCH_DragPoint: - case STATUS_SKETCH_DragCurve: + case STATUS_SKETCH_Drag: case STATUS_SKETCH_DragConstraint: case STATUS_SKETCH_StartRubberBand: case STATUS_SKETCH_UseRubberBand: @@ -1487,9 +1368,8 @@ bool ViewProviderSketch::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventor bool preselectChanged = false; if (Mode != STATUS_SELECT_Point && Mode != STATUS_SELECT_Edge - && Mode != STATUS_SELECT_Constraint && Mode != STATUS_SKETCH_DragPoint - && Mode != STATUS_SKETCH_DragCurve && Mode != STATUS_SKETCH_DragConstraint - && Mode != STATUS_SKETCH_UseRubberBand) { + && Mode != STATUS_SELECT_Constraint && Mode != STATUS_SKETCH_Drag + && Mode != STATUS_SKETCH_DragConstraint && Mode != STATUS_SKETCH_UseRubberBand) { std::unique_ptr Point(this->getPointOnRay(cursorPos, viewer)); @@ -1505,19 +1385,12 @@ bool ViewProviderSketch::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventor } return false; case STATUS_SELECT_Point: - if (!getSolvedSketch().hasConflicts() && preselection.isPreselectPointValid() - && drag.DragPoint != preselection.PreselectPoint) { - Mode = STATUS_SKETCH_DragPoint; - drag.DragPoint = preselection.PreselectPoint; - int GeoId; - Sketcher::PointPos PosId; + if (!getSolvedSketch().hasConflicts() && preselection.isPreselectPointValid()) { + int geoId; + Sketcher::PointPos pos; + getSketchObject()->getGeoVertexIndex(preselection.PreselectPoint, geoId, pos); - getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); - - if (GeoId != Sketcher::GeoEnum::GeoUndef && PosId != Sketcher::PointPos::none) { - getSketchObject()->initTemporaryMove(GeoId, PosId, false); - drag.resetVector(); - } + initDragging(geoId, pos, viewer); } else { Mode = STATUS_NONE; @@ -1525,107 +1398,11 @@ bool ViewProviderSketch::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventor resetPreselectPoint(); return true; case STATUS_SELECT_Edge: - if (!getSolvedSketch().hasConflicts() && preselection.isPreselectCurveValid() - && drag.DragCurve != preselection.PreselectCurve) { - Mode = STATUS_SKETCH_DragCurve; - drag.DragCurve = preselection.PreselectCurve; - const Part::Geometry* geo = getSketchObject()->getGeometry(drag.DragCurve); + if (!getSolvedSketch().hasConflicts() && preselection.isPreselectCurveValid()) { + int geoId = preselection.PreselectCurve; + Sketcher::PointPos pos = Sketcher::PointPos::none; - // BSpline Control points are edge draggable only if their radius is movable - // This is because dragging gives unwanted cosmetic results due to the scale ratio. - // This is an heuristic as it does not check all indirect routes. - if (GeometryFacade::isInternalType(geo, InternalType::BSplineControlPoint)) { - if (geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) { - auto solvext = - std::static_pointer_cast( - geo->getExtension( - Sketcher::SolverGeometryExtension::getClassTypeId()) - .lock()); - - // Edge parameters are Independent, so weight won't move - if (solvext->getEdge() == Sketcher::SolverGeometryExtension::Independent) { - Mode = STATUS_NONE; - return false; - } - - // The B-Spline is constrained to be non-rational (equal weights), moving - // produces a bad effect because OCCT will normalize the values of the - // weights. - auto grp = getSolvedSketch().getDependencyGroup(drag.DragCurve, - Sketcher::PointPos::none); - - int bsplinegeoid = -1; - - std::vector polegeoids; - - for (auto c : getSketchObject()->Constraints.getValues()) { - if (c->Type == Sketcher::InternalAlignment - && c->AlignmentType == BSplineControlPoint - && c->First == drag.DragCurve) { - - bsplinegeoid = c->Second; - break; - } - } - - if (bsplinegeoid == -1) { - Mode = STATUS_NONE; - return false; - } - - for (auto c : getSketchObject()->Constraints.getValues()) { - if (c->Type == Sketcher::InternalAlignment - && c->AlignmentType == BSplineControlPoint - && c->Second == bsplinegeoid) { - - polegeoids.push_back(c->First); - } - } - - bool allingroup = true; - - for (auto polegeoid : polegeoids) { - std::pair thispole = - std::make_pair(polegeoid, Sketcher::PointPos::none); - - if (grp.find(thispole) == grp.end())// not found - allingroup = false; - } - - if (allingroup) {// it is constrained to be non-rational - Mode = STATUS_NONE; - return false; - } - } - } - - if (geo->is() - || geo->is()) { - drag.relative = true; - - // Since the cursor moved from where it was clicked, and this is a relative - // move, calculate the click position and use it as initial point. - SbLine line2; - getProjectingLine(DoubleClick::prvCursorPos, viewer, line2); - getCoordsOnSketchPlane( - line2.getPosition(), line2.getDirection(), drag.xInit, drag.yInit); - snapManager->snap(drag.xInit, drag.yInit); - } - else { - drag.resetVector(); - } - - if (geo->is()) { - getSketchObject()->initTemporaryBSplinePieceMove( - drag.DragCurve, - Sketcher::PointPos::none, - Base::Vector3d(drag.xInit, drag.yInit, 0.0), - false); - } - else { - getSketchObject()->initTemporaryMove( - drag.DragCurve, Sketcher::PointPos::none, false); - } + initDragging(geoId, pos, viewer); } else { Mode = STATUS_NONE; @@ -1639,64 +1416,10 @@ bool ViewProviderSketch::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventor drag.yInit = y; resetPreselectPoint(); return true; - case STATUS_SKETCH_DragPoint: - if (drag.isDragPointValid()) { - // Base::Console().Log("Drag Point:%d\n",edit->DragPoint); - int GeoId; - Sketcher::PointPos PosId; - getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); - Base::Vector3d vec(x, y, 0); - - if (GeoId != Sketcher::GeoEnum::GeoUndef && PosId != Sketcher::PointPos::none) { - if (getSketchObject()->moveTemporaryPoint(GeoId, PosId, vec, false) == 0) { - setPositionText(Base::Vector2d(x, y)); - draw(true, false); - } - } - } - return true; - case STATUS_SKETCH_DragCurve: - if (drag.isDragCurveValid()) { - auto geo = getSketchObject()->getGeometry(drag.DragCurve); - auto gf = GeometryFacade::getFacade(geo); - - Base::Vector3d vec(x - drag.xInit, y - drag.yInit, 0); - - // BSpline weights have a radius corresponding to the weight value - // However, in order for them proportional to the B-Spline size, - // the scenograph has a size scalefactor times the weight - // This code normalizes the information sent to the solver. - if (gf->getInternalType() == InternalType::BSplineControlPoint) { - auto circle = static_cast(geo); - Base::Vector3d center = circle->getCenter(); - - Base::Vector3d dir = vec - center; - - double scalefactor = 1.0; - - if (circle->hasExtension( - SketcherGui::ViewProviderSketchGeometryExtension::getClassTypeId())) { - auto vpext = std::static_pointer_cast< - const SketcherGui::ViewProviderSketchGeometryExtension>( - circle - ->getExtension(SketcherGui::ViewProviderSketchGeometryExtension:: - getClassTypeId()) - .lock()); - - scalefactor = vpext->getRepresentationFactor(); - } - - vec = center + dir / scalefactor; - } - - if (getSketchObject()->moveTemporaryPoint( - drag.DragCurve, Sketcher::PointPos::none, vec, drag.relative) - == 0) { - setPositionText(Base::Vector2d(x, y)); - draw(true, false); - } - } + case STATUS_SKETCH_Drag: { + doDragStep(x, y); return true; + } case STATUS_SKETCH_DragConstraint: if (!drag.DragConstraintSet.empty()) { auto idset = drag.DragConstraintSet; @@ -1738,6 +1461,291 @@ bool ViewProviderSketch::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventor return false; } +void ViewProviderSketch::initDragging(int geoId, Sketcher::PointPos pos, Gui::View3DInventorViewer* viewer) +{ + if (geoId < 0) { + return; // don't drag externals + } + + drag.reset(); + Mode = STATUS_SKETCH_Drag; + drag.Dragged.push_back(GeoElementId(geoId, pos)); + + // Adding selected geos that should be dragged as well. + for (auto& geoIdi : selection.SelCurvSet) { + if (geoIdi < 0) { + continue; //skip externals + } + + if (geoIdi == geoId) { + // geoId is already added because it was the preselected. + // 2 cases : either the edge was added or a point of it. + // If its a point then we replace it by the edge. + // If it's the edge it's replaced by itself so it's ok. + drag.Dragged[0].Pos = Sketcher::PointPos::none; + } + else { + // For group dragging, we skip the internal geos. + const Part::Geometry* geo = getSketchObject()->getGeometry(geoId); + if (!GeometryFacade::isInternalAligned(geo)) { + drag.Dragged.push_back(GeoElementId(geoIdi)); + } + } + } + for (auto& pointId : selection.SelPointSet) { + int geoIdi; + Sketcher::PointPos posi; + getSketchObject()->getGeoVertexIndex(pointId, geoIdi, posi); + if (geoIdi < 0) { + continue; //skip externals + } + + bool add = true; + for (auto& pair : drag.Dragged) { + int geoIdj = pair.GeoId; + Sketcher::PointPos posj = pair.Pos; + if (geoIdi == geoIdj && (posi == posj || posj == Sketcher::PointPos::none)) { + add = false; + break; + } + } + if (add) { + drag.Dragged.push_back(GeoElementId(geoIdi, posi)); + } + } + + auto setRelative = [&]() { + drag.relative = true; + + // Calculate the click position and use it as the initial point + SbLine line2; + getProjectingLine(DoubleClick::prvCursorPos, viewer, line2); + getCoordsOnSketchPlane( + line2.getPosition(), line2.getDirection(), drag.xInit, drag.yInit); + snapManager->snap(drag.xInit, drag.yInit); + }; + + if (drag.Dragged.size() == 1 && pos == Sketcher::PointPos::none) { + const Part::Geometry* geo = getSketchObject()->getGeometry(geoId); + + // BSpline Control points are edge draggable only if their radius is movable + // This is because dragging gives unwanted cosmetic results due to the scale ratio. + // This is an heuristic as it does not check all indirect routes. + if (GeometryFacade::isInternalType(geo, InternalType::BSplineControlPoint)) { + if (geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) { + auto solvext = + std::static_pointer_cast( + geo->getExtension( + Sketcher::SolverGeometryExtension::getClassTypeId()) + .lock()); + + // Edge parameters are Independent, so weight won't move + if (solvext->getEdge() == Sketcher::SolverGeometryExtension::Independent) { + Mode = STATUS_NONE; + return; + } + + // The B-Spline is constrained to be non-rational (equal weights), moving + // produces a bad effect because OCCT will normalize the values of the + // weights. + auto grp = getSolvedSketch().getDependencyGroup(geoId, + Sketcher::PointPos::none); + + int bsplinegeoid = -1; + + std::vector polegeoids; + + for (auto c : getSketchObject()->Constraints.getValues()) { + if (c->Type == Sketcher::InternalAlignment + && c->AlignmentType == BSplineControlPoint + && c->First == geoId) { + + bsplinegeoid = c->Second; + break; + } + } + + if (bsplinegeoid == -1) { + Mode = STATUS_NONE; + return; + } + + for (auto c : getSketchObject()->Constraints.getValues()) { + if (c->Type == Sketcher::InternalAlignment + && c->AlignmentType == BSplineControlPoint + && c->Second == bsplinegeoid) { + + polegeoids.push_back(c->First); + } + } + + bool allingroup = true; + + for (auto polegeoid : polegeoids) { + std::pair thispole = + std::make_pair(polegeoid, Sketcher::PointPos::none); + + if (grp.find(thispole) == grp.end())// not found + allingroup = false; + } + + if (allingroup) {// it is constrained to be non-rational + Mode = STATUS_NONE; + return; + } + } + } + + if (geo->is() || geo->is()) { + setRelative(); + } + + if (geo->is()) { + getSketchObject()->initTemporaryBSplinePieceMove( + geoId, + Sketcher::PointPos::none, + Base::Vector3d(drag.xInit, drag.yInit, 0.0), + false); + return; + } + } + else if (drag.Dragged.size() > 1) { + setRelative(); + } + + getSketchObject()->initTemporaryMove(drag.Dragged, false); +} + +void ViewProviderSketch::doDragStep(double x, double y) +{ + Base::Vector3d vec(x - drag.xInit, y - drag.yInit, 0); + + if (drag.Dragged.size() == 1) { + // special single bspline point handling. + Sketcher::PointPos PosId = drag.Dragged[0].Pos; + + if (PosId == Sketcher::PointPos::none) { + int GeoId = drag.Dragged[0].GeoId; + auto geo = getSketchObject()->getGeometry(GeoId); + auto gf = GeometryFacade::getFacade(geo); + + // BSpline weights have a radius corresponding to the weight value + // However, in order for them proportional to the B-Spline size, + // the scenograph has a size scalefactor times the weight + // This code normalizes the information sent to the solver. + if (gf->getInternalType() == InternalType::BSplineControlPoint) { + auto circle = static_cast(geo); + Base::Vector3d center = circle->getCenter(); + + Base::Vector3d dir = vec - center; + + double scalefactor = 1.0; + + if (circle->hasExtension( + SketcherGui::ViewProviderSketchGeometryExtension::getClassTypeId())) { + auto vpext = std::static_pointer_cast< + const SketcherGui::ViewProviderSketchGeometryExtension>( + circle + ->getExtension(SketcherGui::ViewProviderSketchGeometryExtension:: + getClassTypeId()) + .lock()); + + scalefactor = vpext->getRepresentationFactor(); + } + + vec = center + dir / scalefactor; + } + } + } + + if (getSketchObject()->moveTemporaryPoint(drag.Dragged, vec, drag.relative) == 0) { + setPositionText(Base::Vector2d(x, y)); + draw(true, false); + } +} + +void ViewProviderSketch::commitDragMove(double x, double y) +{ + const char* cmdName = (drag.Dragged.size() == 1) ? + (drag.Dragged[0].Pos == Sketcher::PointPos::none ? + QT_TRANSLATE_NOOP("Command", "Drag Curve") : QT_TRANSLATE_NOOP("Command", "Drag Point")) + : QT_TRANSLATE_NOOP("Command", "Drag geometries"); + + getDocument()->openCommand(cmdName); + + Base::Vector3d vec(x - drag.xInit, y - drag.yInit, 0); + + if (drag.Dragged.size() == 1) { + // special single bspline point handling. + Sketcher::PointPos PosId = drag.Dragged[0].Pos; + + if (PosId == Sketcher::PointPos::none) { + int GeoId = drag.Dragged[0].GeoId; + auto geo = getSketchObject()->getGeometry(GeoId); + auto gf = GeometryFacade::getFacade(geo); + + // BSpline weights have a radius corresponding to the weight value + // However, in order for them proportional to the B-Spline size, + // the scenograph has a size scalefactor times the weight + // This code normalizes the information sent to the solver. + if (gf->getInternalType() == InternalType::BSplineControlPoint) { + auto circle = static_cast(geo); + Base::Vector3d center = circle->getCenter(); + + Base::Vector3d dir = vec - center; + + double scalefactor = 1.0; + + if (circle->hasExtension( + SketcherGui::ViewProviderSketchGeometryExtension:: + getClassTypeId())) { + auto vpext = std::static_pointer_cast< + const SketcherGui::ViewProviderSketchGeometryExtension>( + circle + ->getExtension( + SketcherGui::ViewProviderSketchGeometryExtension:: + getClassTypeId()) + .lock()); + + scalefactor = vpext->getRepresentationFactor(); + } + + vec = center + dir / scalefactor; + } + } + } + + std::stringstream cmd; + cmd << "moveGeometries("; + cmd << "["; + for (size_t i = 0; i < drag.Dragged.size(); ++i) { + if (i > 0) { + cmd << ", "; + } + cmd << "(" << drag.Dragged[i].GeoId << ", " << static_cast(drag.Dragged[i].Pos) << ")"; + } + cmd << "], App.Vector(" << vec.x << ", " << vec.y << ", 0)"; + + if (drag.relative) { + cmd << ", True"; + } + cmd << ")"; + + try { + Gui::cmdAppObjectArgs(getObject(), cmd.str().c_str()); + } + catch (const Base::Exception& e) { + getDocument()->abortCommand(); + Base::Console().DeveloperError("ViewProviderSketch", "Drag: %s\n", e.what()); + } + + getDocument()->commitCommand(); + + tryAutoRecomputeIfNotSolve(getSketchObject()); + drag.reset(); + resetPositionText(); +} + void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d& toPos) { if (auto constr = getConstraint(constNum)) { diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index 0ceb339efd..869f714c54 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "PropertyVisualLayerList.h" @@ -261,13 +262,12 @@ private: class Drag { public: - enum SpecialValues - { - InvalidPoint = -1, - InvalidCurve = -1 - }; - Drag() + { + reset(); + } + + void reset() { resetVector(); resetIds(); @@ -282,28 +282,15 @@ private: void resetIds() { - DragPoint = InvalidPoint; - DragCurve = InvalidCurve; + Dragged.clear(); DragConstraintSet.clear(); } - bool isDragPointValid() - { - return DragPoint > InvalidPoint; - } - bool isDragCurveValid() - { - return DragCurve > InvalidCurve; - } - double xInit, yInit; // starting point of the dragging operation bool relative; // whether the dragging move vector is relative or absolute - - int DragPoint; // dragged point id (only positive integers) - int DragCurve; // dragged curve id (only positive integers), negative external curves - // cannot be dragged. - std::set DragConstraintSet; // dragged constraints ids + std::vector Dragged; // dragged geometries + std::set DragConstraintSet; // dragged constraints ids }; // TODO: Selection and Preselection should use a same structure. Probably Drag should use the @@ -553,8 +540,7 @@ public: STATUS_SELECT_Constraint, /**< enum value a constraint was selected. */ STATUS_SELECT_Cross, /**< enum value the base coordinate system was selected. */ STATUS_SELECT_Wire, /**< enum value and edge was double clicked. */ - STATUS_SKETCH_DragPoint, /**< enum value while dragging a point. */ - STATUS_SKETCH_DragCurve, /**< enum value while dragging a curve. */ + STATUS_SKETCH_Drag, /**< enum value while dragging curves and or points. */ STATUS_SKETCH_DragConstraint, /**< enum value while dragging a compatible constraint. */ STATUS_SKETCH_UseHandler, /**< enum value a DrawSketchHandler is in control. */ STATUS_SKETCH_StartRubberBand, /**< enum value for initiating a rubber band selection */ @@ -792,6 +778,15 @@ private: bool setPreselect(const std::string& subNameSuffix, float x = 0, float y = 0, float z = 0); //@} + /** @name dragging functions */ + //@{ + /// dragging helpers + void initDragging(int geoId, Sketcher::PointPos pos, Gui::View3DInventorViewer* viewer); + void doDragStep(double x, double y); + void commitDragMove(double x, double y); + + //@} + /** @name Selection functions */ //@{ /// box selection method