diff --git a/src/Base/Tools2D.h b/src/Base/Tools2D.h index 1b1b07f6e..0e2b9b7ce 100644 --- a/src/Base/Tools2D.h +++ b/src/Base/Tools2D.h @@ -61,6 +61,7 @@ public: inline bool operator== (const Vector2D &rclVct) const; inline Vector2D operator+ (const Vector2D &rclVct) const; inline Vector2D operator- (const Vector2D &rclVct) const; + inline Vector2D operator/ (const double x) const; inline void Set (double fPX, double fPY); inline void Scale (double fS); @@ -221,6 +222,11 @@ inline double Vector2D::operator* (const Vector2D &rclVct) const return (fX * rclVct.fX) + (fY * rclVct.fY); } +inline Vector2D Vector2D::operator/ (const double x) const +{ + return Vector2D(fX / x, fY / x); +} + inline void Vector2D::Scale (double fS) { fX *= fS; diff --git a/src/Mod/Complete/Gui/Workbench.cpp b/src/Mod/Complete/Gui/Workbench.cpp index ebc9b06b7..a67b47f9d 100644 --- a/src/Mod/Complete/Gui/Workbench.cpp +++ b/src/Mod/Complete/Gui/Workbench.cpp @@ -241,6 +241,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const geom->setCommand("Sketcher geometries"); *geom << "Sketcher_CreatePoint" << "Sketcher_CreateArc" + << "Sketcher_Create3PointArc" << "Sketcher_CreateCircle" << "Sketcher_CreateLine" << "Sketcher_CreatePolyline" @@ -503,6 +504,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "Separator" << "Sketcher_CreatePoint" << "Sketcher_CreateArc" + << "Sketcher_Create3PointArc" << "Sketcher_CreateCircle" << "Sketcher_CreateLine" << "Sketcher_CreatePolyline" diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 72573757f..efc22d6ca 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -167,6 +167,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const geom->setCommand("Sketcher geometries"); *geom << "Sketcher_CreatePoint" << "Sketcher_CreateArc" + << "Sketcher_Create3PointArc" << "Sketcher_CreateCircle" << "Sketcher_CreateLine" << "Sketcher_CreatePolyline" @@ -260,6 +261,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const geom->setCommand("Sketcher geometries"); *geom << "Sketcher_CreatePoint" << "Sketcher_CreateArc" + << "Sketcher_Create3PointArc" << "Sketcher_CreateCircle" << "Sketcher_CreateLine" << "Sketcher_CreatePolyline" diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 88a5349fa..c0b437bf3 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -49,6 +49,40 @@ using namespace SketcherGui; /* helper functions ======================================================*/ +// Return counter-clockwise angle from horizontal out of p1 to p2 in radians. +double GetPointAngle (const Base::Vector2D &p1, const Base::Vector2D &p2) +{ + double dX = p2.fX - p1.fX; + double dY = p2.fY - p1.fY; + return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2*M_PI; +} + +/* +Find the centerpoint of a circle drawn through any 3 points: + +Given points p1-3, draw 2 lines: S12 and S23 which each connect two points. From the +midpoint of each line, draw a perpendicular line (S12p/S23p) across the circle. These +lines will cross at the centerpoint. + +Mathematically, line S12 will have a slope of m12 which can be determined. Therefore, +the slope m12p is -1/m12. Line S12p will have an equation of y = m12p*x + b12p. b12p can +be solved for using the midpoint of the line. This can be done for both lines. Since +both S12p and S23p cross at the centerpoint, solving the two equations together will give +the location of the centerpoint. +*/ +Base::Vector2D GetCircleCenter (const Base::Vector2D &p1, const Base::Vector2D &p2, const Base::Vector2D &p3) +{ + double m12p = (p1.fX - p2.fX) / (p2.fY - p1.fY); + double m23p = (p2.fX - p3.fX) / (p3.fY - p2.fY); + double x = 1/( 2*(m12p - m23p) ) * ( (pow(p3.fX, 2) - pow(p2.fX, 2)) / (p3.fY - p2.fY) - + (pow(p2.fX, 2) - pow(p1.fX, 2)) / (p2.fY - p1.fY) + + p3.fY - p1.fY ); + double y = m12p * ( x - (p1.fX + p2.fX)/2 ) + (p1.fY + p2.fY)/2; + + return Base::Vector2D(x, y); +} + + void ActivateHandler(Gui::Document *doc,DrawSketchHandler *handler) { if (doc) { @@ -1214,6 +1248,261 @@ bool CmdSketcherCreateArc::isActive(void) return isCreateGeoActive(getActiveGuiDocument()); } + +// ====================================================================================== + +/* XPM */ +static const char *cursor_create3pointarc[]={ +"32 32 3 1", +"+ c white", +"# c red", +". c None", +"......+...........###...........", +"......+...........#.#...........", +"......+...........###...........", +"......+..............##.........", +"......+...............##........", +".......................#........", +"+++++...+++++...........#.......", +"........................##......", +"......+..................#......", +"......+..................#......", +"......+...................#.....", +"......+...................#.....", +"......+...................#.....", +"..........................#.....", +"..........................#.....", +"..........................#.....", +"..........................#.....", +".........................#......", +".......................###......", +".......................#.#......", +".......................###......", +"...###.................#........", +"...#.#................#.........", +"...###...............#..........", +"......##...........##...........", +".......###.......##.............", +"..........#######...............", +"................................", +"................................", +"................................", +"................................", +"................................"}; + +class DrawSketchHandler3PointArc : public DrawSketchHandler +{ +public: + DrawSketchHandler3PointArc() + : Mode(STATUS_SEEK_First),EditCurve(2){} + virtual ~DrawSketchHandler3PointArc(){} + /// mode table + enum SelectMode { + STATUS_SEEK_First, /**< enum value ----. */ + STATUS_SEEK_Second, /**< enum value ----. */ + STATUS_SEEK_Third, /**< enum value ----. */ + STATUS_End + }; + + virtual void activated(ViewProviderSketch *sketchgui) + { + setCursor(QPixmap(cursor_create3pointarc),7,7); + } + + virtual void mouseMove(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_SEEK_First) { + setPositionText(onSketchPos); + if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) { + renderSuggestConstraintsCursor(sugConstr1); + return; + } + } + else if (Mode==STATUS_SEEK_Second) { + CenterPoint = EditCurve[0] = (onSketchPos - FirstPoint)/2 + FirstPoint; + EditCurve[1] = EditCurve[33] = onSketchPos; + radius = (onSketchPos - CenterPoint).Length(); + double lineAngle = GetPointAngle(CenterPoint, onSketchPos); + + // Build a 32 point circle ignoring already constructed points + for (int i=1; i <= 32; i++) { + // Start at current angle + double angle = (i-1)*2*M_PI/32.0 + lineAngle; // N point closed circle has N segments + if (i != 1 && i != 17 ) { + EditCurve[i] = Base::Vector2D(CenterPoint.fX + radius*cos(angle), + CenterPoint.fY + radius*sin(angle)); + } + } + + // Display radius and start angle + // This lineAngle will report counter-clockwise from +X, not relatively + SbString text; + text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) lineAngle * 180 / M_PI); + setPositionText(onSketchPos, text); + + sketchgui->drawEdit(EditCurve); + if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f))) { + renderSuggestConstraintsCursor(sugConstr2); + return; + } + } + else if (Mode==STATUS_SEEK_Third) { + /* + Centerline inverts when the arc flips sides. Easily taken care of by replacing + centerline with a point. It happens because the direction the curve is being drawn + reverses. + */ + CenterPoint = EditCurve[30] = GetCircleCenter(FirstPoint, SecondPoint, onSketchPos); + radius = (SecondPoint - CenterPoint).Length(); + + double angle1 = GetPointAngle(CenterPoint, FirstPoint); + double angle2 = GetPointAngle(CenterPoint, SecondPoint); + double angle3 = GetPointAngle(CenterPoint, onSketchPos); + + // angle3 == angle1 or angle2 shouldn't be allowed but not sure...catch/try? + // Point 3 is between Point 1 and 2 + if ( angle3 > min(angle1, angle2) && angle3 < max(angle1, angle2) ) { + EditCurve[0] = angle2 > angle1 ? FirstPoint : SecondPoint; + EditCurve[29] = angle2 > angle1 ? SecondPoint : FirstPoint; + startAngle = min(angle1, angle2); + endAngle = max(angle1, angle2); + arcAngle = endAngle - startAngle; + } + // Point 3 is not between Point 1 and 2 + else { + EditCurve[0] = angle2 > angle1 ? SecondPoint : FirstPoint; + EditCurve[29] = angle2 > angle1 ? FirstPoint : SecondPoint; + startAngle = max(angle1, angle2); + endAngle = min(angle1, angle2); + arcAngle = 2*M_PI - (startAngle - endAngle); + } + + // Build a 30 point circle ignoring already constructed points + for (int i=1; i <= 28; i++) { + double angle = startAngle + i*arcAngle/29.0; // N point arc has N-1 segments + EditCurve[i] = Base::Vector2D(CenterPoint.fX + radius*cos(angle), + CenterPoint.fY + radius*sin(angle)); + } + + SbString text; + text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) arcAngle * 180 / M_PI); + setPositionText(onSketchPos, text); + + sketchgui->drawEdit(EditCurve); + if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.0,0.0))) { + renderSuggestConstraintsCursor(sugConstr3); + return; + } + } + applyCursor(); + } + + virtual bool pressButton(Base::Vector2D onSketchPos) + { + if (Mode==STATUS_SEEK_First){ + // 32 point curve + center + endpoint + EditCurve.resize(34); + // 17 is circle halfway point (1+32/2) + FirstPoint = EditCurve[17] = onSketchPos; + + Mode = STATUS_SEEK_Second; + } + else if (Mode==STATUS_SEEK_Second){ + // 30 point arc and center point + EditCurve.resize(31); + SecondPoint = onSketchPos; + + Mode = STATUS_SEEK_Third; + } + else { + EditCurve.resize(30); + + sketchgui->drawEdit(EditCurve); + applyCursor(); + Mode = STATUS_End; + } + + return true; + } + + virtual bool releaseButton(Base::Vector2D onSketchPos) + { + // Need to look at. rx might need fixing. + if (Mode==STATUS_End) { + unsetCursor(); + resetPositionText(); + Gui::Command::openCommand("Add sketch arc"); + Gui::Command::doCommand(Gui::Command::Doc, + "App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle" + "(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f)," + "%f,%f))", + sketchgui->getObject()->getNameInDocument(), + CenterPoint.fX, CenterPoint.fY, radius, + startAngle, endAngle); + + Gui::Command::commitCommand(); + Gui::Command::updateActive(); + + // Auto Constraint center point + if (sugConstr1.size() > 0) { + createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::mid); + sugConstr1.clear(); + } + + // Auto Constraint first picked point + if (sugConstr2.size() > 0) { + createAutoConstraints(sugConstr2, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::start : Sketcher::end ); + sugConstr2.clear(); + } + + // Auto Constraint second picked point + if (sugConstr3.size() > 0) { + createAutoConstraints(sugConstr3, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::end : Sketcher::start); + sugConstr3.clear(); + } + + EditCurve.clear(); + sketchgui->drawEdit(EditCurve); + sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider + } + return true; + } +protected: + SelectMode Mode; + std::vector EditCurve; + Base::Vector2D CenterPoint, FirstPoint, SecondPoint; + double radius, startAngle, endAngle, arcAngle; + std::vector sugConstr1, sugConstr2, sugConstr3; +}; + + +DEF_STD_CMD_A(CmdSketcherCreate3PointArc); + +CmdSketcherCreate3PointArc::CmdSketcherCreate3PointArc() + : Command("Sketcher_Create3PointArc") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("Create a 3 point arc"); + sToolTipText = QT_TR_NOOP("Create an arc in the sketch"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Sketcher_Create3PointArc"; + eType = ForEdit; +} + +void CmdSketcherCreate3PointArc::activated(int iMsg) +{ + ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointArc() ); +} + +bool CmdSketcherCreate3PointArc::isActive(void) +{ + return isCreateGeoActive(getActiveGuiDocument()); +} + + + // ====================================================================================== /* XPM */ @@ -2135,6 +2424,7 @@ void CreateSketcherCommandsCreateGeo(void) rcCmdMgr.addCommand(new CmdSketcherCreatePoint()); rcCmdMgr.addCommand(new CmdSketcherCreateArc()); + rcCmdMgr.addCommand(new CmdSketcherCreate3PointArc()); rcCmdMgr.addCommand(new CmdSketcherCreateCircle()); rcCmdMgr.addCommand(new CmdSketcherCreateLine()); rcCmdMgr.addCommand(new CmdSketcherCreatePolyline()); diff --git a/src/Mod/Sketcher/Gui/Resources/Makefile.am b/src/Mod/Sketcher/Gui/Resources/Makefile.am index abb54c947..2bb55a36f 100644 --- a/src/Mod/Sketcher/Gui/Resources/Makefile.am +++ b/src/Mod/Sketcher/Gui/Resources/Makefile.am @@ -61,6 +61,7 @@ icons/Constraint_PointOnObject.svg \ icons/Sketcher_ConstrainLock.svg \ icons/Sketcher_ConstrainVertical.svg \ icons/Sketcher_CreateArc.svg \ + icons/Sketcher_Create3PointArc.svg \ icons/Sketcher_CreateCircle.svg \ icons/Sketcher_CreateLine.svg \ icons/Sketcher_CreatePoint.svg \ diff --git a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc index 6a2191e9e..391f5d02e 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -51,6 +51,7 @@ icons/Sketcher_ConstrainParallel.svg icons/Sketcher_ConstrainVertical.svg icons/Sketcher_CreateArc.svg + icons/Sketcher_Create3PointArc.svg icons/Sketcher_CreateCircle.svg icons/Sketcher_CreateLine.svg icons/Sketcher_CreatePoint.svg diff --git a/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_Create3PointArc.svg b/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_Create3PointArc.svg new file mode 100644 index 000000000..c454a530e --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_Create3PointArc.svg @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index 3d6b36d4c..828de4714 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -62,6 +62,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const geom->setCommand("Sketcher geometries"); *geom << "Sketcher_CreatePoint" << "Sketcher_CreateArc" + << "Sketcher_Create3PointArc" << "Sketcher_CreateCircle" << "Sketcher_CreateLine" << "Sketcher_CreatePolyline" @@ -122,6 +123,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const geom->setCommand("Sketcher geometries"); *geom << "Sketcher_CreatePoint" << "Sketcher_CreateArc" + << "Sketcher_Create3PointArc" << "Sketcher_CreateCircle" << "Sketcher_CreateLine" << "Sketcher_CreatePolyline"