Split DSHs from CommandCreateGeo
This commit is contained in:
@@ -64,6 +64,25 @@ SET(SketcherGui_SRCS
|
||||
AutoConstraint.h
|
||||
Utils.h
|
||||
Utils.cpp
|
||||
DrawSketchHandlerLine.h
|
||||
DrawSketchHandlerRectangle.h
|
||||
DrawSketchHandlerPolygon.h
|
||||
DrawSketchHandlerLineSet.h
|
||||
DrawSketchHandlerCircle.h
|
||||
DrawSketchHandlerEllipse.h
|
||||
DrawSketchHandlerArc.h
|
||||
DrawSketchHandlerArcOfEllipse.h
|
||||
DrawSketchHandlerArcOfHyperbola.h
|
||||
DrawSketchHandlerArcOfParabola.h
|
||||
DrawSketchHandlerBSpline.h
|
||||
DrawSketchHandlerPoint.h
|
||||
DrawSketchHandlerFillet.h
|
||||
DrawSketchHandlerTrimming.h
|
||||
DrawSketchHandlerExtend.h
|
||||
DrawSketchHandlerSplitting.h
|
||||
DrawSketchHandlerExternal.h
|
||||
DrawSketchHandlerCarbonCopy.h
|
||||
DrawSketchHandlerSlot.h
|
||||
CommandCreateGeo.cpp
|
||||
CommandConstraints.h
|
||||
CommandConstraints.cpp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
485
src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h
Normal file
485
src/Mod/Sketcher/Gui/DrawSketchHandlerArc.h
Normal file
@@ -0,0 +1,485 @@
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerArc_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerArc_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerArc : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerArc()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, EditCurve(2)
|
||||
, rx(0), ry(0)
|
||||
, startAngle(0)
|
||||
, endAngle(0)
|
||||
, arcAngle(0)
|
||||
{
|
||||
}
|
||||
virtual ~DrawSketchHandlerArc(){}
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_SEEK_Third, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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) {
|
||||
double dx_ = onSketchPos.x - EditCurve[0].x;
|
||||
double dy_ = onSketchPos.y - EditCurve[0].y;
|
||||
for (int i=0; i < 16; i++) {
|
||||
double angle = i*M_PI/16.0;
|
||||
double dx = dx_ * cos(angle) + dy_ * sin(angle);
|
||||
double dy = -dx_ * sin(angle) + dy_ * cos(angle);
|
||||
EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + dx, EditCurve[0].y + dy);
|
||||
EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - dx, EditCurve[0].y - dy);
|
||||
}
|
||||
EditCurve[33] = EditCurve[1];
|
||||
|
||||
// Display radius and start angle
|
||||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
float angle = atan2f(dy_ , dx_);
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fdeg)", radius, angle * 180 / M_PI);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Third) {
|
||||
double angle1 = atan2(onSketchPos.y - CenterPoint.y,
|
||||
onSketchPos.x - CenterPoint.x) - startAngle;
|
||||
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
|
||||
arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
|
||||
for (int i=1; i <= 29; i++) {
|
||||
double angle = i*arcAngle/29.0;
|
||||
double dx = rx * cos(angle) - ry * sin(angle);
|
||||
double dy = rx * sin(angle) + ry * cos(angle);
|
||||
EditCurve[i] = Base::Vector2d(CenterPoint.x + dx, CenterPoint.y + dy);
|
||||
}
|
||||
|
||||
// Display radius and arc angle
|
||||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fdeg)", radius, arcAngle * 180 / M_PI);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.0,0.0))) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
CenterPoint = onSketchPos;
|
||||
EditCurve.resize(34);
|
||||
EditCurve[0] = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second){
|
||||
EditCurve.resize(31);
|
||||
EditCurve[0] = onSketchPos;
|
||||
EditCurve[30] = CenterPoint;
|
||||
rx = EditCurve[0].x - CenterPoint.x;
|
||||
ry = EditCurve[0].y - CenterPoint.y;
|
||||
startAngle = atan2(ry, rx);
|
||||
arcAngle = 0.;
|
||||
Mode = STATUS_SEEK_Third;
|
||||
}
|
||||
else {
|
||||
EditCurve.resize(30);
|
||||
double angle1 = atan2(onSketchPos.y - CenterPoint.y,
|
||||
onSketchPos.x - CenterPoint.x) - startAngle;
|
||||
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
|
||||
arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
|
||||
if (arcAngle > 0)
|
||||
endAngle = startAngle + arcAngle;
|
||||
else {
|
||||
endAngle = startAngle;
|
||||
startAngle += arcAngle;
|
||||
}
|
||||
|
||||
drawEdit(EditCurve);
|
||||
applyCursor();
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode==STATUS_End) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfCircle"
|
||||
"(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f),%s)",
|
||||
CenterPoint.x, CenterPoint.y, sqrt(rx*rx + ry*ry),
|
||||
startAngle, endAngle,
|
||||
geometryCreationMode==Construction?"True":"False"); //arcAngle > 0 ? 0 : 1);
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add arc: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
// Auto Constraint center point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// Auto Constraint first picked point
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::PointPos::start : Sketcher::PointPos::end );
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
// Auto Constraint second picked point
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::PointPos::end : Sketcher::PointPos::start);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Arc");
|
||||
}
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
Base::Vector2d CenterPoint;
|
||||
double rx, ry, startAngle, endAngle, arcAngle;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
|
||||
};
|
||||
|
||||
class DrawSketchHandler3PointArc : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandler3PointArc()
|
||||
: Mode(STATUS_SEEK_First), EditCurve(2)
|
||||
, radius(0), startAngle(0)
|
||||
, endAngle(0), arcAngle(0)
|
||||
, arcPos1(Sketcher::PointPos::none)
|
||||
, arcPos2(Sketcher::PointPos::none)
|
||||
{
|
||||
}
|
||||
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 mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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.x + radius*cos(angle),
|
||||
CenterPoint.y + 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);
|
||||
|
||||
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.
|
||||
*/
|
||||
try {
|
||||
CenterPoint = EditCurve[30] = Part::Geom2dCircle::getCircleCenter(FirstPoint, SecondPoint, onSketchPos);
|
||||
|
||||
radius = (SecondPoint - CenterPoint).Length();
|
||||
|
||||
double angle1 = GetPointAngle(CenterPoint, FirstPoint);
|
||||
double angle2 = GetPointAngle(CenterPoint, SecondPoint);
|
||||
double angle3 = GetPointAngle(CenterPoint, onSketchPos);
|
||||
|
||||
// Always build arc counter-clockwise
|
||||
// Point 3 is between Point 1 and 2
|
||||
if ( angle3 > min(angle1, angle2) && angle3 < max(angle1, angle2) ) {
|
||||
if (angle2 > angle1) {
|
||||
EditCurve[0] = FirstPoint;
|
||||
EditCurve[29] = SecondPoint;
|
||||
arcPos1 = Sketcher::PointPos::start;
|
||||
arcPos2 = Sketcher::PointPos::end;
|
||||
}
|
||||
else {
|
||||
EditCurve[0] = SecondPoint;
|
||||
EditCurve[29] = FirstPoint;
|
||||
arcPos1 = Sketcher::PointPos::end;
|
||||
arcPos2 = Sketcher::PointPos::start;
|
||||
}
|
||||
startAngle = min(angle1, angle2);
|
||||
endAngle = max(angle1, angle2);
|
||||
arcAngle = endAngle - startAngle;
|
||||
}
|
||||
// Point 3 is not between Point 1 and 2
|
||||
else {
|
||||
if (angle2 > angle1) {
|
||||
EditCurve[0] = SecondPoint;
|
||||
EditCurve[29] = FirstPoint;
|
||||
arcPos1 = Sketcher::PointPos::end;
|
||||
arcPos2 = Sketcher::PointPos::start;
|
||||
}
|
||||
else {
|
||||
EditCurve[0] = FirstPoint;
|
||||
EditCurve[29] = SecondPoint;
|
||||
arcPos1 = Sketcher::PointPos::start;
|
||||
arcPos2 = Sketcher::PointPos::end;
|
||||
}
|
||||
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.x + radius*cos(angle),
|
||||
CenterPoint.y + radius*sin(angle));
|
||||
}
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) arcAngle * 180 / M_PI);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.0,0.0),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(Base::ValueError &e) {
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
applyCursor();
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
// Need to look at. rx might need fixing.
|
||||
if (Mode==STATUS_End) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfCircle"
|
||||
"(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f),%s)",
|
||||
CenterPoint.x, CenterPoint.y, radius,
|
||||
startAngle, endAngle,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add arc: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
// Auto Constraint first picked point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), arcPos1);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// Auto Constraint second picked point
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(), arcPos2);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
// Auto Constraint third picked point
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, getHighestCurveIndex(), Sketcher::PointPos::none);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_3PointArc");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
Base::Vector2d CenterPoint, FirstPoint, SecondPoint;
|
||||
double radius, startAngle, endAngle, arcAngle;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
|
||||
Sketcher::PointPos arcPos1, arcPos2;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerArc_H
|
||||
|
||||
341
src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h
Normal file
341
src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfEllipse.h
Normal file
@@ -0,0 +1,341 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerArcOfEllipse_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerArcOfEllipse_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerArcOfEllipse : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerArcOfEllipse()
|
||||
: Mode(STATUS_SEEK_First), EditCurve(34)
|
||||
, rx(0), ry(0), startAngle(0), endAngle(0)
|
||||
, arcAngle(0), arcAngle_t(0) {}
|
||||
|
||||
virtual ~DrawSketchHandlerArcOfEllipse() = default;
|
||||
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_SEEK_Third, /**< enum value ----. */
|
||||
STATUS_SEEK_Fourth, /**< enum value ----. */
|
||||
STATUS_Close
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) { // TODO: ellipse prio 1
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second) {
|
||||
double rx0 = onSketchPos.x - EditCurve[0].x;
|
||||
double ry0 = onSketchPos.y - EditCurve[0].y;
|
||||
for (int i=0; i < 16; i++) {
|
||||
double angle = i*M_PI/16.0;
|
||||
double rx1 = rx0 * cos(angle) + ry0 * sin(angle);
|
||||
double ry1 = -rx0 * sin(angle) + ry0 * cos(angle);
|
||||
EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1);
|
||||
EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - rx1, EditCurve[0].y - ry1);
|
||||
}
|
||||
EditCurve[33] = EditCurve[1];
|
||||
|
||||
// Display radius for user
|
||||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", radius,radius);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centerPoint,
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Third) {
|
||||
// angle between the major axis of the ellipse and the X axis
|
||||
double a = (EditCurve[1]-EditCurve[0]).Length();
|
||||
double phi = atan2(EditCurve[1].y-EditCurve[0].y,EditCurve[1].x-EditCurve[0].x);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double angleatpoint = acos((onSketchPos.x-EditCurve[0].x+(onSketchPos.y-EditCurve[0].y)*tan(phi))/(a*(cos(phi)+tan(phi)*sin(phi))));
|
||||
double b=(onSketchPos.y-EditCurve[0].y-a*cos(angleatpoint)*sin(phi))/(sin(angleatpoint)*cos(phi));
|
||||
|
||||
for (int i=1; i < 16; i++) {
|
||||
double angle = i*M_PI/16.0;
|
||||
double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi);
|
||||
double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi);
|
||||
EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1);
|
||||
EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - rx1, EditCurve[0].y - ry1);
|
||||
}
|
||||
EditCurve[33] = EditCurve[1];
|
||||
EditCurve[17] = EditCurve[16];
|
||||
|
||||
// Display radius for user
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", a, b);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Fourth) { // here we differ from ellipse creation
|
||||
// angle between the major axis of the ellipse and the X axis
|
||||
double a = (axisPoint-centerPoint).Length();
|
||||
double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double angleatpoint = acos((startingPoint.x-centerPoint.x+(startingPoint.y-centerPoint.y)*tan(phi))/(a*(cos(phi)+tan(phi)*sin(phi))));
|
||||
double b=abs((startingPoint.y-centerPoint.y-a*cos(angleatpoint)*sin(phi))/(sin(angleatpoint)*cos(phi)));
|
||||
|
||||
double rxs = startingPoint.x - centerPoint.x;
|
||||
double rys = startingPoint.y - centerPoint.y;
|
||||
startAngle = atan2(a*(rys*cos(phi)-rxs*sin(phi)), b*(rxs*cos(phi)+rys*sin(phi))); // eccentric anomaly angle
|
||||
|
||||
double angle1 = atan2(a*((onSketchPos.y - centerPoint.y)*cos(phi)-(onSketchPos.x - centerPoint.x)*sin(phi)),
|
||||
b*((onSketchPos.x - centerPoint.x)*cos(phi)+(onSketchPos.y - centerPoint.y)*sin(phi)))- startAngle;
|
||||
|
||||
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
|
||||
arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
|
||||
|
||||
for (int i=0; i < 34; i++) {
|
||||
double angle = startAngle+i*arcAngle/34.0;
|
||||
double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi);
|
||||
double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi);
|
||||
EditCurve[i] = Base::Vector2d(centerPoint.x + rx1, centerPoint.y + ry1);
|
||||
}
|
||||
// EditCurve[33] = EditCurve[1];
|
||||
// EditCurve[17] = EditCurve[16];
|
||||
|
||||
// Display radii and angle for user
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR,%.1fdeg)", a, b, arcAngle * 180 / M_PI);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
centerPoint = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Second) {
|
||||
EditCurve[1] = onSketchPos;
|
||||
axisPoint = onSketchPos;
|
||||
Mode = STATUS_SEEK_Third;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Third) {
|
||||
startingPoint = onSketchPos;
|
||||
arcAngle = 0.;
|
||||
arcAngle_t= 0.;
|
||||
Mode = STATUS_SEEK_Fourth;
|
||||
}
|
||||
else { // Fourth
|
||||
endPoint = onSketchPos;
|
||||
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode==STATUS_Close) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
// angle between the major axis of the ellipse and the X axisEllipse
|
||||
double a = (axisPoint-centerPoint).Length();
|
||||
double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double angleatpoint = acos((startingPoint.x-centerPoint.x+(startingPoint.y-centerPoint.y)*tan(phi))/(a*(cos(phi)+tan(phi)*sin(phi))));
|
||||
double b=abs((startingPoint.y-centerPoint.y-a*cos(angleatpoint)*sin(phi))/(sin(angleatpoint)*cos(phi)));
|
||||
|
||||
double angle1 = atan2(a*((endPoint.y - centerPoint.y)*cos(phi)-(endPoint.x - centerPoint.x)*sin(phi)),
|
||||
b*((endPoint.x - centerPoint.x)*cos(phi)+(endPoint.y - centerPoint.y)*sin(phi)))- startAngle;
|
||||
|
||||
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
|
||||
arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
|
||||
|
||||
bool isOriginalArcCCW=true;
|
||||
|
||||
if (arcAngle > 0)
|
||||
endAngle = startAngle + arcAngle;
|
||||
else {
|
||||
endAngle = startAngle;
|
||||
startAngle += arcAngle;
|
||||
isOriginalArcCCW=false;
|
||||
}
|
||||
|
||||
Base::Vector2d majAxisDir,minAxisDir,minAxisPoint,majAxisPoint;
|
||||
// We always create a CCW ellipse, because we want our XY reference system to be in the +X +Y direction
|
||||
// Our normal will then always be in the +Z axis (local +Z axis of the sketcher)
|
||||
|
||||
if(a>b)
|
||||
{
|
||||
// force second semidiameter to be perpendicular to first semidiamater
|
||||
majAxisDir = axisPoint - centerPoint;
|
||||
Base::Vector2d perp(-majAxisDir.y,majAxisDir.x);
|
||||
perp.Normalize();
|
||||
perp.Scale(abs(b));
|
||||
minAxisPoint = centerPoint+perp;
|
||||
majAxisPoint = centerPoint+majAxisDir;
|
||||
}
|
||||
else {
|
||||
// force second semidiameter to be perpendicular to first semidiamater
|
||||
minAxisDir = axisPoint - centerPoint;
|
||||
Base::Vector2d perp(minAxisDir.y,-minAxisDir.x);
|
||||
perp.Normalize();
|
||||
perp.Scale(abs(b));
|
||||
majAxisPoint = centerPoint+perp;
|
||||
minAxisPoint = centerPoint+minAxisDir;
|
||||
endAngle += M_PI/2;
|
||||
startAngle += M_PI/2;
|
||||
phi-=M_PI/2;
|
||||
double t=a; a=b; b=t;//swap a,b
|
||||
}
|
||||
|
||||
int currentgeoid = getHighestCurveIndex();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of ellipse"));
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfEllipse"
|
||||
"(Part.Ellipse(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%f,%f),%s)",
|
||||
majAxisPoint.x, majAxisPoint.y,
|
||||
minAxisPoint.x, minAxisPoint.y,
|
||||
centerPoint.x, centerPoint.y,
|
||||
startAngle, endAngle,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
currentgeoid++;
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// add auto constraints for the center point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for arc
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for start of arc
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::start:Sketcher::PointPos::end);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for start of arc
|
||||
if (sugConstr4.size() > 0) {
|
||||
createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::end:Sketcher::PointPos::start);
|
||||
sugConstr4.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(34);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_ArcOfEllipse");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
Base::Vector2d centerPoint, axisPoint, startingPoint, endPoint;
|
||||
double rx, ry, startAngle, endAngle, arcAngle, arcAngle_t;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerArcOfEllipse_H
|
||||
|
||||
358
src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h
Normal file
358
src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfHyperbola.h
Normal file
@@ -0,0 +1,358 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerArcOfHyperbola_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerArcOfHyperbola_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerArcOfHyperbola : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerArcOfHyperbola()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, EditCurve(34)
|
||||
, arcAngle(0)
|
||||
, arcAngle_t(0) {}
|
||||
|
||||
virtual ~DrawSketchHandlerArcOfHyperbola() = default;
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_SEEK_Third, /**< enum value ----. */
|
||||
STATUS_SEEK_Fourth, /**< enum value ----. */
|
||||
STATUS_Close
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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) {
|
||||
EditCurve[1]= onSketchPos;
|
||||
|
||||
// Display radius for user
|
||||
float radius = (onSketchPos - centerPoint).Length();
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", radius,radius);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Third) {
|
||||
// angle between the major axis of the hyperbola and the X axis
|
||||
double a = (axisPoint-centerPoint).Length();
|
||||
double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double angleatpoint = acosh(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))/a);
|
||||
double b=(onSketchPos.y-centerPoint.y-a*cosh(angleatpoint)*sin(phi))/(sinh(angleatpoint)*cos(phi));
|
||||
|
||||
if(!boost::math::isnan(b)){
|
||||
for (int i=15; i >= -15; i--) {
|
||||
// P(U) = O + MajRad*Cosh(U)*XDir + MinRad*Sinh(U)*YDir
|
||||
//double angle = i*M_PI/16.0;
|
||||
double angle=i*angleatpoint/15;
|
||||
double rx = a * cosh(angle) * cos(phi) - b * sinh(angle) * sin(phi);
|
||||
double ry = a * cosh(angle) * sin(phi) + b * sinh(angle) * cos(phi);
|
||||
EditCurve[15+i] = Base::Vector2d(centerPoint.x + rx, centerPoint.y + ry);
|
||||
}
|
||||
|
||||
// Display radius for user
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", a, b);
|
||||
setPositionText(onSketchPos, text);
|
||||
}
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Fourth) {
|
||||
// angle between the major axis of the hyperbola and the X axis
|
||||
double a = (axisPoint-centerPoint).Length();
|
||||
double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double angleatstartingpoint = acosh(((startingPoint.x-centerPoint.x)*cos(phi)+(startingPoint.y-centerPoint.y)*sin(phi))/a);
|
||||
double b=(startingPoint.y-centerPoint.y-a*cosh(angleatstartingpoint)*sin(phi))/(sinh(angleatstartingpoint)*cos(phi));
|
||||
|
||||
double startAngle = angleatstartingpoint;
|
||||
|
||||
//double angleatpoint = acosh(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))/a);
|
||||
|
||||
double angleatpoint = atanh( (((onSketchPos.y-centerPoint.y)*cos(phi)-(onSketchPos.x-centerPoint.x)*sin(phi))*a) /
|
||||
(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))*b) );
|
||||
|
||||
/*double angle1 = angleatpoint - startAngle;
|
||||
|
||||
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
|
||||
arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;*/
|
||||
|
||||
arcAngle = angleatpoint - startAngle;
|
||||
|
||||
//if(!boost::math::isnan(angle1) && !boost::math::isnan(angle2)){
|
||||
if (!boost::math::isnan(arcAngle)) {
|
||||
EditCurve.resize(33);
|
||||
for (int i=0; i < 33; i++) {
|
||||
// P(U) = O + MajRad*Cosh(U)*XDir + MinRad*Sinh(U)*YDir
|
||||
//double angle=i*angleatpoint/16;
|
||||
double angle = startAngle+i*arcAngle/32.0;
|
||||
double rx = a * cosh(angle) * cos(phi) - b * sinh(angle) * sin(phi);
|
||||
double ry = a * cosh(angle) * sin(phi) + b * sinh(angle) * cos(phi);
|
||||
EditCurve[i] = Base::Vector2d(centerPoint.x + rx, centerPoint.y + ry);
|
||||
}
|
||||
|
||||
// Display radius for user
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", a, b);
|
||||
setPositionText(onSketchPos, text);
|
||||
}
|
||||
else {
|
||||
arcAngle=0.;
|
||||
}
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
centerPoint = onSketchPos;
|
||||
EditCurve.resize(2);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Second) {
|
||||
EditCurve[1] = onSketchPos;
|
||||
axisPoint = onSketchPos;
|
||||
EditCurve.resize(31);
|
||||
Mode = STATUS_SEEK_Third;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Third) {
|
||||
startingPoint = onSketchPos;
|
||||
arcAngle = 0.;
|
||||
arcAngle_t= 0.;
|
||||
Mode = STATUS_SEEK_Fourth;
|
||||
}
|
||||
else { // Fourth
|
||||
endPoint = onSketchPos;
|
||||
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d /*onSketchPos*/) override
|
||||
{
|
||||
if (Mode==STATUS_Close) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
|
||||
// angle between the major axis of the hyperbola and the X axis
|
||||
double a = (axisPoint-centerPoint).Length();
|
||||
double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double angleatstartingpoint = acosh(((startingPoint.x-centerPoint.x)*cos(phi)+(startingPoint.y-centerPoint.y)*sin(phi))/a);
|
||||
double b=(startingPoint.y-centerPoint.y-a*cosh(angleatstartingpoint)*sin(phi))/(sinh(angleatstartingpoint)*cos(phi));
|
||||
|
||||
double startAngle = angleatstartingpoint;
|
||||
|
||||
//double angleatpoint = acosh(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))/a);
|
||||
|
||||
double endAngle = atanh( (((endPoint.y-centerPoint.y)*cos(phi)-(endPoint.x-centerPoint.x)*sin(phi))*a) /
|
||||
(((endPoint.x-centerPoint.x)*cos(phi)+(endPoint.y-centerPoint.y)*sin(phi))*b) );
|
||||
|
||||
if (boost::math::isnan(startAngle) || boost::math::isnan(endAngle)) {
|
||||
sketchgui->purgeHandler();
|
||||
Base::Console().Error("Cannot create arc of hyperbola from invalid angles, try again!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool isOriginalArcCCW=true;
|
||||
|
||||
if (arcAngle > 0)
|
||||
endAngle = startAngle + arcAngle;
|
||||
else {
|
||||
endAngle = startAngle;
|
||||
startAngle += arcAngle;
|
||||
isOriginalArcCCW=false;
|
||||
}
|
||||
|
||||
Base::Vector2d majAxisDir,minAxisDir,minAxisPoint,majAxisPoint;
|
||||
// We always create a CCW hyperbola, because we want our XY reference system to be in the +X +Y direction
|
||||
// Our normal will then always be in the +Z axis (local +Z axis of the sketcher)
|
||||
|
||||
if(a>b)
|
||||
{
|
||||
// force second semidiameter to be perpendicular to first semidiamater
|
||||
majAxisDir = axisPoint - centerPoint;
|
||||
Base::Vector2d perp(-majAxisDir.y,majAxisDir.x);
|
||||
perp.Normalize();
|
||||
perp.Scale(abs(b));
|
||||
minAxisPoint = centerPoint+perp;
|
||||
majAxisPoint = centerPoint+majAxisDir;
|
||||
}
|
||||
else {
|
||||
// force second semidiameter to be perpendicular to first semidiamater
|
||||
minAxisDir = axisPoint - centerPoint;
|
||||
Base::Vector2d perp(minAxisDir.y,-minAxisDir.x);
|
||||
perp.Normalize();
|
||||
perp.Scale(abs(b));
|
||||
majAxisPoint = centerPoint+perp;
|
||||
minAxisPoint = centerPoint+minAxisDir;
|
||||
endAngle += M_PI/2;
|
||||
startAngle += M_PI/2;
|
||||
}
|
||||
|
||||
int currentgeoid = getHighestCurveIndex();
|
||||
|
||||
try {
|
||||
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of hyperbola"));
|
||||
|
||||
//Add arc of hyperbola, point and constrain point as focus2. We add focus2 for it to balance
|
||||
//the intrinsic focus1, in order to balance out the intrinsic invisible focus1 when AOE is
|
||||
//dragged by its center
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfHyperbola"
|
||||
"(Part.Hyperbola(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%f,%f),%s)",
|
||||
majAxisPoint.x, majAxisPoint.y,
|
||||
minAxisPoint.x, minAxisPoint.y,
|
||||
centerPoint.x, centerPoint.y,
|
||||
startAngle, endAngle,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
currentgeoid++;
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// add auto constraints for the center point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for arc
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for start of arc
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::start:Sketcher::PointPos::end);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for start of arc
|
||||
if (sugConstr4.size() > 0) {
|
||||
createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::end:Sketcher::PointPos::start);
|
||||
sugConstr4.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode = STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(34);
|
||||
applyCursor();
|
||||
/* It is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_ArcOfHyperbola");
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
Base::Vector2d centerPoint, axisPoint, startingPoint, endPoint;
|
||||
double arcAngle, arcAngle_t;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerArcOfHyperbola_H
|
||||
|
||||
312
src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfParabola.h
Normal file
312
src/Mod/Sketcher/Gui/DrawSketchHandlerArcOfParabola.h
Normal file
@@ -0,0 +1,312 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerArcOfParabola_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerArcOfParabola_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerArcOfParabola : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerArcOfParabola()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, EditCurve(34)
|
||||
, startAngle(0)
|
||||
, endAngle(0)
|
||||
, arcAngle(0)
|
||||
, arcAngle_t(0) {}
|
||||
|
||||
virtual ~DrawSketchHandlerArcOfParabola() = default;
|
||||
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_SEEK_Third, /**< enum value ----. */
|
||||
STATUS_SEEK_Fourth, /**< enum value ----. */
|
||||
STATUS_Close
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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) {
|
||||
EditCurve[1]= onSketchPos;
|
||||
|
||||
// Display radius for user
|
||||
float radius = (onSketchPos - focusPoint).Length();
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (F%.1f)", radius);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Third) {
|
||||
double focal = (axisPoint-focusPoint).Length();
|
||||
double phi = atan2(focusPoint.y-axisPoint.y,focusPoint.x-axisPoint.x);
|
||||
|
||||
// P(U) = O + U*U/(4.*F)*XDir + U*YDir
|
||||
//
|
||||
// pnt = Base::Vector3d(pnt0.x + angle * angle / 4 / focal * cos(phi) - angle * sin(phi),
|
||||
// pnt0.y + angle * angle / 4 / focal * sin(phi) + angle * cos(phi),
|
||||
// 0.f);
|
||||
|
||||
// This is the angle at cursor point
|
||||
double u =
|
||||
( cos(phi) * (onSketchPos.y - axisPoint.y) - (onSketchPos.x - axisPoint.x) * sin(phi));
|
||||
|
||||
for (int i=15; i >= -15; i--) {
|
||||
double angle=i*u/15;
|
||||
double rx = angle * angle / 4 / focal * cos(phi) - angle * sin(phi);
|
||||
double ry = angle * angle / 4 / focal * sin(phi) + angle * cos(phi);
|
||||
EditCurve[15+i] = Base::Vector2d(axisPoint.x + rx, axisPoint.y + ry);
|
||||
}
|
||||
|
||||
// Display radius for user
|
||||
SbString text;
|
||||
text.sprintf(" (F%.1f)", focal);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Fourth) {
|
||||
double focal = (axisPoint-focusPoint).Length();
|
||||
double phi = atan2(focusPoint.y-axisPoint.y,focusPoint.x-axisPoint.x);
|
||||
|
||||
// P(U) = O + U*U/(4.*F)*XDir + U*YDir
|
||||
//
|
||||
// pnt = Base::Vector3d(pnt0.x + angle * angle / 4 / focal * cos(phi) - angle * sin(phi),
|
||||
// pnt0.y + angle * angle / 4 / focal * sin(phi) + angle * cos(phi),
|
||||
// 0.f);
|
||||
|
||||
// This is the angle at starting point
|
||||
double ustartpoint =
|
||||
( cos(phi) * (startingPoint.y - axisPoint.y) - (startingPoint.x - axisPoint.x) * sin(phi));
|
||||
|
||||
double startValue = ustartpoint;
|
||||
|
||||
double u =
|
||||
( cos(phi) * (onSketchPos.y - axisPoint.y) - (onSketchPos.x - axisPoint.x) * sin(phi));
|
||||
|
||||
|
||||
arcAngle = u - startValue;
|
||||
|
||||
if (!boost::math::isnan(arcAngle)) {
|
||||
EditCurve.resize(33);
|
||||
for (std::size_t i=0; i < 33; i++) {
|
||||
double angle = startValue+i*arcAngle/32.0;
|
||||
double rx = angle * angle / 4 / focal * cos(phi) - angle * sin(phi);
|
||||
double ry = angle * angle / 4 / focal * sin(phi) + angle * cos(phi);
|
||||
EditCurve[i] = Base::Vector2d(axisPoint.x + rx, axisPoint.y + ry);
|
||||
}
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (F%.1f)", focal);
|
||||
setPositionText(onSketchPos, text);
|
||||
}
|
||||
else {
|
||||
arcAngle=0.;
|
||||
}
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
focusPoint = onSketchPos;
|
||||
EditCurve.resize(2);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Second) {
|
||||
EditCurve[1] = onSketchPos;
|
||||
axisPoint = onSketchPos;
|
||||
EditCurve.resize(31);
|
||||
Mode = STATUS_SEEK_Third;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Third) {
|
||||
startingPoint = onSketchPos;
|
||||
arcAngle = 0.;
|
||||
arcAngle_t= 0.;
|
||||
Mode = STATUS_SEEK_Fourth;
|
||||
}
|
||||
else { // Fourth
|
||||
endPoint = onSketchPos;
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d /*onSketchPos*/) override
|
||||
{
|
||||
if (Mode==STATUS_Close) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
double phi = atan2(focusPoint.y-axisPoint.y,focusPoint.x-axisPoint.x);
|
||||
|
||||
double ustartpoint =
|
||||
( cos(phi) * (startingPoint.y - axisPoint.y) - (startingPoint.x - axisPoint.x) * sin(phi));
|
||||
|
||||
double uendpoint =
|
||||
( cos(phi) * (endPoint.y - axisPoint.y) - (endPoint.x - axisPoint.x) * sin(phi));
|
||||
|
||||
double startAngle = ustartpoint;
|
||||
|
||||
double endAngle = uendpoint;
|
||||
|
||||
bool isOriginalArcCCW=true;
|
||||
|
||||
if (arcAngle > 0) {
|
||||
endAngle = startAngle + arcAngle;
|
||||
}
|
||||
else {
|
||||
endAngle = startAngle;
|
||||
startAngle += arcAngle;
|
||||
isOriginalArcCCW=false;
|
||||
}
|
||||
|
||||
int currentgeoid = getHighestCurveIndex();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of Parabola"));
|
||||
|
||||
//Add arc of parabola
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfParabola"
|
||||
"(Part.Parabola(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(0,0,1)),%f,%f),%s)",
|
||||
focusPoint.x, focusPoint.y,
|
||||
axisPoint.x, axisPoint.y,
|
||||
startAngle, endAngle,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
currentgeoid++;
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// add auto constraints for the focus point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, currentgeoid+1, Sketcher::PointPos::start);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for vertex point
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::mid);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for start of arc
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::start:Sketcher::PointPos::end);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for start of arc
|
||||
if (sugConstr4.size() > 0) {
|
||||
createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::end:Sketcher::PointPos::start);
|
||||
sugConstr4.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if (continuousMode) {
|
||||
// This code enables the continuous creation mode.
|
||||
Mode = STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(34);
|
||||
applyCursor();
|
||||
/* It is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else {
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_ArcOfParabola");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
Base::Vector2d focusPoint, axisPoint, startingPoint, endPoint;
|
||||
double startAngle, endAngle, arcAngle, arcAngle_t;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerArcOfParabola_H
|
||||
|
||||
509
src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h
Normal file
509
src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h
Normal file
@@ -0,0 +1,509 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerBSpline_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerBSpline_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerBSpline: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerBSpline(int constructionMethod)
|
||||
: Mode(STATUS_SEEK_FIRST_CONTROLPOINT)
|
||||
, MousePressMode(MOUSE_NOT_PRESSED)
|
||||
, ConstrMethod(constructionMethod)
|
||||
, SplineDegree(3)
|
||||
, IsClosed(false)
|
||||
{
|
||||
addSugConstraint();
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual ~DrawSketchHandlerBSpline() = default;
|
||||
|
||||
/// modes
|
||||
enum SELECT_MODE {
|
||||
STATUS_SEEK_FIRST_CONTROLPOINT,
|
||||
STATUS_SEEK_ADDITIONAL_CONTROLPOINTS,
|
||||
STATUS_CLOSE
|
||||
};
|
||||
|
||||
// TODO: this kind of behavior will be useful in a superclass
|
||||
// when LMB is pressed it's a transitional state so some undos can't be done
|
||||
// (like delete last pole)
|
||||
enum MOUSE_PRESS_MODE {
|
||||
MOUSE_PRESSED,
|
||||
MOUSE_NOT_PRESSED
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
prevCursorPosition = onSketchPos;
|
||||
|
||||
if (Mode==STATUS_SEEK_FIRST_CONTROLPOINT) {
|
||||
setPositionText(onSketchPos);
|
||||
|
||||
if (seekAutoConstraint(sugConstr.back(), onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr.back());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_ADDITIONAL_CONTROLPOINTS) {
|
||||
|
||||
drawControlPolygonToPosition(onSketchPos);
|
||||
|
||||
drawCursorToPosition(onSketchPos);
|
||||
|
||||
if (seekAutoConstraint(sugConstr.back(), onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr.back());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
prevCursorPosition = onSketchPos;
|
||||
|
||||
MousePressMode = MOUSE_PRESSED;
|
||||
|
||||
if (Mode == STATUS_SEEK_FIRST_CONTROLPOINT) {
|
||||
BSplinePoles.push_back(onSketchPos);
|
||||
|
||||
Mode = STATUS_SEEK_ADDITIONAL_CONTROLPOINTS;
|
||||
|
||||
// insert circle point for pole, defer internal alignment constraining.
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Pole circle"));
|
||||
|
||||
//Add pole
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),10),True)",
|
||||
BSplinePoles.back().x, BSplinePoles.back().y);
|
||||
|
||||
poleGeoIds.push_back(getHighestCurveIndex());
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Weight',%d,%f)) ",
|
||||
poleGeoIds.back(), 1.0 ); // First pole defaults to 1.0 weight
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Gui::Command::commitCommand();
|
||||
|
||||
//static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
// add auto constraints on pole
|
||||
if (sugConstr.back().size() > 0) {
|
||||
createAutoConstraints(sugConstr.back(), poleGeoIds.back(), Sketcher::PointPos::mid, false);
|
||||
}
|
||||
|
||||
static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
addSugConstraint();
|
||||
|
||||
}
|
||||
else if (Mode == STATUS_SEEK_ADDITIONAL_CONTROLPOINTS) {
|
||||
BSplinePoles.push_back(onSketchPos);
|
||||
|
||||
// check if coincident with first pole
|
||||
for(auto & ac : sugConstr.back()) {
|
||||
if( ac.Type == Sketcher::Coincident && ac.GeoId == poleGeoIds[0] && ac.PosId == Sketcher::PointPos::mid ) {
|
||||
IsClosed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClosed) {
|
||||
Mode = STATUS_CLOSE;
|
||||
|
||||
if (ConstrMethod == 1) { // if periodic we do not need the last pole
|
||||
BSplinePoles.pop_back();
|
||||
sugConstr.pop_back();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// insert circle point for pole, defer internal alignment constraining.
|
||||
try {
|
||||
|
||||
//Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Pole circle"));
|
||||
|
||||
//Add pole
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),10),True)",
|
||||
BSplinePoles.back().x,BSplinePoles.back().y);
|
||||
|
||||
poleGeoIds.push_back(getHighestCurveIndex());
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
|
||||
poleGeoIds[0], poleGeoIds.back());
|
||||
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Gui::Command::commitCommand();
|
||||
|
||||
//static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
// add auto constraints on pole
|
||||
if (sugConstr.back().size() > 0) {
|
||||
createAutoConstraints(sugConstr.back(), poleGeoIds.back(), Sketcher::PointPos::mid, false);
|
||||
}
|
||||
|
||||
//static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
if (!IsClosed) {
|
||||
addSugConstraint();
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
prevCursorPosition = onSketchPos;
|
||||
MousePressMode = MOUSE_NOT_PRESSED;
|
||||
|
||||
return finishCommand(onSketchPos);
|
||||
}
|
||||
|
||||
virtual void registerPressedKey(bool pressed, int key) override
|
||||
{
|
||||
if (SoKeyboardEvent::D == key && pressed) {
|
||||
SplineDegree = QInputDialog::getInt(
|
||||
Gui::getMainWindow(),
|
||||
QObject::tr("B-Spline Degree"),
|
||||
QObject::tr("Define B-Spline Degree, between 1 and %1:")
|
||||
.arg(QString::number(Geom_BSplineCurve::MaxDegree())),
|
||||
SplineDegree, 1, Geom_BSplineCurve::MaxDegree(), 1);
|
||||
// FIXME: Pressing Esc here also finishes the B-Spline creation.
|
||||
// The user may only want to exit the dialog.
|
||||
}
|
||||
// On pressing Backspace delete last pole
|
||||
else if (SoKeyboardEvent::BACKSPACE == key && pressed) {
|
||||
// when mouse is pressed we are in a transitional state so don't mess with it
|
||||
if (MOUSE_PRESSED == MousePressMode)
|
||||
return;
|
||||
|
||||
// can only delete last pole if it exists
|
||||
if (STATUS_SEEK_FIRST_CONTROLPOINT == Mode ||
|
||||
STATUS_CLOSE == Mode)
|
||||
return;
|
||||
|
||||
// if only first pole exists it's equivalent to canceling current spline
|
||||
if (poleGeoIds.size() == 1) {
|
||||
// this also exits b-spline creation if continuous mode is off
|
||||
this->quit();
|
||||
return;
|
||||
}
|
||||
|
||||
// reverse the steps of press/release button
|
||||
try {
|
||||
// already ensured that CurrentConstraint == EditCurve.size() > 1
|
||||
const int delGeoId = poleGeoIds.back();
|
||||
const auto& constraints = static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->Constraints.getValues();
|
||||
for (int i = constraints.size() - 1; i >= 0; --i) {
|
||||
if (delGeoId == constraints[i]->First ||
|
||||
delGeoId == constraints[i]->Second ||
|
||||
delGeoId == constraints[i]->Third)
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "delConstraint(%d)", i);
|
||||
}
|
||||
|
||||
// Remove pole
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "delGeometry(%d)", delGeoId);
|
||||
|
||||
static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
poleGeoIds.pop_back();
|
||||
BSplinePoles.pop_back();
|
||||
|
||||
// last entry is kept, as it corresponds to the current pole, but the one corresponding to the erased pole is removed
|
||||
sugConstr.erase(std::prev(std::prev(sugConstr.end())));
|
||||
|
||||
|
||||
// run this in the end to draw lines and position text
|
||||
drawControlPolygonToPosition(prevCursorPosition);
|
||||
drawCursorToPosition(prevCursorPosition);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
// some commands might have already deleted some constraints/geometries but not others
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// TODO: On pressing, say, W, modify last pole's weight
|
||||
// TODO: On pressing, say, M, modify next knot's multiplicity
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
virtual void quit(void) override
|
||||
{
|
||||
// We must see if we need to create a B-spline before cancelling everything
|
||||
// and now just like any other Handler,
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if (poleGeoIds.size() > 1) {
|
||||
// create B-spline from existing poles
|
||||
Mode=STATUS_CLOSE;
|
||||
finishCommand(Base::Vector2d(0.f,0.f));
|
||||
}
|
||||
else if(poleGeoIds.size() == 1) {
|
||||
// if we just have one point and we can not close anything, then cancel this creation but continue according to continuous mode
|
||||
//sketchgui->getDocument()->undo(1);
|
||||
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
if(!continuousMode){
|
||||
DrawSketchHandler::quit();
|
||||
}
|
||||
else {
|
||||
// This code disregards existing data and enables the continuous creation mode.
|
||||
resetHandlerState();
|
||||
}
|
||||
}
|
||||
else { // we have no data (CurrentConstraint == 0) so user when right-clicking really wants to exit
|
||||
DrawSketchHandler::quit();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void resetHandlerState()
|
||||
{
|
||||
Mode = STATUS_SEEK_FIRST_CONTROLPOINT;
|
||||
applyCursor();
|
||||
|
||||
SplineDegree = 3;
|
||||
|
||||
sugConstr.clear();
|
||||
poleGeoIds.clear();
|
||||
BSplinePoles.clear();
|
||||
|
||||
eraseEditCurve();
|
||||
|
||||
addSugConstraint();
|
||||
|
||||
IsClosed = false;
|
||||
}
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_BSpline");
|
||||
}
|
||||
|
||||
void addSugConstraint() {
|
||||
std::vector<AutoConstraint> sugConstr1;
|
||||
sugConstr.push_back(std::move(sugConstr1));
|
||||
}
|
||||
|
||||
void drawControlPolygonToPosition(Base::Vector2d position) {
|
||||
|
||||
std::vector<Base::Vector2d> editcurve(BSplinePoles);
|
||||
editcurve.push_back(position);
|
||||
|
||||
drawEdit(editcurve);
|
||||
}
|
||||
|
||||
void drawCursorToPosition(Base::Vector2d position) {
|
||||
if (!BSplinePoles.empty()) {
|
||||
float length = (position - BSplinePoles.back()).Length();
|
||||
float angle = (position - BSplinePoles.back()).GetAngle(Base::Vector2d(1.f,0.f));
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1f,%.1fdeg)", length, (angle != -FLOAT_MAX) ? angle * 180 / M_PI : 0);
|
||||
setPositionText(position, text);
|
||||
}
|
||||
}
|
||||
|
||||
void eraseEditCurve() {
|
||||
drawEdit(std::vector<Base::Vector2d>());
|
||||
}
|
||||
|
||||
bool finishCommand(Base::Vector2d position) {
|
||||
if (Mode==STATUS_CLOSE) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
for (auto & pole : BSplinePoles) {
|
||||
stream << "App.Vector(" << pole.x << "," << pole.y << "),";
|
||||
}
|
||||
|
||||
std::string controlpoints = stream.str();
|
||||
|
||||
// remove last comma and add brackets
|
||||
int index = controlpoints.rfind(',');
|
||||
controlpoints.resize(index);
|
||||
|
||||
controlpoints.insert(0,1,'[');
|
||||
controlpoints.append(1,']');
|
||||
|
||||
int currentgeoid = getHighestCurveIndex();
|
||||
|
||||
try {
|
||||
//Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add B-spline curve"));
|
||||
|
||||
/*Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.BSplineCurve"
|
||||
"(%s,%s),"
|
||||
"%s)",
|
||||
controlpoints.c_str(),
|
||||
ConstrMethod == 0 ?"False":"True",
|
||||
geometryCreationMode==Construction?"True":"False"); */
|
||||
|
||||
// {"poles", "mults", "knots", "periodic", "degree", "weights", "CheckRational", NULL};
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.BSplineCurve"
|
||||
"(%s,None,None,%s,%d,None,False),%s)",
|
||||
controlpoints.c_str(),
|
||||
ConstrMethod == 0 ?"False":"True",
|
||||
SplineDegree,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
currentgeoid++;
|
||||
|
||||
// autoconstraints were added to the circles of the poles, which is ok because they must go to the
|
||||
// right position, or the user will freak-out if they appear out of the autoconstrained position.
|
||||
// However, autoconstraints on the first and last pole, in normal non-periodic b-splines (with appropriate endpoint knot multiplicity)
|
||||
// as the ones created by this tool are intended for the b-spline endpoints, and not for the poles,
|
||||
// so here we retrieve any autoconstraint on those poles' center and mangle it to the endpoint.
|
||||
if (ConstrMethod == 0) {
|
||||
for(auto & constr : static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->Constraints.getValues()) {
|
||||
if(constr->First == poleGeoIds[0] && constr->FirstPos == Sketcher::PointPos::mid) {
|
||||
constr->First = currentgeoid;
|
||||
constr->FirstPos = Sketcher::PointPos::start;
|
||||
}
|
||||
else if(constr->First == poleGeoIds.back() && constr->FirstPos == Sketcher::PointPos::mid) {
|
||||
constr->First = currentgeoid;
|
||||
constr->FirstPos = Sketcher::PointPos::end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constraint pole circles to B-spline.
|
||||
std::stringstream cstream;
|
||||
|
||||
cstream << "conList = []\n";
|
||||
|
||||
for (size_t i = 0; i < poleGeoIds.size(); i++) {
|
||||
cstream << "conList.append(Sketcher.Constraint('InternalAlignment:Sketcher::BSplineControlPoint'," << poleGeoIds[0] + i
|
||||
<< "," << static_cast<int>(Sketcher::PointPos::mid) << "," << currentgeoid << "," << i << "))\n";
|
||||
}
|
||||
|
||||
cstream << Gui::Command::getObjectCmd(sketchgui->getObject()) << ".addConstraint(conList)\n";
|
||||
cstream << "del conList\n";
|
||||
|
||||
Gui::Command::doCommand(Gui::Command::Doc, cstream.str().c_str());
|
||||
|
||||
// for showing the knots on creation
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
resetHandlerState();
|
||||
|
||||
drawCursorToPosition(position);
|
||||
|
||||
/* It is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawCursorToPosition(position);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
SELECT_MODE Mode;
|
||||
MOUSE_PRESS_MODE MousePressMode;
|
||||
|
||||
// Stores position of the poles of the BSpline.
|
||||
std::vector<Base::Vector2d> BSplinePoles;
|
||||
|
||||
// suggested autoconstraints for poles.
|
||||
// A new one must be added e.g. using addSugConstraint() before adding a new pole.
|
||||
std::vector<std::vector<AutoConstraint>> sugConstr;
|
||||
|
||||
int ConstrMethod;
|
||||
int SplineDegree;
|
||||
bool IsClosed;
|
||||
std::vector<int> poleGeoIds;
|
||||
Base::Vector2d prevCursorPosition;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerBSpline_H
|
||||
|
||||
188
src/Mod/Sketcher/Gui/DrawSketchHandlerCarbonCopy.h
Normal file
188
src/Mod/Sketcher/Gui/DrawSketchHandlerCarbonCopy.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerCarbonCopy_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerCarbonCopy_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class CarbonCopySelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
CarbonCopySelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate(nullPointer()), object(obj)
|
||||
{}
|
||||
|
||||
bool allow(App::Document *pDoc, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
Q_UNUSED(sSubName);
|
||||
|
||||
Sketcher::SketchObject *sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
sketch->setAllowOtherBody(QApplication::keyboardModifiers() == Qt::ControlModifier || QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::AltModifier));
|
||||
sketch->setAllowUnaligned(QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::AltModifier));
|
||||
|
||||
this->notAllowedReason = "";
|
||||
Sketcher::SketchObject::eReasonList msg;
|
||||
// Reusing code: All good reasons not to allow a carbon copy
|
||||
bool xinv = false, yinv = false;
|
||||
if (!sketch->isCarbonCopyAllowed(pDoc, pObj, xinv, yinv, &msg)){
|
||||
switch(msg){
|
||||
case Sketcher::SketchObject::rlCircularReference:
|
||||
this->notAllowedReason = QT_TR_NOOP("Carbon copy would cause a circular dependency.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherDoc:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object is in another document.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherBody:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object belongs to another body. Hold Ctrl to allow cross-references.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherBodyWithLinks:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object belongs to another body and it contains external geometry. Cross-reference not allowed.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherPart:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object belongs to another part.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlNonParallel:
|
||||
this->notAllowedReason = QT_TR_NOOP("The selected sketch is not parallel to this sketch. Hold Ctrl+Alt to allow non-parallel sketches.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlAxesMisaligned:
|
||||
this->notAllowedReason = QT_TR_NOOP("The XY axes of the selected sketch do not have the same direction as this sketch. Hold Ctrl+Alt to disregard it.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOriginsMisaligned:
|
||||
this->notAllowedReason = QT_TR_NOOP("The origin of the selected sketch is not aligned with the origin of this sketch. Hold Ctrl+Alt to disregard it.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Carbon copy only works on sketches that are not disallowed (e.g. would produce a circular reference)
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class DrawSketchHandlerCarbonCopy: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerCarbonCopy() = default;
|
||||
virtual ~DrawSketchHandlerCarbonCopy()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Gui::Selection().getPreselection().pObjectName)
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool onSelectionChanged(const Gui::SelectionChanges& msg) override
|
||||
{
|
||||
if (msg.Type == Gui::SelectionChanges::AddSelection) {
|
||||
App::DocumentObject* obj = sketchgui->getObject()->getDocument()->getObject(msg.pObjectName);
|
||||
if (obj == nullptr)
|
||||
throw Base::ValueError("Sketcher: Carbon Copy: Invalid object in selection");
|
||||
|
||||
if (obj->getTypeId() == Sketcher::SketchObject::getClassTypeId()) {
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add carbon copy"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "carbonCopy(\"%s\",%s)",
|
||||
msg.pObjectName, geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
Gui::Selection().clearSelection();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add carbon copy: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void activated() override
|
||||
{
|
||||
setAxisPickStyle(false);
|
||||
Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView();
|
||||
Gui::View3DInventorViewer *viewer;
|
||||
viewer = static_cast<Gui::View3DInventor *>(mdi)->getViewer();
|
||||
|
||||
SoNode* root = viewer->getSceneGraph();
|
||||
static_cast<Gui::SoFCUnifiedSelection*>(root)->selectionRole.setValue(true);
|
||||
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
Gui::Selection().addSelectionGate(new CarbonCopySelection(sketchgui->getObject()));
|
||||
}
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_CarbonCopy");
|
||||
}
|
||||
|
||||
virtual void deactivated() override
|
||||
{
|
||||
Q_UNUSED(sketchgui);
|
||||
setAxisPickStyle(true);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerCarbonCopy_H
|
||||
|
||||
350
src/Mod/Sketcher/Gui/DrawSketchHandlerCircle.h
Normal file
350
src/Mod/Sketcher/Gui/DrawSketchHandlerCircle.h
Normal file
@@ -0,0 +1,350 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerCircle_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerCircle_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerCircle : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerCircle() : Mode(STATUS_SEEK_First),EditCurve(34){}
|
||||
virtual ~DrawSketchHandlerCircle(){}
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_Close
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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) {
|
||||
double rx0 = onSketchPos.x - EditCurve[0].x;
|
||||
double ry0 = onSketchPos.y - EditCurve[0].y;
|
||||
for (int i=0; i < 16; i++) {
|
||||
double angle = i*M_PI/16.0;
|
||||
double rx = rx0 * cos(angle) + ry0 * sin(angle);
|
||||
double ry = -rx0 * sin(angle) + ry0 * cos(angle);
|
||||
EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + rx, EditCurve[0].y + ry);
|
||||
EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - rx, EditCurve[0].y - ry);
|
||||
}
|
||||
EditCurve[33] = EditCurve[1];
|
||||
|
||||
// Display radius for user
|
||||
float radius = (onSketchPos - EditCurve[0]).Length();
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR)", radius);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0],
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
} else {
|
||||
EditCurve[1] = onSketchPos;
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode==STATUS_Close) {
|
||||
double rx = EditCurve[1].x - EditCurve[0].x;
|
||||
double ry = EditCurve[1].y - EditCurve[0].y;
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch circle"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle"
|
||||
"(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%s)",
|
||||
EditCurve[0].x, EditCurve[0].y,
|
||||
sqrt(rx*rx + ry*ry),
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add circle: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
// add auto constraints for the center point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add suggested constraints for circumference
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::none);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(34);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Circle");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
|
||||
};
|
||||
|
||||
class DrawSketchHandler3PointCircle : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandler3PointCircle()
|
||||
: Mode(STATUS_SEEK_First),EditCurve(2),radius(1),N(32.0){}
|
||||
virtual ~DrawSketchHandler3PointCircle(){}
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_SEEK_Third, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Mode == STATUS_SEEK_Second || Mode == STATUS_SEEK_Third) {
|
||||
try
|
||||
{
|
||||
if (Mode == STATUS_SEEK_Second)
|
||||
CenterPoint = EditCurve[N+1] = (onSketchPos - FirstPoint)/2 + FirstPoint;
|
||||
else
|
||||
CenterPoint = EditCurve[N+1] = Part::Geom2dCircle::getCircleCenter(FirstPoint, SecondPoint, onSketchPos);
|
||||
radius = (onSketchPos - CenterPoint).Length();
|
||||
double lineAngle = GetPointAngle(CenterPoint, onSketchPos);
|
||||
|
||||
// Build a N point circle
|
||||
for (int i=1; i < N; i++) {
|
||||
// Start at current angle
|
||||
double angle = i*2*M_PI/N + lineAngle; // N point closed circle has N segments
|
||||
EditCurve[i] = Base::Vector2d(CenterPoint.x + radius*cos(angle),
|
||||
CenterPoint.y + radius*sin(angle));
|
||||
}
|
||||
// Beginning and end of curve should be exact
|
||||
EditCurve[0] = EditCurve[N] = onSketchPos;
|
||||
|
||||
// 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);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (Mode == STATUS_SEEK_Second) {
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Base::ValueError &e) {
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
// N point curve + center + endpoint
|
||||
EditCurve.resize(N+2);
|
||||
FirstPoint = onSketchPos;
|
||||
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if (Mode == STATUS_SEEK_Second) {
|
||||
SecondPoint = onSketchPos;
|
||||
|
||||
Mode = STATUS_SEEK_Third;
|
||||
}
|
||||
else {
|
||||
EditCurve.resize(N);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
applyCursor();
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
// Need to look at. rx might need fixing.
|
||||
if (Mode==STATUS_End) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch circle"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle"
|
||||
"(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%s)",
|
||||
CenterPoint.x, CenterPoint.y,
|
||||
radius,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add circle: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
// Auto Constraint first picked point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::none);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// Auto Constraint second picked point
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::none);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
// Auto Constraint third picked point
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, getHighestCurveIndex(), Sketcher::PointPos::none);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_3PointCircle");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
Base::Vector2d CenterPoint, FirstPoint, SecondPoint;
|
||||
double radius, N; // N should be even
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerCircle_H
|
||||
|
||||
808
src/Mod/Sketcher/Gui/DrawSketchHandlerEllipse.h
Normal file
808
src/Mod/Sketcher/Gui/DrawSketchHandlerEllipse.h
Normal file
@@ -0,0 +1,808 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerEllipse_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerEllipse_H
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
/* Ellipse ==============================================================================*/
|
||||
/**
|
||||
* @brief This class handles user interaction to draw and save the ellipse
|
||||
*
|
||||
* Two construction methods are implemented:
|
||||
* -Periapsis, apoapsis, and b; and
|
||||
* -Center, periapsis, and b.
|
||||
*
|
||||
* The first method limits the ellipse to a circle, while the second method allows for
|
||||
* swapping of the semi-major and semi-minor axes.
|
||||
*
|
||||
* We use three reference frames in this class. The first (and primary), is the cartesian
|
||||
* frame of the sketcher; all our work begins and ends in this frame. The second is the
|
||||
* perifocal frame of the ellipse using polar coordinates. We use this frame for naming
|
||||
* conventions and working with the ellipse. The last is a rotated right-handed cartesian
|
||||
* frame centered at the ellipse center with the +X direction towards periapsis, +Z out of
|
||||
* screen.
|
||||
*
|
||||
* When working with an ellipse in the perifocal frame, the following equations are useful:
|
||||
*
|
||||
* \f{eqnarray*}{
|
||||
* r &\equiv& \textrm{ radial distance from the focus to a point on the ellipse}\\
|
||||
* r_a &\equiv& \textrm{ radial distance from the focus to apopasis}\\
|
||||
* r_p &\equiv& \textrm{ radial distance from the focus to periapsis}\\
|
||||
* a &\equiv& \textrm{ length of the semi-major axis, colloquially 'radius'}\\
|
||||
* b &\equiv& \textrm{ length of the semi-minor axis, colloquially 'radius'}\\
|
||||
* e &\equiv& \textrm{ eccentricity of the ellipse}\\
|
||||
* \theta_b &\equiv& \textrm{ angle to the intersection of the semi-minor axis and the ellipse, relative to the focus}\\
|
||||
* ae &\equiv& \textrm{ distance from the focus to the centroid}\\
|
||||
* r &=& \frac{a(1-e^2)}{1+e\cos(\theta)} = \frac{r_a(1-e)}{1+e\cos(\theta)} = \frac{r_p(1+e)}{1+e\cos(\theta)}\\
|
||||
* r_a &=& a(1-e)\\
|
||||
* r_p &=& a(1+e)\\
|
||||
* a &=& \frac{r_p+r_a}{2}\\
|
||||
* b &=& a\sqrt{1-e^2}\\
|
||||
* e &=& \frac{r_a-r_p}{r_a+r_p} = \sqrt{1-\frac{b^2}{a^2}}\\
|
||||
* \theta_b &=& \left[\pi - \arctan\left(\frac{b}{ae}\right)\right] \pm N\pi
|
||||
* \f}
|
||||
*
|
||||
*/
|
||||
class DrawSketchHandlerEllipse : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerEllipse(int constructionMethod)
|
||||
: mode(STATUS_Close)
|
||||
, method(CENTER_PERIAPSIS_B)
|
||||
, constrMethod(constructionMethod)
|
||||
, a(0), b(0), e(0), ratio(0), ae(0)
|
||||
, num(0), r(0), theta(0), phi(0)
|
||||
, editCurve(33), fixedAxisLength(0)
|
||||
{
|
||||
}
|
||||
virtual ~DrawSketchHandlerEllipse(){}
|
||||
/// Mode table, describes what step of the process we are in
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_PERIAPSIS, /**< enum value, looking for click to set periapsis. */
|
||||
STATUS_SEEK_APOAPSIS, /**< enum value, looking for click to set apoapsis. */
|
||||
STATUS_SEEK_CENTROID, /**< enum value, looking for click to set centroid. */
|
||||
STATUS_SEEK_A, /**< enum value, looking for click to set a. */
|
||||
STATUS_SEEK_B, /**< enum value, looking for click to set b. */
|
||||
STATUS_Close /**< enum value, finalizing and saving ellipse. */
|
||||
};
|
||||
/// Construction methods, describes the method used to construct the ellipse
|
||||
enum ConstructionMethod {
|
||||
CENTER_PERIAPSIS_B, /**< enum value, click on center, then periapsis, then b point. */
|
||||
PERIAPSIS_APOAPSIS_B /**< enum value, click on periapsis, then apoapsis, then b point. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Updates the ellipse when the cursor moves
|
||||
* @param onSketchPos the position of the cursor on the sketch
|
||||
*/
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (method == PERIAPSIS_APOAPSIS_B) {
|
||||
if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
} else if (mode == STATUS_SEEK_APOAPSIS) {
|
||||
solveEllipse(onSketchPos);
|
||||
approximateEllipse();
|
||||
|
||||
// Display radius for user
|
||||
float semiMajorRadius = a * 2;
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", semiMajorRadius,semiMajorRadius);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(editCurve);
|
||||
// Suggestions for ellipse and curves are disabled because many tangent constraints
|
||||
// need an intermediate point or line.
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
} else if (mode == STATUS_SEEK_B) {
|
||||
solveEllipse(onSketchPos);
|
||||
approximateEllipse();
|
||||
|
||||
// Display radius for user
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", a, b);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(editCurve);
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f),
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else { // method is CENTER_PERIAPSIS_B
|
||||
if (mode == STATUS_SEEK_CENTROID) {
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) { // TODO: ellipse prio 1
|
||||
renderSuggestConstraintsCursor(sugConstr1);
|
||||
return;
|
||||
}
|
||||
} else if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
solveEllipse(onSketchPos);
|
||||
approximateEllipse();
|
||||
|
||||
// Display radius for user
|
||||
float semiMajorRadius = a * 2;
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", semiMajorRadius,semiMajorRadius);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(editCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centroid,
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
} else if ((mode == STATUS_SEEK_A) || (mode == STATUS_SEEK_B)) {
|
||||
solveEllipse(onSketchPos);
|
||||
approximateEllipse();
|
||||
|
||||
// Display radius for user
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fR)", a, b);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(editCurve);
|
||||
if (seekAutoConstraint(sugConstr3, onSketchPos, onSketchPos - centroid,
|
||||
AutoConstraint::CURVE)) {
|
||||
renderSuggestConstraintsCursor(sugConstr3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Changes drawing mode on user-click
|
||||
* @param onSketchPos the position of the cursor on the sketch
|
||||
* @return
|
||||
*/
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (method == PERIAPSIS_APOAPSIS_B) {
|
||||
if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
periapsis = onSketchPos;
|
||||
mode = STATUS_SEEK_APOAPSIS;
|
||||
}
|
||||
else if (mode == STATUS_SEEK_APOAPSIS) {
|
||||
apoapsis = onSketchPos;
|
||||
mode = STATUS_SEEK_B;
|
||||
}
|
||||
else {
|
||||
mode = STATUS_Close;
|
||||
}
|
||||
} else { // method is CENTER_PERIAPSIS_B
|
||||
if (mode == STATUS_SEEK_CENTROID) {
|
||||
centroid = onSketchPos;
|
||||
mode = STATUS_SEEK_PERIAPSIS;
|
||||
}
|
||||
else if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
periapsis = onSketchPos;
|
||||
mode = STATUS_SEEK_B;
|
||||
}
|
||||
else {
|
||||
mode = STATUS_Close;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calls \c saveEllipse() after last user input
|
||||
* @param onSketchPos the position of the cursor on the sketch
|
||||
* @return
|
||||
*/
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (mode == STATUS_Close) {
|
||||
saveEllipse();
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
if (constrMethod == 0) {
|
||||
method = CENTER_PERIAPSIS_B;
|
||||
mode = STATUS_SEEK_CENTROID;
|
||||
} else {
|
||||
method = PERIAPSIS_APOAPSIS_B;
|
||||
mode = STATUS_SEEK_PERIAPSIS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Slot called when the create ellipse command is activated
|
||||
* @param sketchgui A pointer to the active sketch
|
||||
*/
|
||||
virtual void activated() override
|
||||
{
|
||||
if (constrMethod == 0) {
|
||||
method = CENTER_PERIAPSIS_B;
|
||||
mode = STATUS_SEEK_CENTROID;
|
||||
} else {
|
||||
method = PERIAPSIS_APOAPSIS_B;
|
||||
mode = STATUS_SEEK_PERIAPSIS;
|
||||
}
|
||||
}
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Ellipse");
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
|
||||
private:
|
||||
SelectMode mode;
|
||||
/// the method of constructing the ellipse
|
||||
ConstructionMethod method;
|
||||
int constrMethod;
|
||||
/// periapsis position vector, in standard position in sketch coordinate system
|
||||
Base::Vector2d periapsis;
|
||||
/// apoapsis position vector, in standard position in sketch coordinate system
|
||||
Base::Vector2d apoapsis;
|
||||
/// centroid position vector, in standard position in sketch coordinate system
|
||||
Base::Vector2d centroid;
|
||||
/**
|
||||
* @brief position vector of positive b point, in standard position in sketch coordinate system
|
||||
* I.E. in polar perifocal system, the first intersection of the semiminor axis with the ellipse
|
||||
* as theta increases from 0. This always happens when:
|
||||
* \f{eqnarray*}{
|
||||
* \theta_b &=& \left[\pi - \arctan\left(\frac{b}{ae}\right)\right] \pm N 2\pi
|
||||
* \f}
|
||||
*
|
||||
* In a rotated R^3 cartesian system, centered at the centroid, +X towards periapsis, and
|
||||
* +Z coming out of the sketch, this b position is in the +Y direction from the centroid.
|
||||
*/
|
||||
Base::Vector2d positiveB;
|
||||
/// the other b position
|
||||
Base::Vector2d negativeB;
|
||||
/// cart. position vector for primary focus
|
||||
Base::Vector2d f;
|
||||
/// cart. position vector for other focus
|
||||
Base::Vector2d fPrime;
|
||||
/// Unit vector for apse line
|
||||
Base::Vector2d apseHat;
|
||||
/// length of semimajor axis, i.e. 'radius' colloquially
|
||||
double a;
|
||||
/// length of semiminor axis, i.e. 'radius' colloquially
|
||||
double b;
|
||||
/// eccentricity [unitless]
|
||||
double e;
|
||||
/// optimization, holds a term that helps calculate b in terms of a and e
|
||||
double ratio;
|
||||
/// holds product of a * e
|
||||
double ae;
|
||||
/// holds numerator of orbit equation of form a(1-e^2)
|
||||
double num;
|
||||
/// holds a radial distance from f to the ellipse for a given theta
|
||||
double r;
|
||||
/// angle of a point in a perifocal frame centered at f
|
||||
double theta;
|
||||
/// angle of apse line relative to sketch coordinate system
|
||||
double phi;
|
||||
/// holds a position vector for a point on the ellipse from f
|
||||
Base::Vector2d pos;
|
||||
/// holds a position vector for a point on the ellipse from fPrime
|
||||
Base::Vector2d posPrime;
|
||||
/// holds position vectors for a points on the ellipse
|
||||
std::vector<Base::Vector2d> editCurve;
|
||||
/// local i_hat vector for ellipse, from centroid to periapsis
|
||||
Base::Vector3d iPrime;
|
||||
/// local j_hat vector for ellipse, from centroid to b point
|
||||
Base::Vector3d jPrime;
|
||||
/// length (radius) of the fixed axis
|
||||
double fixedAxisLength;
|
||||
/// position vector of fixed axis point in sketch coordinates
|
||||
Base::Vector2d fixedAxis;
|
||||
|
||||
/**
|
||||
* @brief Computes a vector of 2D points representing an ellipse
|
||||
* @param onSketchPos Current position of the cursor on the sketch
|
||||
*/
|
||||
void solveEllipse(Base::Vector2d onSketchPos)
|
||||
{
|
||||
const double GOLDEN_RATIO = 1.6180339887;
|
||||
Base::Vector3d k(0,0,1);
|
||||
|
||||
if (method == PERIAPSIS_APOAPSIS_B) {
|
||||
if (mode == STATUS_SEEK_APOAPSIS) {
|
||||
apoapsis = onSketchPos;
|
||||
}
|
||||
a = (apoapsis - periapsis).Length() / 2;
|
||||
apseHat = (periapsis - apoapsis);
|
||||
apseHat.Normalize();
|
||||
centroid = apseHat;
|
||||
centroid.Scale(-1 * a);
|
||||
centroid = periapsis + centroid;
|
||||
if (mode == STATUS_SEEK_APOAPSIS) {
|
||||
// for first step, we draw an ellipse inscribed in a golden rectangle
|
||||
ratio = 1 / GOLDEN_RATIO; // ~= 0.6180339887
|
||||
e = sqrt(ratio); // ~= 0.7861513777
|
||||
b = a * ratio;
|
||||
}
|
||||
else if (mode == STATUS_SEEK_B) {
|
||||
// Get the closest distance from onSketchPos to apse line, as a 'requested' value for b
|
||||
Base::Vector2d cursor = Base::Vector2d(onSketchPos - f); // vector from f to cursor pos
|
||||
// decompose cursor with a projection, then length of w_2 will give us b
|
||||
Base::Vector2d w_1 = cursor;
|
||||
w_1.ProjectToLine(cursor, (periapsis - apoapsis)); // projection of cursor line onto apse line
|
||||
Base::Vector2d w_2 = (cursor - w_1);
|
||||
b = w_2.Length();
|
||||
|
||||
// limit us to ellipse or circles
|
||||
if (b > a) {
|
||||
b = a;
|
||||
}
|
||||
|
||||
e = sqrt(1 - ((b * b) / (a * a)));
|
||||
ratio = sqrt(1 - (e*e));
|
||||
}
|
||||
ae = a * e;
|
||||
f = apseHat;
|
||||
f.Scale(ae);
|
||||
f = centroid + f;
|
||||
fPrime = apseHat;
|
||||
fPrime.Scale(-1 * ae);
|
||||
fPrime = centroid + fPrime;
|
||||
phi = atan2(apseHat.y, apseHat.x);
|
||||
num = a * (1 - (e * e));
|
||||
// The ellipse is now solved
|
||||
} else { // method == CENTER_PERIAPSIS_B
|
||||
if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
// solve the ellipse inscribed in a golden rectangle
|
||||
periapsis = onSketchPos;
|
||||
a = (centroid - periapsis).Length();
|
||||
iPrime.x = periapsis.x - centroid.x;
|
||||
iPrime.y = periapsis.y - centroid.y;
|
||||
iPrime.z = 0;
|
||||
jPrime = k % iPrime; // j = k cross i
|
||||
|
||||
// these are constant for any ellipse inscribed in a golden rectangle
|
||||
ratio = 1 / GOLDEN_RATIO; // ~= 0.6180339887
|
||||
e = sqrt(ratio); // ~= 0.7861513777
|
||||
|
||||
b = a * ratio;
|
||||
ae = a * e;
|
||||
apseHat = (periapsis - centroid);
|
||||
apseHat.Normalize();
|
||||
f = apseHat;
|
||||
f.Scale(ae);
|
||||
f = centroid + f;
|
||||
fPrime = apseHat;
|
||||
fPrime.Scale(-1 * ae);
|
||||
fPrime = centroid + fPrime;
|
||||
apoapsis = apseHat;
|
||||
apoapsis.Scale(-1 * a);
|
||||
apoapsis = centroid + apoapsis;
|
||||
phi = atan2(apseHat.y, apseHat.x);
|
||||
num = a * (1 - (e * e));
|
||||
fixedAxisLength = a;
|
||||
fixedAxis = periapsis;
|
||||
} else if ((mode == STATUS_SEEK_B) || (mode == STATUS_SEEK_A)) {
|
||||
// while looking for the last click, we may switch back and forth
|
||||
// between looking for a b point and looking for periapsis, so ensure
|
||||
// we are in the right mode
|
||||
Base::Vector2d cursor = Base::Vector2d(onSketchPos - centroid); // vector from centroid to cursor pos
|
||||
// decompose cursor with a projection, then length of w_2 will give us b
|
||||
Base::Vector2d w_1 = cursor;
|
||||
w_1.ProjectToLine(cursor, (fixedAxis - centroid)); // projection of cursor line onto fixed axis line
|
||||
Base::Vector2d w_2 = (cursor - w_1);
|
||||
if (w_2.Length() > fixedAxisLength) {
|
||||
// b is fixed, we are seeking a
|
||||
mode = STATUS_SEEK_A;
|
||||
jPrime.x = (fixedAxis - centroid).x;
|
||||
jPrime.y = (fixedAxis - centroid).y;
|
||||
jPrime.Normalize();
|
||||
iPrime = jPrime % k; // cross
|
||||
b = fixedAxisLength;
|
||||
a = w_2.Length();
|
||||
} else {
|
||||
// a is fixed, we are seeking b
|
||||
mode = STATUS_SEEK_B;
|
||||
iPrime.x = (fixedAxis - centroid).x;
|
||||
iPrime.y = (fixedAxis - centroid).y;
|
||||
iPrime.Normalize();
|
||||
jPrime = k % iPrime; // cross
|
||||
a = fixedAxisLength;
|
||||
b = w_2.Length();
|
||||
}
|
||||
// now finish solving the ellipse
|
||||
periapsis.x = centroid.x + (iPrime * a).x;
|
||||
periapsis.y = centroid.y + (iPrime * a).y;
|
||||
e = sqrt(1 - ((b * b) / (a * a)));
|
||||
ratio = sqrt(1 - (e*e));
|
||||
ae = a * e;
|
||||
apseHat = (periapsis - centroid);
|
||||
apseHat.Normalize();
|
||||
f = apseHat;
|
||||
f.Scale(ae);
|
||||
f = centroid + f;
|
||||
fPrime = apseHat;
|
||||
fPrime.Scale(-1 * ae);
|
||||
fPrime = centroid + fPrime;
|
||||
apoapsis = apseHat;
|
||||
apoapsis.Scale(-1 * a);
|
||||
apoapsis = centroid + apoapsis;
|
||||
phi = atan2(apseHat.y, apseHat.x);
|
||||
num = a * (1 - (e * e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Computes a sequence of 2D vectors to approximate the ellipse
|
||||
*/
|
||||
void approximateEllipse()
|
||||
{
|
||||
// We will approximate the ellipse as a sequence of connected chords
|
||||
// Number of points per quadrant of the ellipse
|
||||
int n = static_cast<int>((editCurve.size() - 1) / 4);
|
||||
|
||||
// We choose points in the perifocal frame then translate them to sketch cartesian.
|
||||
// This gives us a better approximation of an ellipse, i.e. more points where the
|
||||
// curvature is higher. If the eccentricity is high, we shift the points a bit towards
|
||||
// the semi-minor axis.
|
||||
double partitionAngle = (M_PI - atan2(b, ae)) / n;
|
||||
double radianShift = 0;
|
||||
if (e > 0.8) {radianShift = (partitionAngle / 5) * 4;}
|
||||
for (int i=0; i < n; i++) {
|
||||
theta = i * partitionAngle;
|
||||
if (i > 0) {theta = theta + radianShift;}
|
||||
r = num / (1 + (e * cos(theta)));
|
||||
// r(pi/2) is semi-latus rectum, if we need it
|
||||
pos.x = r*cos(theta+phi); // phi rotates, sin/cos translate
|
||||
pos.y = r*sin(theta+phi);
|
||||
pos = pos + f;
|
||||
posPrime.x = r*cos(theta+phi+M_PI);
|
||||
posPrime.y = r*sin(theta+phi+M_PI);
|
||||
posPrime = posPrime + fPrime;
|
||||
// over the loop, loads Quadrant I points, by using f as origin
|
||||
editCurve[i] = pos;
|
||||
// over the loop, loads Quadrant III points, by using fPrime as origin
|
||||
editCurve[(2*n) + i] = posPrime;
|
||||
// load points with negative theta angles (i.e. cw)
|
||||
if (i>0) {
|
||||
pos.x = r*cos(-1*theta+phi);
|
||||
pos.y = r*sin(-1*theta+phi);
|
||||
pos = pos + f;
|
||||
// loads Quadrant IV points
|
||||
editCurve[(4*n) - i] = pos;
|
||||
posPrime.x = r*cos(-1*theta+phi+M_PI);
|
||||
posPrime.y = r*sin(-1*theta+phi+M_PI);
|
||||
posPrime = posPrime + fPrime;
|
||||
// loads Quadrant II points
|
||||
editCurve[(2*n) - i] = posPrime;
|
||||
}
|
||||
}
|
||||
// load pos & neg b points
|
||||
theta = M_PI - atan2(b, ae); // the angle from f to the positive b point
|
||||
r = num / (1 + (e * cos(theta)));
|
||||
pos.x = r*cos(theta+phi);
|
||||
pos.y = r*sin(theta+phi);
|
||||
pos = pos + f;
|
||||
editCurve[n] = pos; // positive
|
||||
pos.x = r*cos(-1*theta+phi);
|
||||
pos.y = r*sin(-1*theta+phi);
|
||||
pos = pos + f;
|
||||
editCurve[(3*n)] = pos; // negative
|
||||
// force the curve to be a closed shape
|
||||
editCurve[(4*n)] = editCurve[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints the ellipse data to STDOUT as an GNU Octave script
|
||||
* @param onSketchPos position of the cursor on the sketch
|
||||
*/
|
||||
void ellipseToOctave(Base::Vector2d /*onSketchPos*/)
|
||||
{
|
||||
int n = static_cast<int>((editCurve.size() - 1) / 4);
|
||||
|
||||
// send a GNU Octave script to stdout to plot points for debugging
|
||||
std::ostringstream octave;
|
||||
octave << std::fixed << std::setprecision(12);
|
||||
octave << "\nclear all;\nclose all;\nclc;\n\n";
|
||||
octave << "periapsis = [" << periapsis.x << ", " << periapsis.y << "];\n";
|
||||
octave << "apoapsis = [" << apoapsis.x << ", " << apoapsis.y << "];\n";
|
||||
octave << "positiveB = [" << editCurve[n].x << ", " << editCurve[n].y << "];\n";
|
||||
octave << "apseHat = [" << apseHat.x << ", " << apseHat.y << "];\n";
|
||||
octave << "a = " << a << ";\n";
|
||||
octave << "b = " << b << ";\n";
|
||||
octave << "eccentricity = " << e << ";\n";
|
||||
octave << "centroid = [" << centroid.x << ", " << centroid.y << "];\n";
|
||||
octave << "f = [" << f.x << ", " << f.y << "];\n";
|
||||
octave << "fPrime = [" << fPrime.x << ", " << fPrime.y << "];\n";
|
||||
octave << "phi = " << phi << ";\n\n";
|
||||
octave << "x = [";
|
||||
for (int i=0; i < 4*n + 1; i++) {
|
||||
octave << editCurve[i].x;
|
||||
if (i < 4*n) {
|
||||
octave << ", ";
|
||||
}
|
||||
}
|
||||
octave << "];\n";
|
||||
octave << "y = [";
|
||||
for (int i=0; i < 4*n + 1; i++) {
|
||||
octave << editCurve[i].y;
|
||||
if (i < 4*n) {
|
||||
octave << ", ";
|
||||
}
|
||||
}
|
||||
octave << "];\n\n";
|
||||
octave << "% Draw ellipse points in red;\n";
|
||||
octave << "plot (x, y, \"r.\", \"markersize\", 5);\n";
|
||||
octave << "axis ([-300, 300, -300, 300], \"square\");grid on;\n";
|
||||
octave << "hold on;\n\n";
|
||||
octave << "% Draw centroid in blue, f in cyan, and fPrime in magenta;\n";
|
||||
octave << "plot(centroid(1), centroid(2), \"b.\", \"markersize\", 5);\n";
|
||||
octave << "plot(f(1), f(2), \"c.\", \"markersize\", 5);\n";
|
||||
octave << "plot(fPrime(1), fPrime(2), \"m.\", \"markersize\", 5);\n";
|
||||
octave << "n = [periapsis(1) - f(1), periapsis(2) - f(2)];\n";
|
||||
octave << "h = quiver(f(1),f(2),n(1),n(2), 0);\n";
|
||||
octave << "set (h, \"maxheadsize\", 0.1);\n\n";
|
||||
octave << "% Draw the three position vectors used for Gui::Command::doCommand(...)\n";
|
||||
octave << "periapsisVec = quiver(0,0,periapsis(1),periapsis(2), 0);\n";
|
||||
octave << "set (periapsisVec, \"maxheadsize\", 0.01, \"color\", \"black\");\n";
|
||||
octave << "centroidVec = quiver(0,0,centroid(1),centroid(2), 0);\n";
|
||||
octave << "set (centroidVec, \"maxheadsize\", 0.01, \"color\", \"black\");\n";
|
||||
octave << "bVec = quiver(0,0,positiveB(1),positiveB(2), 0);\n";
|
||||
octave << "set (bVec, \"maxheadsize\", 0.01, \"color\", \"black\");\n\n";
|
||||
octave << "% Draw the local x & y basis vectors, scaled to a and b, in red and blue, respectively\n";
|
||||
octave << "xLocalVec = quiver(centroid(1),centroid(2),periapsis(1)-centroid(1),periapsis(2)-centroid(2), 0);\n";
|
||||
octave << "set (xLocalVec, \"maxheadsize\", 0.01, \"color\", \"red\");\n";
|
||||
octave << "yLocalVec = quiver(centroid(1),centroid(2), positiveB(1)-centroid(1), positiveB(2)-centroid(2), 0);\n";
|
||||
octave << "set (yLocalVec, \"maxheadsize\", 0.01, \"color\", \"blue\");\nhold off;\n";
|
||||
qDebug() << QString::fromStdString(octave.str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finalizes and saves the drawn ellipse
|
||||
* @return nothing
|
||||
*/
|
||||
void saveEllipse()
|
||||
{
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
/* There are a couple of issues with Gui::Command::doCommand(...) and
|
||||
* GC_MakeEllipse(...) that cause bugs if not handled properly, even
|
||||
* when we give them a mathematically-correct ellipse.
|
||||
*
|
||||
* GC_MakeEllipse may fail with a gce_InvertAxis error for a small
|
||||
* circular ellipse when floating point roundoff or representation
|
||||
* errors make the b axis slightly larger than the a axis.
|
||||
*
|
||||
* A similar, larger, issue arises in Gui::Command::doCommand(...) because
|
||||
* we cast our double vector components into strings with a fixed
|
||||
* precision of six, and then create new doubles from the strings
|
||||
* in EllipsePy::PyInit(...). Thus, by the time we call GC_MakeEllipse(...)
|
||||
* in EllipsePy::PyInit(...), our ellipse may not be valid anymore
|
||||
* because b is now greater than a.
|
||||
*
|
||||
* To handle these issues, we simulate the effects Gui::Command::doCommand(...)
|
||||
* has on our ellipse, and we adjust our ellipse parameters until
|
||||
* GC_MakeEllipse successfully creates an ellipse with our mangled
|
||||
* parameters.
|
||||
*
|
||||
* In almost all cases, we only have to make our test ellipse one time;
|
||||
* it is only in the rare edge cases that require repeated test ellipses
|
||||
* until we get a valid one, or fail due to excessive attempts. With a
|
||||
* limit of 25 attempts, I have been unable to make it fail.
|
||||
*/
|
||||
|
||||
// simulate loss of precision in centroid, periapsis, and apoapsis
|
||||
char cx[64];
|
||||
char cy[64];
|
||||
char px[64];
|
||||
char py[64];
|
||||
char ax[64];
|
||||
char ay[64];
|
||||
sprintf(cx, "%.6lf\n", centroid.x);
|
||||
sprintf(cy, "%.6lf\n", centroid.y);
|
||||
sprintf(px, "%.6lf\n", periapsis.x);
|
||||
sprintf(py, "%.6lf\n", periapsis.y);
|
||||
sprintf(ax, "%.6lf\n", apoapsis.x);
|
||||
sprintf(ay, "%.6lf\n", apoapsis.y);
|
||||
centroid.x = atof(cx);
|
||||
centroid.y = atof(cy);
|
||||
periapsis.x = atof(px);
|
||||
periapsis.y = atof(py);
|
||||
apoapsis.x = atof(ax);
|
||||
apoapsis.y = atof(ay);
|
||||
double majorLength = (periapsis - apoapsis).Length();
|
||||
double minorLength = 0;
|
||||
|
||||
/* GC_MakeEllipse requires a right-handed coordinate system, with +X
|
||||
* from centroid to periapsis, +Z out of the page.
|
||||
*/
|
||||
Base::Vector3d k(0,0,1);
|
||||
Base::Vector3d i(periapsis.x - centroid.x, periapsis.y - centroid.y, 0);
|
||||
Base::Vector3d j = k % i; // j = k cross i
|
||||
double beta = 1e-7;
|
||||
int count = 0;
|
||||
int limit = 25; // no infinite loops!
|
||||
bool success = false;
|
||||
double tempB = b;
|
||||
|
||||
// adjust b until our mangled vectors produce a good ellipse in GC_MakeEllipse
|
||||
// and the mangled major and minor lines in LinePy::PyInit(...) are such that
|
||||
// major is at least slightly larger than minor
|
||||
do {
|
||||
tempB = b - double(count * beta);
|
||||
j = j.Normalize() * tempB;
|
||||
positiveB.x = centroid.x + j.x;
|
||||
positiveB.y = centroid.y + j.y;
|
||||
negativeB.x = centroid.x + (j.x * -1);
|
||||
negativeB.y = centroid.y + (j.y * -1);
|
||||
char bpx[64];
|
||||
char bpy[64];
|
||||
char bnx[64];
|
||||
char bny[64];
|
||||
sprintf(bpx, "%.6lf\n", positiveB.x);
|
||||
sprintf(bpy, "%.6lf\n", positiveB.y);
|
||||
sprintf(bnx, "%.6lf\n", negativeB.x);
|
||||
sprintf(bny, "%.6lf\n", negativeB.y);
|
||||
positiveB.x = atof(bpx);
|
||||
positiveB.y = atof(bpy);
|
||||
negativeB.x = atof(bnx);
|
||||
negativeB.y = atof(bny);
|
||||
GC_MakeEllipse me(gp_Pnt(periapsis.x,periapsis.y,0),
|
||||
gp_Pnt(positiveB.x,positiveB.y,0),
|
||||
gp_Pnt(centroid.x,centroid.y,0));
|
||||
minorLength = (negativeB - positiveB).Length();
|
||||
count++;
|
||||
success = me.IsDone() && (minorLength + beta < majorLength);
|
||||
} while (!success && (count <= limit));
|
||||
if (!success) {
|
||||
qDebug() << "Failed to create a valid mangled ellipse after" << count << "attempts";
|
||||
}
|
||||
|
||||
// save any changes to b, then recalculate ellipse as required due to change in b
|
||||
b = tempB;
|
||||
e = sqrt(1 - ((b * b) / (a * a)));
|
||||
ae = a * e;
|
||||
f = apseHat;
|
||||
f.Scale(ae);
|
||||
f = centroid + f;
|
||||
fPrime = apseHat;
|
||||
fPrime.Scale(-1 * ae);
|
||||
fPrime = centroid + fPrime;
|
||||
|
||||
int currentgeoid = getHighestCurveIndex(); // index of the ellipse we just created
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch ellipse"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Ellipse"
|
||||
"(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)",
|
||||
periapsis.x, periapsis.y,
|
||||
positiveB.x, positiveB.y,
|
||||
centroid.x, centroid.y,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
currentgeoid++;
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("%s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
if (method == CENTER_PERIAPSIS_B) {
|
||||
// add auto constraints for the center point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (method == PERIAPSIS_APOAPSIS_B) {
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
if (sugConstr3.size() > 0) {
|
||||
createAutoConstraints(sugConstr3, currentgeoid, Sketcher::PointPos::none);
|
||||
sugConstr3.clear();
|
||||
}
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
// This code enables the continuous creation mode.
|
||||
if (constrMethod == 0) {
|
||||
method = CENTER_PERIAPSIS_B;
|
||||
mode = STATUS_SEEK_CENTROID;
|
||||
} else {
|
||||
method = PERIAPSIS_APOAPSIS_B;
|
||||
mode = STATUS_SEEK_PERIAPSIS;
|
||||
}
|
||||
editCurve.clear();
|
||||
drawEdit(editCurve);
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
editCurve.resize(33);
|
||||
applyCursor();
|
||||
/* It is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerEllipse_H
|
||||
|
||||
324
src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h
Normal file
324
src/Mod/Sketcher/Gui/DrawSketchHandlerExtend.h
Normal file
@@ -0,0 +1,324 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerExtend_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerExtend_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class ExtendSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
ExtendSelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate(nullPointer())
|
||||
, object(obj)
|
||||
, disabled(false)
|
||||
{}
|
||||
|
||||
bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
if (pObj != this->object)
|
||||
return false;
|
||||
if (!sSubName || sSubName[0] == '\0')
|
||||
return false;
|
||||
if (disabled)
|
||||
return true;
|
||||
std::string element(sSubName);
|
||||
if (element.substr(0, 4) == "Edge") {
|
||||
int GeoId = std::atoi(element.substr(4, 4000).c_str()) - 1;
|
||||
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
const Part::Geometry *geom = Sketch->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setDisabled(bool isDisabled) {
|
||||
disabled = isDisabled;
|
||||
}
|
||||
protected:
|
||||
bool disabled;
|
||||
};
|
||||
|
||||
|
||||
class DrawSketchHandlerExtend: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerExtend()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, EditCurve(2)
|
||||
, BaseGeoId(-1)
|
||||
, ExtendFromStart(false)
|
||||
, SavedExtendFromStart(false)
|
||||
, Increment(0) {}
|
||||
|
||||
virtual ~DrawSketchHandlerExtend()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First,
|
||||
STATUS_SEEK_Second,
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode == STATUS_SEEK_Second) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geom);
|
||||
// project point to the existing curve
|
||||
Base::Vector3d start3d = lineSeg->getStartPoint();
|
||||
Base::Vector3d end3d = lineSeg->getEndPoint();
|
||||
|
||||
Base::Vector2d startPoint = Base::Vector2d(start3d.x, start3d.y);
|
||||
Base::Vector2d endPoint = Base::Vector2d(end3d.x, end3d.y);
|
||||
Base::Vector2d recenteredLine = endPoint - startPoint;
|
||||
Base::Vector2d recenteredPoint = onSketchPos - startPoint;
|
||||
Base::Vector2d projection;
|
||||
projection.ProjectToLine(recenteredPoint, recenteredLine);
|
||||
if (recenteredPoint.Length() < recenteredPoint.Distance(recenteredLine)) {
|
||||
EditCurve[0] = startPoint + projection;
|
||||
EditCurve[1] = endPoint;
|
||||
} else {
|
||||
EditCurve[0] = startPoint;
|
||||
EditCurve[1] = startPoint + projection;
|
||||
}
|
||||
/**
|
||||
* If in-curve, the intuitive behavior is for the line to shrink an amount from
|
||||
* the original click-point.
|
||||
*
|
||||
* If out-of-curve, the intuitive behavior is for the closest line endpoint to
|
||||
* expand.
|
||||
*/
|
||||
bool inCurve = (projection.Length() < recenteredLine.Length()
|
||||
&& projection.GetAngle(recenteredLine) < 0.1); // Two possible values here, M_PI and 0, but 0.1 is to avoid floating point problems.
|
||||
if (inCurve) {
|
||||
Increment = SavedExtendFromStart ? -1 * projection.Length() : projection.Length() - recenteredLine.Length();
|
||||
ExtendFromStart = SavedExtendFromStart;
|
||||
} else {
|
||||
ExtendFromStart = onSketchPos.Distance(startPoint) < onSketchPos.Distance(endPoint);
|
||||
Increment = ExtendFromStart ? projection.Length() : projection.Length() - recenteredLine.Length();
|
||||
}
|
||||
drawEdit(EditCurve);
|
||||
|
||||
} else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
Base::Vector3d center = arc->getCenter();
|
||||
double radius = arc->getRadius();
|
||||
|
||||
double start, end;
|
||||
arc->getRange(start, end, true);
|
||||
double arcAngle = end - start;
|
||||
|
||||
Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y);
|
||||
Base::Vector2d startAngle = Base::Vector2d(cos(start), sin(start));
|
||||
Base::Vector2d endAngle = Base::Vector2d(cos(end), sin(end));
|
||||
|
||||
Base::Vector2d arcHalf = Base::Vector2d(cos(start + arcAngle/ 2.0), sin(start+ arcAngle / 2.0));
|
||||
double angleToEndAngle = angle.GetAngle(endAngle);
|
||||
double angleToStartAngle = angle.GetAngle(startAngle);
|
||||
|
||||
|
||||
double modStartAngle = start;
|
||||
double modArcAngle = end - start;
|
||||
bool outOfArc = arcHalf.GetAngle(angle) * 2.0 > arcAngle;
|
||||
if (ExtendFromStart) {
|
||||
bool isCCWFromStart = crossProduct(angle, startAngle) < 0;
|
||||
if (outOfArc) {
|
||||
if (isCCWFromStart) {
|
||||
modStartAngle -= 2*M_PI - angleToStartAngle;
|
||||
modArcAngle += 2*M_PI - angleToStartAngle;
|
||||
} else {
|
||||
modStartAngle -= angleToStartAngle;
|
||||
modArcAngle += angleToStartAngle;
|
||||
}
|
||||
} else {
|
||||
if (isCCWFromStart) {
|
||||
modStartAngle += angleToStartAngle;
|
||||
modArcAngle -= angleToStartAngle;
|
||||
} else {
|
||||
modStartAngle += 2*M_PI - angleToStartAngle;
|
||||
modArcAngle -= 2*M_PI - angleToStartAngle;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool isCWFromEnd = crossProduct(angle, endAngle) >= 0;
|
||||
if (outOfArc) {
|
||||
if (isCWFromEnd) {
|
||||
modArcAngle += 2*M_PI - angleToEndAngle;
|
||||
} else {
|
||||
modArcAngle += angleToEndAngle;
|
||||
}
|
||||
} else {
|
||||
if (isCWFromEnd) {
|
||||
modArcAngle -= angleToEndAngle;
|
||||
} else {
|
||||
modArcAngle -= 2*M_PI - angleToEndAngle;
|
||||
}
|
||||
}
|
||||
}
|
||||
Increment = modArcAngle - (end - start);
|
||||
for (int i = 0; i < 31; i++) {
|
||||
double angle = modStartAngle + i * modArcAngle/30.0;
|
||||
EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle));
|
||||
}
|
||||
drawEdit(EditCurve);
|
||||
}
|
||||
int curveId = getPreselectCurve();
|
||||
if (BaseGeoId != curveId && seekAutoConstraint(SugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(SugConstr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
BaseGeoId = getPreselectCurve();
|
||||
if (BaseGeoId > -1) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *seg = static_cast<const Part::GeomLineSegment *>(geom);
|
||||
Base::Vector3d start3d = seg->getStartPoint();
|
||||
Base::Vector3d end3d = seg->getEndPoint();
|
||||
Base::Vector2d start = Base::Vector2d(start3d.x, start3d.y);
|
||||
Base::Vector2d end = Base::Vector2d(end3d.x, end3d.y);
|
||||
SavedExtendFromStart = (onSketchPos.Distance(start) < onSketchPos.Distance(end));
|
||||
ExtendFromStart = SavedExtendFromStart;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
} else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
double start, end;
|
||||
arc->getRange(start, end, true);
|
||||
|
||||
Base::Vector3d center = arc->getCenter();
|
||||
Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y);
|
||||
double angleToStart = angle.GetAngle(Base::Vector2d(cos(start), sin(start)));
|
||||
double angleToEnd = angle.GetAngle(Base::Vector2d(cos(end), sin(end)));
|
||||
ExtendFromStart = (angleToStart < angleToEnd); // move start point if closer to angle than end point
|
||||
EditCurve.resize(31);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
filterGate->setDisabled(true);
|
||||
}
|
||||
} else if (Mode == STATUS_SEEK_Second) {
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Extend edge"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "extend(%d, %f, %d)\n", // GeoId, increment, PointPos
|
||||
BaseGeoId, Increment, ExtendFromStart ? static_cast<int>(Sketcher::PointPos::start) : static_cast<int>(Sketcher::PointPos::end));
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool autoRecompute = hGrp->GetBool("AutoRecompute",false);
|
||||
if(autoRecompute)
|
||||
Gui::Command::updateActive();
|
||||
|
||||
// constrain chosen point
|
||||
if (SugConstr.size() > 0) {
|
||||
createAutoConstraints(SugConstr, BaseGeoId, (ExtendFromStart) ? Sketcher::PointPos::start : Sketcher::PointPos::end);
|
||||
SugConstr.clear();
|
||||
}
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
filterGate->setDisabled(false);
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
} else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to extend edge: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
} else { // exit extension tool if user clicked on empty space
|
||||
BaseGeoId = -1;
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void activated() override
|
||||
{
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
filterGate = new ExtendSelection(sketchgui->getObject());
|
||||
Gui::Selection().addSelectionGate(filterGate);
|
||||
}
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Extension");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
int BaseGeoId;
|
||||
ExtendSelection* filterGate = nullptr;
|
||||
bool ExtendFromStart; // if true, extend from start, else extend from end (circle only)
|
||||
bool SavedExtendFromStart;
|
||||
double Increment;
|
||||
std::vector<AutoConstraint> SugConstr;
|
||||
|
||||
private:
|
||||
int crossProduct(Base::Vector2d &vec1, Base::Vector2d &vec2) {
|
||||
return vec1.x * vec2.y - vec1.y * vec2.x;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerExtend_H
|
||||
|
||||
199
src/Mod/Sketcher/Gui/DrawSketchHandlerExternal.h
Normal file
199
src/Mod/Sketcher/Gui/DrawSketchHandlerExternal.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerExternal_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerExternal_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class ExternalSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
ExternalSelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate(nullPointer()), object(obj)
|
||||
{}
|
||||
|
||||
bool allow(App::Document *pDoc, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
Sketcher::SketchObject *sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
|
||||
this->notAllowedReason = "";
|
||||
Sketcher::SketchObject::eReasonList msg;
|
||||
if (!sketch->isExternalAllowed(pDoc, pObj, &msg)){
|
||||
switch(msg){
|
||||
case Sketcher::SketchObject::rlCircularReference:
|
||||
this->notAllowedReason = QT_TR_NOOP("Linking this will cause circular dependency.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherDoc:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object is in another document.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherBody:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object belongs to another body, can't link.");
|
||||
break;
|
||||
case Sketcher::SketchObject::rlOtherPart:
|
||||
this->notAllowedReason = QT_TR_NOOP("This object belongs to another part, can't link.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: its better to search the support of the sketch in case the sketch support is a base plane
|
||||
//Part::BodyBase* body = Part::BodyBase::findBodyOf(sketch);
|
||||
//if ( body && body->hasFeature ( pObj ) && body->isAfter ( pObj, sketch ) ) {
|
||||
// Don't allow selection after the sketch in the same body
|
||||
// NOTE: allowness of features in other bodies is handled by SketchObject::isExternalAllowed()
|
||||
// TODO may be this should be in SketchObject::isExternalAllowed() (2015-08-07, Fat-Zer)
|
||||
//return false;
|
||||
//}
|
||||
|
||||
if (!sSubName || sSubName[0] == '\0')
|
||||
return false;
|
||||
std::string element(sSubName);
|
||||
if ((element.size() > 4 && element.substr(0,4) == "Edge") ||
|
||||
(element.size() > 6 && element.substr(0,6) == "Vertex") ||
|
||||
(element.size() > 4 && element.substr(0,4) == "Face")) {
|
||||
return true;
|
||||
}
|
||||
if (pObj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) ||
|
||||
pObj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class DrawSketchHandlerExternal: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerExternal() = default;
|
||||
virtual ~DrawSketchHandlerExternal()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Gui::Selection().getPreselection().pObjectName)
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool onSelectionChanged(const Gui::SelectionChanges& msg) override
|
||||
{
|
||||
if (msg.Type == Gui::SelectionChanges::AddSelection) {
|
||||
App::DocumentObject* obj = sketchgui->getObject()->getDocument()->getObject(msg.pObjectName);
|
||||
if (obj == nullptr)
|
||||
throw Base::ValueError("Sketcher: External geometry: Invalid object in selection");
|
||||
std::string subName(msg.pSubName);
|
||||
if (obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) ||
|
||||
obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId()) ||
|
||||
(subName.size() > 4 && subName.substr(0,4) == "Edge") ||
|
||||
(subName.size() > 6 && subName.substr(0,6) == "Vertex") ||
|
||||
(subName.size() > 4 && subName.substr(0,4) == "Face")) {
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add external geometry"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addExternal(\"%s\",\"%s\")",
|
||||
msg.pObjectName, msg.pSubName);
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// adding external geometry does not require a solve() per se (the DoF is the same),
|
||||
// however a solve is required to update the amount of solver geometry, because we only
|
||||
// redraw a changed Sketch if the solver geometry amount is the same as the SkethObject
|
||||
// geometry amount (as this avoids other issues).
|
||||
// This solver is a very low cost one anyway (there is actually nothing to solve).
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
Gui::Selection().clearSelection();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add external geometry: %s\n", e.what());
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void activated() override
|
||||
{
|
||||
setAxisPickStyle(false);
|
||||
Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView();
|
||||
Gui::View3DInventorViewer *viewer;
|
||||
viewer = static_cast<Gui::View3DInventor *>(mdi)->getViewer();
|
||||
|
||||
SoNode* root = viewer->getSceneGraph();
|
||||
static_cast<Gui::SoFCUnifiedSelection*>(root)->selectionRole.setValue(true);
|
||||
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
Gui::Selection().addSelectionGate(new ExternalSelection(sketchgui->getObject()));
|
||||
}
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_External");
|
||||
}
|
||||
|
||||
virtual void deactivated() override
|
||||
{
|
||||
Q_UNUSED(sketchgui);
|
||||
setAxisPickStyle(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerExternal_H
|
||||
|
||||
291
src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h
Normal file
291
src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h
Normal file
@@ -0,0 +1,291 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerFillet_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerFillet_H
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class FilletSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
FilletSelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate(nullPointer()), object(obj)
|
||||
{}
|
||||
|
||||
bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
if (pObj != this->object)
|
||||
return false;
|
||||
if (!sSubName || sSubName[0] == '\0')
|
||||
return false;
|
||||
std::string element(sSubName);
|
||||
if (element.substr(0,4) == "Edge") {
|
||||
int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1;
|
||||
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
const Part::Geometry *geom = Sketch->getGeometry(GeoId);
|
||||
if (geom->getTypeId().isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId()))
|
||||
return true;
|
||||
}
|
||||
if (element.substr(0,6) == "Vertex") {
|
||||
int VtId = std::atoi(element.substr(6,4000).c_str()) - 1;
|
||||
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
std::vector<int> GeoIdList;
|
||||
std::vector<Sketcher::PointPos> PosIdList;
|
||||
Sketch->getDirectlyCoincidentPoints(VtId, GeoIdList, PosIdList);
|
||||
if (GeoIdList.size() == 2 && GeoIdList[0] >= 0 && GeoIdList[1] >= 0) {
|
||||
const Part::Geometry *geom1 = Sketch->getGeometry(GeoIdList[0]);
|
||||
const Part::Geometry *geom2 = Sketch->getGeometry(GeoIdList[1]);
|
||||
if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
|
||||
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class DrawSketchHandlerFillet: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
enum FilletType {
|
||||
SimpleFillet,
|
||||
ConstraintPreservingFillet
|
||||
};
|
||||
|
||||
DrawSketchHandlerFillet(FilletType filletType) : filletType(filletType), Mode(STATUS_SEEK_First), firstCurve(0) {}
|
||||
virtual ~DrawSketchHandlerFillet()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
|
||||
enum SelectMode{
|
||||
STATUS_SEEK_First,
|
||||
STATUS_SEEK_Second
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
bool construction=false;
|
||||
int VtId = getPreselectPoint();
|
||||
if (Mode == STATUS_SEEK_First && VtId != -1) {
|
||||
int GeoId;
|
||||
Sketcher::PointPos PosId=Sketcher::PointPos::none;
|
||||
sketchgui->getSketchObject()->getGeoVertexIndex(VtId,GeoId,PosId);
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
|
||||
(PosId == Sketcher::PointPos::start || PosId == Sketcher::PointPos::end)) {
|
||||
|
||||
// guess fillet radius
|
||||
double radius=-1;
|
||||
std::vector<int> GeoIdList;
|
||||
std::vector<Sketcher::PointPos> PosIdList;
|
||||
sketchgui->getSketchObject()->getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList);
|
||||
if (GeoIdList.size() == 2 && GeoIdList[0] >= 0 && GeoIdList[1] >= 0) {
|
||||
const Part::Geometry *geom1 = sketchgui->getSketchObject()->getGeometry(GeoIdList[0]);
|
||||
const Part::Geometry *geom2 = sketchgui->getSketchObject()->getGeometry(GeoIdList[1]);
|
||||
construction=Sketcher::GeometryFacade::getConstruction(geom1) && Sketcher::GeometryFacade::getConstruction(geom2);
|
||||
if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
|
||||
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>(geom1);
|
||||
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>(geom2);
|
||||
Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint();
|
||||
Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint();
|
||||
if (PosIdList[0] == Sketcher::PointPos::end)
|
||||
dir1 *= -1;
|
||||
if (PosIdList[1] == Sketcher::PointPos::end)
|
||||
dir2 *= -1;
|
||||
double l1 = dir1.Length();
|
||||
double l2 = dir2.Length();
|
||||
double angle = dir1.GetAngle(dir2);
|
||||
radius = (l1 < l2 ? l1 : l2) * 0.2 * sin(angle/2);
|
||||
}
|
||||
}
|
||||
if (radius < 0)
|
||||
return false;
|
||||
|
||||
int currentgeoid= getHighestCurveIndex();
|
||||
// create fillet at point
|
||||
try {
|
||||
bool pointFillet = (filletType == 1);
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "fillet(%d,%d,%f,%s,%s)", GeoId, static_cast<int>(PosId), radius, "True",
|
||||
pointFillet ? "True":"False");
|
||||
|
||||
if (construction) {
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "toggleConstruction(%d) ", currentgeoid+1);
|
||||
}
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to create fillet: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int GeoId = getPreselectCurve();
|
||||
if (GeoId > -1) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
|
||||
if (geom->getTypeId().isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId())) {
|
||||
if (Mode==STATUS_SEEK_First) {
|
||||
firstCurve = GeoId;
|
||||
firstPos = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
// add the line to the selection
|
||||
std::stringstream ss;
|
||||
ss << "Edge" << firstCurve + 1;
|
||||
Gui::Selection().addSelection(sketchgui->getSketchObject()->getDocument()->getName()
|
||||
,sketchgui->getSketchObject()->getNameInDocument()
|
||||
,ss.str().c_str()
|
||||
,onSketchPos.x
|
||||
,onSketchPos.y
|
||||
,0.f);
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second) {
|
||||
int secondCurve = GeoId;
|
||||
Base::Vector2d secondPos = onSketchPos;
|
||||
|
||||
Base::Vector3d refPnt1(firstPos.x, firstPos.y, 0.f);
|
||||
Base::Vector3d refPnt2(secondPos.x, secondPos.y, 0.f);
|
||||
|
||||
const Part::Geometry *geom1 = sketchgui->getSketchObject()->getGeometry(firstCurve);
|
||||
|
||||
double radius = 0;
|
||||
|
||||
if( geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
|
||||
geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
// guess fillet radius
|
||||
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>
|
||||
(sketchgui->getSketchObject()->getGeometry(firstCurve));
|
||||
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>
|
||||
(sketchgui->getSketchObject()->getGeometry(secondCurve));
|
||||
|
||||
radius = Part::suggestFilletRadius(lineSeg1, lineSeg2, refPnt1, refPnt2);
|
||||
if (radius < 0)
|
||||
return false;
|
||||
|
||||
construction=Sketcher::GeometryFacade::getConstruction(lineSeg1) && Sketcher::GeometryFacade::getConstruction(lineSeg2);
|
||||
}
|
||||
else { // other supported curves
|
||||
const Part::Geometry *geo1 = static_cast<const Part::Geometry *>
|
||||
(sketchgui->getSketchObject()->getGeometry(firstCurve));
|
||||
const Part::Geometry *geo2 = static_cast<const Part::Geometry *>
|
||||
(sketchgui->getSketchObject()->getGeometry(secondCurve));
|
||||
|
||||
construction=Sketcher::GeometryFacade::getConstruction(geo1) && Sketcher::GeometryFacade::getConstruction(geo2);
|
||||
}
|
||||
|
||||
|
||||
int currentgeoid= getHighestCurveIndex();
|
||||
|
||||
// create fillet between lines
|
||||
try {
|
||||
bool pointFillet = (filletType == 1);
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s)",
|
||||
firstCurve, secondCurve,
|
||||
firstPos.x, firstPos.y,
|
||||
secondPos.x, secondPos.y, radius,
|
||||
"True", pointFillet ? "True":"False");
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::CADKernelError& e) {
|
||||
e.ReportException();
|
||||
if(e.getTranslatable()) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("CAD Kernel Error"),
|
||||
QObject::tr(e.getMessage().c_str()));
|
||||
}
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Command::abortCommand();
|
||||
Mode = STATUS_SEEK_First;
|
||||
}
|
||||
catch (const Base::ValueError& e) {
|
||||
e.ReportException();
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Command::abortCommand();
|
||||
Mode = STATUS_SEEK_First;
|
||||
}
|
||||
|
||||
tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
if(construction) {
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "toggleConstruction(%d) ",
|
||||
currentgeoid+1);
|
||||
}
|
||||
|
||||
|
||||
Gui::Selection().clearSelection();
|
||||
Mode = STATUS_SEEK_First;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (VtId < 0 && GeoId < 0) // exit the fillet tool if the user clicked on empty space
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject()));
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Fillet");
|
||||
}
|
||||
|
||||
protected:
|
||||
int filletType;
|
||||
SelectMode Mode;
|
||||
int firstCurve;
|
||||
Base::Vector2d firstPos;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerFillet_H
|
||||
|
||||
165
src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h
Normal file
165
src/Mod/Sketcher/Gui/DrawSketchHandlerLine.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerLine_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerLine_H
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerLine: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerLine():Mode(STATUS_SEEK_First),EditCurve(2){}
|
||||
virtual ~DrawSketchHandlerLine(){}
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
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){
|
||||
float length = (onSketchPos - EditCurve[0]).Length();
|
||||
float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2d(1.f,0.f));
|
||||
SbString text;
|
||||
text.sprintf(" (%.1f,%.1fdeg)", length, angle * 180 / M_PI);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
EditCurve[1] = onSketchPos;
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else {
|
||||
EditCurve[1] = onSketchPos;
|
||||
drawEdit(EditCurve);
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode==STATUS_End){
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch line"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)",
|
||||
EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add line: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool avoidredundant = sketchgui->AvoidRedundant.getValue() && sketchgui->Autoconstraints.getValue();
|
||||
|
||||
if(avoidredundant)
|
||||
removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),sugConstr1,sugConstr2);
|
||||
|
||||
// add auto constraints for the line segment start
|
||||
if (!sugConstr1.empty()) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::start);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add auto constraints for the line segment end
|
||||
if (!sugConstr2.empty()) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::end);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* It is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Line");
|
||||
}
|
||||
|
||||
protected:
|
||||
SelectMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerLine_H
|
||||
|
||||
685
src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h
Normal file
685
src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h
Normal file
@@ -0,0 +1,685 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerLineSet_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerLineSet_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerLineSet: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerLineSet()
|
||||
: Mode(STATUS_SEEK_First), SegmentMode(SEGMENT_MODE_Line)
|
||||
, TransitionMode(TRANSITION_MODE_Free)
|
||||
, SnapMode(SNAP_MODE_Free)
|
||||
, suppressTransition(false)
|
||||
, EditCurve(2)
|
||||
, firstCurve(-1)
|
||||
, previousCurve(-1)
|
||||
, firstPosId(Sketcher::PointPos::none)
|
||||
, previousPosId(Sketcher::PointPos::none)
|
||||
, startAngle(0)
|
||||
, endAngle(0)
|
||||
, arcRadius(0)
|
||||
, firstsegment(true) {}
|
||||
|
||||
virtual ~DrawSketchHandlerLineSet() = default;
|
||||
|
||||
/// mode table
|
||||
enum SELECT_MODE {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_Do,
|
||||
STATUS_Close
|
||||
};
|
||||
|
||||
enum SEGMENT_MODE
|
||||
{
|
||||
SEGMENT_MODE_Arc,
|
||||
SEGMENT_MODE_Line
|
||||
};
|
||||
|
||||
enum TRANSITION_MODE
|
||||
{
|
||||
TRANSITION_MODE_Free,
|
||||
TRANSITION_MODE_Tangent,
|
||||
TRANSITION_MODE_Perpendicular_L,
|
||||
TRANSITION_MODE_Perpendicular_R
|
||||
};
|
||||
|
||||
enum SNAP_MODE
|
||||
{
|
||||
SNAP_MODE_Free,
|
||||
SNAP_MODE_45Degree
|
||||
};
|
||||
|
||||
virtual void registerPressedKey(bool pressed, int key) override
|
||||
{
|
||||
if (Mode != STATUS_SEEK_Second)
|
||||
return; // SegmentMode can be changed only in STATUS_SEEK_Second mode
|
||||
|
||||
if (key == SoKeyboardEvent::M && pressed && previousCurve != -1) {
|
||||
// loop through the following modes:
|
||||
// SEGMENT_MODE_Line, TRANSITION_MODE_Free / TRANSITION_MODE_Tangent
|
||||
// SEGMENT_MODE_Line, TRANSITION_MODE_Perpendicular_L
|
||||
// SEGMENT_MODE_Line, TRANSITION_MODE_Tangent / TRANSITION_MODE_Free
|
||||
// SEGMENT_MODE_Arc, TRANSITION_MODE_Tangent
|
||||
// SEGMENT_MODE_Arc, TRANSITION_MODE_Perpendicular_L
|
||||
// SEGMENT_MODE_Arc, TRANSITION_MODE_Perpendicular_R
|
||||
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
|
||||
Base::Vector2d onSketchPos;
|
||||
if (SegmentMode == SEGMENT_MODE_Line)
|
||||
onSketchPos = EditCurve[EditCurve.size()-1];
|
||||
else
|
||||
onSketchPos = EditCurve[29];
|
||||
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(previousCurve);
|
||||
|
||||
if (SegmentMode == SEGMENT_MODE_Line) {
|
||||
switch (TransitionMode) {
|
||||
case TRANSITION_MODE_Free:
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { // 3rd mode
|
||||
SegmentMode = SEGMENT_MODE_Arc;
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
}
|
||||
else // 1st mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_L;
|
||||
break;
|
||||
case TRANSITION_MODE_Perpendicular_L: // 2nd mode
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
else
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
break;
|
||||
case TRANSITION_MODE_Tangent:
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) // 1st mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_L;
|
||||
else { // 3rd mode
|
||||
SegmentMode = SEGMENT_MODE_Arc;
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
}
|
||||
break;
|
||||
default: // unexpected mode
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (TransitionMode) {
|
||||
case TRANSITION_MODE_Tangent: // 4th mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_L;
|
||||
break;
|
||||
case TRANSITION_MODE_Perpendicular_L: // 5th mode
|
||||
TransitionMode = TRANSITION_MODE_Perpendicular_R;
|
||||
break;
|
||||
default: // 6th mode (Perpendicular_R) + unexpected mode
|
||||
SegmentMode = SEGMENT_MODE_Line;
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
else
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (SegmentMode == SEGMENT_MODE_Line)
|
||||
EditCurve.resize(TransitionMode == TRANSITION_MODE_Free ? 2 : 3);
|
||||
else
|
||||
EditCurve.resize(32);
|
||||
mouseMove(onSketchPos); // trigger an update of EditCurve
|
||||
}
|
||||
}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
suppressTransition = false;
|
||||
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){
|
||||
if (SegmentMode == SEGMENT_MODE_Line) {
|
||||
EditCurve[EditCurve.size()-1] = onSketchPos;
|
||||
if (TransitionMode == TRANSITION_MODE_Tangent) {
|
||||
Base::Vector2d Tangent(dirVec.x,dirVec.y);
|
||||
EditCurve[1].ProjectToLine(EditCurve[2] - EditCurve[0], Tangent);
|
||||
if (EditCurve[1] * Tangent < 0) {
|
||||
EditCurve[1] = EditCurve[2];
|
||||
suppressTransition = true;
|
||||
}
|
||||
else
|
||||
EditCurve[1] = EditCurve[0] + EditCurve[1];
|
||||
}
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_L ||
|
||||
TransitionMode == TRANSITION_MODE_Perpendicular_R) {
|
||||
Base::Vector2d Perpendicular(-dirVec.y,dirVec.x);
|
||||
EditCurve[1].ProjectToLine(EditCurve[2] - EditCurve[0], Perpendicular);
|
||||
EditCurve[1] = EditCurve[0] + EditCurve[1];
|
||||
}
|
||||
|
||||
drawEdit(EditCurve);
|
||||
|
||||
float length = (EditCurve[1] - EditCurve[0]).Length();
|
||||
float angle = (EditCurve[1] - EditCurve[0]).GetAngle(Base::Vector2d(1.f,0.f));
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1f,%.1fdeg)", length, angle * 180 / M_PI);
|
||||
setPositionText(EditCurve[1], text);
|
||||
|
||||
if (TransitionMode == TRANSITION_MODE_Free) {
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SegmentMode == SEGMENT_MODE_Arc) {
|
||||
|
||||
if(QApplication::keyboardModifiers() == Qt::ControlModifier)
|
||||
SnapMode = SNAP_MODE_45Degree;
|
||||
else
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
|
||||
Base::Vector2d Tangent;
|
||||
if (TransitionMode == TRANSITION_MODE_Tangent)
|
||||
Tangent = Base::Vector2d(dirVec.x,dirVec.y);
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_L)
|
||||
Tangent = Base::Vector2d(-dirVec.y,dirVec.x);
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_R)
|
||||
Tangent = Base::Vector2d(dirVec.y,-dirVec.x);
|
||||
|
||||
double theta = Tangent.GetAngle(onSketchPos - EditCurve[0]);
|
||||
|
||||
arcRadius = (onSketchPos - EditCurve[0]).Length()/(2.0*sin(theta));
|
||||
|
||||
// At this point we need a unit normal vector pointing towards
|
||||
// the center of the arc we are drawing. Derivation of the formula
|
||||
// used here can be found at http://people.richland.edu/james/lecture/m116/matrices/area.html
|
||||
double x1 = EditCurve[0].x;
|
||||
double y1 = EditCurve[0].y;
|
||||
double x2 = x1 + Tangent.x;
|
||||
double y2 = y1 + Tangent.y;
|
||||
double x3 = onSketchPos.x;
|
||||
double y3 = onSketchPos.y;
|
||||
if ((x2*y3-x3*y2)-(x1*y3-x3*y1)+(x1*y2-x2*y1) > 0)
|
||||
arcRadius *= -1;
|
||||
if (boost::math::isnan(arcRadius) || boost::math::isinf(arcRadius))
|
||||
arcRadius = 0.f;
|
||||
|
||||
CenterPoint = EditCurve[0] + Base::Vector2d(arcRadius * Tangent.y, -arcRadius * Tangent.x);
|
||||
|
||||
double rx = EditCurve[0].x - CenterPoint.x;
|
||||
double ry = EditCurve[0].y - CenterPoint.y;
|
||||
|
||||
startAngle = atan2(ry,rx);
|
||||
|
||||
double rxe = onSketchPos.x - CenterPoint.x;
|
||||
double rye = onSketchPos.y - CenterPoint.y;
|
||||
double arcAngle = atan2(-rxe*ry + rye*rx, rxe*rx + rye*ry);
|
||||
if (boost::math::isnan(arcAngle) || boost::math::isinf(arcAngle))
|
||||
arcAngle = 0.f;
|
||||
if (arcRadius >= 0 && arcAngle > 0)
|
||||
arcAngle -= 2*M_PI;
|
||||
if (arcRadius < 0 && arcAngle < 0)
|
||||
arcAngle += 2*M_PI;
|
||||
|
||||
if (SnapMode == SNAP_MODE_45Degree)
|
||||
arcAngle = round(arcAngle / (M_PI/4)) * M_PI/4;
|
||||
|
||||
endAngle = startAngle + arcAngle;
|
||||
|
||||
for (int i=1; i <= 29; i++) {
|
||||
double angle = i*arcAngle/29.0;
|
||||
double dx = rx * cos(angle) - ry * sin(angle);
|
||||
double dy = rx * sin(angle) + ry * cos(angle);
|
||||
EditCurve[i] = Base::Vector2d(CenterPoint.x + dx, CenterPoint.y + dy);
|
||||
}
|
||||
|
||||
EditCurve[30] = CenterPoint;
|
||||
EditCurve[31] = EditCurve[0];
|
||||
|
||||
drawEdit(EditCurve);
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR,%.1fdeg)", std::abs(arcRadius), arcAngle * 180 / M_PI);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
|
||||
EditCurve[0] = onSketchPos; // this may be overwritten if previousCurve is found
|
||||
|
||||
virtualsugConstr1 = sugConstr1; // store original autoconstraints.
|
||||
|
||||
// here we check if there is a preselected point and
|
||||
// we set up a transition from the neighbouring segment.
|
||||
// (peviousCurve, previousPosId, dirVec, TransitionMode)
|
||||
for (unsigned int i=0; i < sugConstr1.size(); i++)
|
||||
if (sugConstr1[i].Type == Sketcher::Coincident) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(sugConstr1[i].GeoId);
|
||||
if ((geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) &&
|
||||
(sugConstr1[i].PosId == Sketcher::PointPos::start ||
|
||||
sugConstr1[i].PosId == Sketcher::PointPos::end)) {
|
||||
previousCurve = sugConstr1[i].GeoId;
|
||||
previousPosId = sugConstr1[i].PosId;
|
||||
updateTransitionData(previousCurve,previousPosId); // -> dirVec, EditCurve[0]
|
||||
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
}
|
||||
sugConstr1.erase(sugConstr1.begin()+i); // actually we should clear the vector completely
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remember our first point (even if we are doing a transition from a previous curve)
|
||||
firstCurve = getHighestCurveIndex() + 1;
|
||||
firstPosId = Sketcher::PointPos::start;
|
||||
|
||||
if (SegmentMode == SEGMENT_MODE_Line)
|
||||
EditCurve.resize(TransitionMode == TRANSITION_MODE_Free ? 2 : 3);
|
||||
else if (SegmentMode == SEGMENT_MODE_Arc)
|
||||
EditCurve.resize(32);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if (Mode == STATUS_SEEK_Second) {
|
||||
// exit on clicking exactly at the same position (e.g. double click)
|
||||
if (onSketchPos == EditCurve[0]) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
SegmentMode=SEGMENT_MODE_Line;
|
||||
TransitionMode=TRANSITION_MODE_Free;
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
suppressTransition=false;
|
||||
firstCurve=-1;
|
||||
previousCurve=-1;
|
||||
firstPosId=Sketcher::PointPos::none;
|
||||
previousPosId=Sketcher::PointPos::none;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Mode = STATUS_Do;
|
||||
|
||||
if (getPreselectPoint() != -1 && firstPosId != Sketcher::PointPos::none) {
|
||||
int GeoId;
|
||||
Sketcher::PointPos PosId;
|
||||
sketchgui->getSketchObject()->getGeoVertexIndex(getPreselectPoint(),GeoId,PosId);
|
||||
if (sketchgui->getSketchObject()->arePointsCoincident(GeoId,PosId,firstCurve,firstPosId))
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
else if (getPreselectCross() == 0 && firstPosId != Sketcher::PointPos::none) {
|
||||
// close line started at root point
|
||||
if (sketchgui->getSketchObject()->arePointsCoincident(-1,Sketcher::PointPos::start,firstCurve,firstPosId))
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode == STATUS_Do || Mode == STATUS_Close) {
|
||||
bool addedGeometry = true;
|
||||
if (SegmentMode == SEGMENT_MODE_Line) {
|
||||
// issue the geometry
|
||||
try {
|
||||
// open the transaction
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add line to sketch wire"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)",
|
||||
EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
addedGeometry = false;
|
||||
Base::Console().Error("Failed to add line: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
firstsegment=false;
|
||||
}
|
||||
else if (SegmentMode == SEGMENT_MODE_Arc) { // We're dealing with an Arc
|
||||
if (!boost::math::isnormal(arcRadius)) {
|
||||
Mode = STATUS_SEEK_Second;
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add arc to sketch wire"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfCircle"
|
||||
"(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f),%s)",
|
||||
CenterPoint.x, CenterPoint.y, std::abs(arcRadius),
|
||||
std::min(startAngle,endAngle), std::max(startAngle,endAngle),
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
addedGeometry = false;
|
||||
Base::Console().Error("Failed to add arc: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
firstsegment=false;
|
||||
}
|
||||
|
||||
int lastCurve = getHighestCurveIndex();
|
||||
// issue the constraint
|
||||
if (addedGeometry && (previousPosId != Sketcher::PointPos::none)) {
|
||||
Sketcher::PointPos lastStartPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::PointPos::end : Sketcher::PointPos::start;
|
||||
Sketcher::PointPos lastEndPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::PointPos::start : Sketcher::PointPos::end;
|
||||
// in case of a tangency constraint, the coincident constraint is redundant
|
||||
std::string constrType = "Coincident";
|
||||
if (!suppressTransition && previousCurve != -1) {
|
||||
if (TransitionMode == TRANSITION_MODE_Tangent)
|
||||
constrType = "Tangent";
|
||||
else if (TransitionMode == TRANSITION_MODE_Perpendicular_L ||
|
||||
TransitionMode == TRANSITION_MODE_Perpendicular_R)
|
||||
constrType = "Perpendicular";
|
||||
}
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('%s',%i,%i,%i,%i)) ",
|
||||
constrType.c_str(), previousCurve, static_cast<int>(previousPosId), lastCurve, static_cast<int>(lastStartPosId));
|
||||
|
||||
if(SnapMode == SNAP_MODE_45Degree && Mode != STATUS_Close) {
|
||||
// -360, -315, -270, -225, -180, -135, -90, -45, 0, 45, 90, 135, 180, 225, 270, 315, 360
|
||||
// N/A, a, perp, a, par, a,perp, a,N/A, a,perp, a, par, a,perp, a, N/A
|
||||
|
||||
// #3974: if in radians, the printf %f defaults to six decimals, which leads to loss of precision
|
||||
double arcAngle = abs(round( (endAngle - startAngle) / (M_PI/4)) * 45); // in degrees
|
||||
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Angle',%i,App.Units.Quantity('%f deg'))) ",
|
||||
lastCurve, arcAngle);
|
||||
}
|
||||
if (Mode == STATUS_Close) {
|
||||
// close the loop by constrain to the first curve point
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident',%i,%i,%i,%i)) ",
|
||||
lastCurve,static_cast<int>(lastEndPosId),firstCurve,static_cast<int>(firstPosId));
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool avoidredundant = sketchgui->AvoidRedundant.getValue() && sketchgui->Autoconstraints.getValue();
|
||||
|
||||
if (Mode == STATUS_Close) {
|
||||
|
||||
if(avoidredundant) {
|
||||
if (SegmentMode == SEGMENT_MODE_Line) { // avoid redundant constraints.
|
||||
if (sugConstr1.size() > 0)
|
||||
removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),sugConstr1,sugConstr2);
|
||||
else
|
||||
removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),virtualsugConstr1,sugConstr2);
|
||||
}
|
||||
}
|
||||
|
||||
if (sugConstr2.size() > 0) {
|
||||
// exclude any coincidence constraints
|
||||
std::vector<AutoConstraint> sugConstr;
|
||||
for (unsigned int i=0; i < sugConstr2.size(); i++) {
|
||||
if (sugConstr2[i].Type != Sketcher::Coincident)
|
||||
sugConstr.push_back(sugConstr2[i]);
|
||||
}
|
||||
createAutoConstraints(sugConstr, getHighestCurveIndex(), Sketcher::PointPos::end);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
unsetCursor();
|
||||
|
||||
resetPositionText();
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
SegmentMode=SEGMENT_MODE_Line;
|
||||
TransitionMode=TRANSITION_MODE_Free;
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
suppressTransition=false;
|
||||
firstCurve=-1;
|
||||
previousCurve=-1;
|
||||
firstPosId=Sketcher::PointPos::none;
|
||||
previousPosId=Sketcher::PointPos::none;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
else {
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// Add auto constraints
|
||||
if (sugConstr1.size() > 0) { // this is relevant only to the very first point
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::start);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
|
||||
if(avoidredundant) {
|
||||
if (SegmentMode == SEGMENT_MODE_Line) { // avoid redundant constraints.
|
||||
if (sugConstr1.size() > 0)
|
||||
removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),sugConstr1,sugConstr2);
|
||||
else
|
||||
removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),virtualsugConstr1,sugConstr2);
|
||||
}
|
||||
}
|
||||
|
||||
virtualsugConstr1 = sugConstr2; // these are the initial constraints for the next iteration.
|
||||
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(),
|
||||
(SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::PointPos::start : Sketcher::PointPos::end);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
// remember the vertex for the next rounds constraint..
|
||||
previousCurve = getHighestCurveIndex();
|
||||
previousPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
|
||||
Sketcher::PointPos::start : Sketcher::PointPos::end; // cw arcs are rendered in reverse
|
||||
|
||||
// setup for the next line segment
|
||||
// calculate dirVec and EditCurve[0]
|
||||
updateTransitionData(previousCurve,previousPosId);
|
||||
|
||||
applyCursor();
|
||||
Mode = STATUS_SEEK_Second;
|
||||
if (SegmentMode == SEGMENT_MODE_Arc) {
|
||||
TransitionMode = TRANSITION_MODE_Tangent;
|
||||
EditCurve.resize(3);
|
||||
EditCurve[2] = EditCurve[0];
|
||||
}
|
||||
else {
|
||||
TransitionMode = TRANSITION_MODE_Free;
|
||||
EditCurve.resize(2);
|
||||
}
|
||||
SegmentMode = SEGMENT_MODE_Line;
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
EditCurve[1] = EditCurve[0];
|
||||
mouseMove(onSketchPos); // trigger an update of EditCurve
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void quit(void) override {
|
||||
// We must see if we need to create a B-spline before cancelling everything
|
||||
// and now just like any other Handler,
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if (firstsegment) {
|
||||
// user when right-clicking with no segment in really wants to exit
|
||||
DrawSketchHandler::quit();
|
||||
}
|
||||
else {
|
||||
|
||||
if(!continuousMode){
|
||||
DrawSketchHandler::quit();
|
||||
}
|
||||
else {
|
||||
// This code disregards existing data and enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
SegmentMode=SEGMENT_MODE_Line;
|
||||
TransitionMode=TRANSITION_MODE_Free;
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
suppressTransition=false;
|
||||
firstCurve=-1;
|
||||
previousCurve=-1;
|
||||
firstPosId=Sketcher::PointPos::none;
|
||||
previousPosId=Sketcher::PointPos::none;
|
||||
firstsegment=true;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(2);
|
||||
applyCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Lineset");
|
||||
}
|
||||
|
||||
protected:
|
||||
SELECT_MODE Mode;
|
||||
SEGMENT_MODE SegmentMode;
|
||||
TRANSITION_MODE TransitionMode;
|
||||
SNAP_MODE SnapMode;
|
||||
bool suppressTransition;
|
||||
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
int firstCurve;
|
||||
int previousCurve;
|
||||
Sketcher::PointPos firstPosId;
|
||||
Sketcher::PointPos previousPosId;
|
||||
// the latter stores those constraints that a first point would have been given in absence of the transition mechanism
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2, virtualsugConstr1;
|
||||
|
||||
Base::Vector2d CenterPoint;
|
||||
Base::Vector3d dirVec;
|
||||
double startAngle, endAngle, arcRadius;
|
||||
|
||||
bool firstsegment;
|
||||
|
||||
void updateTransitionData(int GeoId, Sketcher::PointPos PosId) {
|
||||
|
||||
// Use updated startPoint/endPoint as autoconstraints can modify the position
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geom);
|
||||
dirVec.Set(lineSeg->getEndPoint().x - lineSeg->getStartPoint().x,
|
||||
lineSeg->getEndPoint().y - lineSeg->getStartPoint().y,
|
||||
0.f);
|
||||
if (PosId == Sketcher::PointPos::start) {
|
||||
dirVec *= -1;
|
||||
EditCurve[0] = Base::Vector2d(lineSeg->getStartPoint().x, lineSeg->getStartPoint().y);
|
||||
}
|
||||
else
|
||||
EditCurve[0] = Base::Vector2d(lineSeg->getEndPoint().x, lineSeg->getEndPoint().y);
|
||||
}
|
||||
else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle *arcSeg = static_cast<const Part::GeomArcOfCircle *>(geom);
|
||||
if (PosId == Sketcher::PointPos::start) {
|
||||
EditCurve[0] = Base::Vector2d(arcSeg->getStartPoint(/*emulateCCW=*/true).x,arcSeg->getStartPoint(/*emulateCCW=*/true).y);
|
||||
dirVec = Base::Vector3d(0.f,0.f,-1.0) % (arcSeg->getStartPoint(/*emulateCCW=*/true)-arcSeg->getCenter());
|
||||
}
|
||||
else {
|
||||
EditCurve[0] = Base::Vector2d(arcSeg->getEndPoint(/*emulateCCW=*/true).x,arcSeg->getEndPoint(/*emulateCCW=*/true).y);
|
||||
dirVec = Base::Vector3d(0.f,0.f,1.0) % (arcSeg->getEndPoint(/*emulateCCW=*/true)-arcSeg->getCenter());
|
||||
}
|
||||
}
|
||||
dirVec.Normalize();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerLineSet_H
|
||||
|
||||
118
src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h
Normal file
118
src/Mod/Sketcher/Gui/DrawSketchHandlerPoint.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerPoint_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerPoint_H
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerPoint: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerPoint() : selectionDone(false) {}
|
||||
virtual ~DrawSketchHandlerPoint() {}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
setPositionText(onSketchPos);
|
||||
if (seekAutoConstraint(sugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr);
|
||||
return;
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
EditPoint = onSketchPos;
|
||||
selectionDone = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (selectionDone){
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch point"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
||||
EditPoint.x,EditPoint.y);
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add point: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
// add auto constraints for the line segment start
|
||||
if (sugConstr.size() > 0) {
|
||||
createAutoConstraints(sugConstr, getHighestCurveIndex(), Sketcher::PointPos::start);
|
||||
sugConstr.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
applyCursor();
|
||||
/* It is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Point");
|
||||
}
|
||||
|
||||
protected:
|
||||
bool selectionDone;
|
||||
Base::Vector2d EditPoint;
|
||||
std::vector<AutoConstraint> sugConstr;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerPoint_H
|
||||
|
||||
191
src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h
Normal file
191
src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerPolygon_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerPolygon_H
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include "SketcherRegularPolygonDialog.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerRegularPolygon: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerRegularPolygon( size_t nof_corners ):
|
||||
Corners( nof_corners ),
|
||||
AngleOfSeparation( 2.0*M_PI/static_cast<double>(Corners) ),
|
||||
cos_v( cos( AngleOfSeparation ) ),
|
||||
sin_v( sin( AngleOfSeparation ) ),
|
||||
Mode(STATUS_SEEK_First),
|
||||
EditCurve(Corners+1)
|
||||
{
|
||||
}
|
||||
virtual ~DrawSketchHandlerRegularPolygon(){}
|
||||
/// mode table
|
||||
enum SelectMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
|
||||
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) {
|
||||
EditCurve[0]= Base::Vector2d(onSketchPos.x, onSketchPos.y);
|
||||
EditCurve[Corners]= Base::Vector2d(onSketchPos.x, onSketchPos.y);
|
||||
|
||||
Base::Vector2d dV = onSketchPos - StartPos;
|
||||
double rx = dV.x;
|
||||
double ry = dV.y;
|
||||
for (int i=1; i < static_cast<int>(Corners); i++) {
|
||||
const double old_rx = rx;
|
||||
rx = cos_v * rx - sin_v * ry;
|
||||
ry = cos_v * ry + sin_v * old_rx;
|
||||
EditCurve[i] = Base::Vector2d(StartPos.x + rx, StartPos.y + ry);
|
||||
}
|
||||
|
||||
// Display radius for user
|
||||
const float radius = dV.Length();
|
||||
const float angle = ( 180.0 / M_PI ) * atan2( dV.y, dV.x );
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR %.1fdeg)", radius, angle );
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
StartPos = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else {
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode==STATUS_End){
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add hexagon"));
|
||||
|
||||
try {
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"import ProfileLib.RegularPolygon\n"
|
||||
"ProfileLib.RegularPolygon.makeRegularPolygon(%s,%i,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%s)",
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(),
|
||||
Corners,
|
||||
StartPos.x,StartPos.y,EditCurve[0].x,EditCurve[0].y,
|
||||
geometryCreationMode==Construction?"True":"False");
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// add auto constraints at the center of the polygon
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add auto constraints to the last side of the polygon
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex() - 1, Sketcher::PointPos::end);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add hexagon: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(Corners+1);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Regular_Polygon");
|
||||
}
|
||||
protected:
|
||||
const size_t Corners;
|
||||
const double AngleOfSeparation;
|
||||
const double cos_v, sin_v;
|
||||
SelectMode Mode;
|
||||
Base::Vector2d StartPos;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerPolygon_H
|
||||
|
||||
589
src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h
Normal file
589
src/Mod/Sketcher/Gui/DrawSketchHandlerRectangle.h
Normal file
@@ -0,0 +1,589 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerRectangle_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerRectangle_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerBox: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
enum ConstructionMethod {
|
||||
Diagonal,
|
||||
CenterAndCorner
|
||||
};
|
||||
|
||||
DrawSketchHandlerBox(ConstructionMethod constrMethod = Diagonal): Mode(STATUS_SEEK_First),
|
||||
EditCurve(5),
|
||||
constructionMethod(constrMethod){}
|
||||
virtual ~DrawSketchHandlerBox(){}
|
||||
|
||||
/// mode table
|
||||
enum BoxMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
|
||||
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) {
|
||||
if(constructionMethod == Diagonal) {
|
||||
float dx = onSketchPos.x - EditCurve[0].x;
|
||||
float dy = onSketchPos.y - EditCurve[0].y;
|
||||
SbString text;
|
||||
text.sprintf(" (%.1f x %.1f)", dx, dy);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
EditCurve[2] = onSketchPos;
|
||||
EditCurve[1] = Base::Vector2d(onSketchPos.x ,EditCurve[0].y);
|
||||
EditCurve[3] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
|
||||
|
||||
}
|
||||
else if (constructionMethod == CenterAndCorner) {
|
||||
float dx = onSketchPos.x - center.x;
|
||||
float dy = onSketchPos.y - center.y;
|
||||
SbString text;
|
||||
text.sprintf(" (%.1f x %.1f)", dx, dy);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
EditCurve[0] = center - (onSketchPos - center);
|
||||
EditCurve[1] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
|
||||
EditCurve[2] = onSketchPos;
|
||||
EditCurve[3] = Base::Vector2d(onSketchPos.x,EditCurve[0].y);
|
||||
EditCurve[4] = EditCurve[0];
|
||||
}
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.0,0.0))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
if(constructionMethod == Diagonal) {
|
||||
EditCurve[0] = onSketchPos;
|
||||
EditCurve[4] = onSketchPos;
|
||||
}
|
||||
else if (constructionMethod == CenterAndCorner) {
|
||||
center = onSketchPos;
|
||||
}
|
||||
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else {
|
||||
if(constructionMethod == Diagonal) {
|
||||
EditCurve[2] = onSketchPos;
|
||||
EditCurve[1] = Base::Vector2d(onSketchPos.x ,EditCurve[0].y);
|
||||
EditCurve[3] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
|
||||
drawEdit(EditCurve);
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
else if (constructionMethod == CenterAndCorner) {
|
||||
EditCurve[0] = center - (onSketchPos - center);
|
||||
EditCurve[1] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
|
||||
EditCurve[2] = onSketchPos;
|
||||
EditCurve[3] = Base::Vector2d(onSketchPos.x,EditCurve[0].y);
|
||||
EditCurve[4] = EditCurve[0];
|
||||
drawEdit(EditCurve);
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode==STATUS_End){
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
int firstCurve = getHighestCurveIndex() + 1;
|
||||
|
||||
try {
|
||||
if(constructionMethod == Diagonal) {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch box"));
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"geoList = []\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"%s.addGeometry(geoList,%s)\n"
|
||||
"conList = []\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Horizontal',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Horizontal',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Vertical',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Vertical',%i))\n"
|
||||
"%s.addConstraint(conList)\n"
|
||||
"del geoList, conList\n",
|
||||
EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1
|
||||
EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2
|
||||
EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3
|
||||
EditCurve[3].x,EditCurve[3].y,EditCurve[0].x,EditCurve[0].y, // line 4
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
|
||||
geometryCreationMode==Construction?"True":"False", // geometry as construction or not
|
||||
firstCurve,firstCurve+1, // coincident1
|
||||
firstCurve+1,firstCurve+2, // coincident2
|
||||
firstCurve+2,firstCurve+3, // coincident3
|
||||
firstCurve+3,firstCurve, // coincident4
|
||||
firstCurve, // horizontal1
|
||||
firstCurve+2, // horizontal2
|
||||
firstCurve+1, // vertical1
|
||||
firstCurve+3, // vertical2
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
else if (constructionMethod == CenterAndCorner) {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add centered sketch box"));
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"geoList = []\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
|
||||
"geoList.append(Part.Point(App.Vector(%f,%f,0)))\n"
|
||||
"%s.addGeometry(geoList,%s)\n"
|
||||
"conList = []\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
|
||||
"conList.append(Sketcher.Constraint('Horizontal',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Horizontal',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Vertical',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Vertical',%i))\n"
|
||||
"conList.append(Sketcher.Constraint('Symmetric',%i,2,%i,1,%i,1))\n"
|
||||
"%s.addConstraint(conList)\n"
|
||||
"del geoList, conList\n",
|
||||
EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1
|
||||
EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2
|
||||
EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3
|
||||
EditCurve[3].x,EditCurve[3].y,EditCurve[0].x,EditCurve[0].y, // line 4
|
||||
center.x,center.y, // center point
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
|
||||
geometryCreationMode==Construction?"True":"False", // geometry as construction or not
|
||||
firstCurve,firstCurve+1, // coincident1
|
||||
firstCurve+1,firstCurve+2, // coincident2
|
||||
firstCurve+2,firstCurve+3, // coincident3
|
||||
firstCurve+3,firstCurve, // coincident4
|
||||
firstCurve+1, // horizontal1
|
||||
firstCurve+3, // horizontal2
|
||||
firstCurve, // vertical1
|
||||
firstCurve+2, // vertical2
|
||||
firstCurve+1, firstCurve, firstCurve + 4, // Symmetric
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add box: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
|
||||
if(constructionMethod == Diagonal) {
|
||||
// add auto constraints at the start of the first side
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3 , Sketcher::PointPos::start);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add auto constraints at the end of the second side
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex() - 2, Sketcher::PointPos::end);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
}
|
||||
else if (constructionMethod == CenterAndCorner) {
|
||||
// add auto constraints at the start of the first side
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::start);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add auto constraints at the end of the second side
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex() - 3, Sketcher::PointPos::end);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
|
||||
if(continuousMode){
|
||||
// This code enables the continuous creation mode.
|
||||
Mode=STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(5);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else{
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Create_Box");
|
||||
}
|
||||
protected:
|
||||
BoxMode Mode;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
ConstructionMethod constructionMethod;
|
||||
Base::Vector2d center;
|
||||
};
|
||||
|
||||
class DrawSketchHandlerOblong : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerOblong()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, lengthX(0), lengthY(0), radius(0), signX(1), signY(1)
|
||||
, EditCurve(37)
|
||||
{
|
||||
}
|
||||
virtual ~DrawSketchHandlerOblong() {}
|
||||
/// mode table
|
||||
enum BoxMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
|
||||
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) {
|
||||
float distanceX = onSketchPos.x - StartPos.x;
|
||||
float distanceY = onSketchPos.y - StartPos.y;
|
||||
|
||||
lengthX = distanceX; lengthY = distanceY;
|
||||
signX = Base::sgn(distanceX);
|
||||
signY = Base::sgn(distanceY);
|
||||
if (fabs(distanceX) > fabs(distanceY)) {
|
||||
radius = fabs(distanceY) / 4; // we use a fourth of the smaller distance as default radius
|
||||
}
|
||||
else {
|
||||
radius = fabs(distanceX) / 4;
|
||||
}
|
||||
|
||||
// we draw the lines with 36 segments, 8 for each arc and 4 lines
|
||||
// draw the arcs
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// calculate the x,y positions forming the the arc
|
||||
double angle = i * M_PI / 16.0;
|
||||
double x_i = -radius * sin(angle);
|
||||
double y_i = -radius * cos(angle);
|
||||
// we are drawing clockwise starting with the arc that is besides StartPos
|
||||
if (signX == signY) {
|
||||
EditCurve[i] = Base::Vector2d(StartPos.x + signX * (radius + x_i), StartPos.y + signY * (radius + y_i));
|
||||
EditCurve[9 + i] = Base::Vector2d(StartPos.x + signY * (radius + y_i), StartPos.y + lengthY - signX * (radius + x_i));
|
||||
EditCurve[18 + i] = Base::Vector2d(StartPos.x + lengthX - signX * (radius + x_i), StartPos.y + lengthY - signY * (radius + y_i));
|
||||
EditCurve[27 + i] = Base::Vector2d(StartPos.x + lengthX - signY * (radius + y_i), StartPos.y + signX * (radius + x_i));
|
||||
}
|
||||
else {
|
||||
EditCurve[i] = Base::Vector2d(StartPos.x - signY * (radius + y_i), StartPos.y - signX * (radius + x_i));
|
||||
EditCurve[9 + i] = Base::Vector2d(StartPos.x + lengthX - signX * (radius + x_i), StartPos.y + signY * (radius + y_i));
|
||||
EditCurve[18 + i] = Base::Vector2d(StartPos.x + lengthX + signY * (radius + y_i), StartPos.y + lengthY + signX * (radius + x_i));
|
||||
EditCurve[27 + i] = Base::Vector2d(StartPos.x + signX * (radius + x_i), StartPos.y + lengthY - signY * (radius + y_i));
|
||||
}
|
||||
}
|
||||
// draw the lines
|
||||
if (signX == signY) {
|
||||
EditCurve[8] = Base::Vector2d(StartPos.x, StartPos.y + (signY * radius));
|
||||
EditCurve[17] = Base::Vector2d(StartPos.x + (signX * radius), StartPos.y + lengthY);
|
||||
EditCurve[26] = Base::Vector2d(StartPos.x + lengthX, StartPos.y + lengthY - (signY * radius));
|
||||
EditCurve[35] = Base::Vector2d(StartPos.x + lengthX - (signX * radius), StartPos.y);
|
||||
}
|
||||
else {
|
||||
EditCurve[8] = Base::Vector2d(StartPos.x + (signX * radius), StartPos.y);
|
||||
EditCurve[17] = Base::Vector2d(StartPos.x + lengthX, StartPos.y + (signY * radius));
|
||||
EditCurve[26] = Base::Vector2d(StartPos.x + lengthX - (signX * radius), StartPos.y + lengthY);
|
||||
EditCurve[35] = Base::Vector2d(StartPos.x, StartPos.y + lengthY - (signY * radius));
|
||||
}
|
||||
// close the curve
|
||||
EditCurve[36] = EditCurve[0];
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR %.1fX %.1fY)", radius, lengthX, lengthY);
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f, 0.f))) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
StartPos = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else {
|
||||
EndPos = onSketchPos;
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode == STATUS_End) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
int firstCurve = getHighestCurveIndex() + 1;
|
||||
// add the geometry to the sketch
|
||||
// first determine the angles for the first arc
|
||||
double start = 0;
|
||||
double end = M_PI / 2;
|
||||
if (signX > 0 && signY > 0) {
|
||||
start = -2 * end;
|
||||
end = -1 * end;
|
||||
}
|
||||
else if (signX > 0 && signY < 0) {
|
||||
start = end;
|
||||
end = 2 * end;
|
||||
}
|
||||
else if (signX < 0 && signY > 0) {
|
||||
start = -1 * end;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add rounded rectangle"));
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
// syntax for arcs: Part.ArcOfCircle(Part.Circle(center, axis, radius), startangle, endangle)
|
||||
"geoList = []\n"
|
||||
"geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
|
||||
"geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
|
||||
"geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
|
||||
"geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
|
||||
"%s.addGeometry(geoList, %s)\n"
|
||||
"conList = []\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
|
||||
"conList.append(Sketcher.Constraint('Horizontal', %i))\n"
|
||||
"conList.append(Sketcher.Constraint('Horizontal', %i))\n"
|
||||
"conList.append(Sketcher.Constraint('Vertical', %i))\n"
|
||||
"conList.append(Sketcher.Constraint('Vertical', %i))\n"
|
||||
"conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
|
||||
"conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
|
||||
"conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
|
||||
"%s.addConstraint(conList)\n"
|
||||
"del geoList, conList\n",
|
||||
StartPos.x + (signX * radius), StartPos.y + (signY * radius), // center of the arc 1
|
||||
radius,
|
||||
start, end, // start and end angle of arc1
|
||||
EditCurve[8].x, EditCurve[8].y, EditCurve[9].x, EditCurve[9].y, // line 1
|
||||
signX == signY ? StartPos.x + (signX * radius) : StartPos.x + lengthX - (signX * radius), // center of the arc 2
|
||||
signX == signY ? StartPos.y + lengthY - (signY * radius) : StartPos.y + (signY * radius),
|
||||
radius,
|
||||
// start and end angle of arc 2
|
||||
// the logic is that end is start + M_PI / 2 and start is the previous end - M_PI
|
||||
end - M_PI,
|
||||
end - 0.5 * M_PI,
|
||||
EditCurve[17].x, EditCurve[17].y, EditCurve[18].x, EditCurve[18].y, // line 2
|
||||
StartPos.x + lengthX - (signX * radius), StartPos.y + lengthY - (signY * radius), // center of the arc 3
|
||||
radius,
|
||||
end - 1.5 * M_PI,
|
||||
end - M_PI,
|
||||
EditCurve[26].x, EditCurve[26].y, EditCurve[27].x, EditCurve[27].y, // line 3
|
||||
signX == signY ? StartPos.x + lengthX - (signX * radius) : StartPos.x + (signX * radius), // center of the arc 4
|
||||
signX == signY ? StartPos.y + (signY * radius) : StartPos.y + lengthY - (signY * radius),
|
||||
radius,
|
||||
end - 2 * M_PI,
|
||||
end - 1.5 * M_PI,
|
||||
EditCurve[35].x, EditCurve[35].y, EditCurve[36].x, EditCurve[36].y, // line 4
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
|
||||
geometryCreationMode == Construction ? "True" : "False", // geometry as construction or not
|
||||
firstCurve, firstCurve + 1, // tangent 1
|
||||
firstCurve + 1, firstCurve + 2, // tangent 2
|
||||
firstCurve + 2, firstCurve + 3, // tangent 3
|
||||
firstCurve + 3, firstCurve + 4, // tangent 4
|
||||
firstCurve + 4, firstCurve + 5, // tangent 5
|
||||
firstCurve + 5, firstCurve + 6, // tangent 6
|
||||
firstCurve + 6, firstCurve + 7, // tangent 7
|
||||
firstCurve + 7, firstCurve, // tangent 8
|
||||
signX == signY ? firstCurve + 3 : firstCurve + 1, // horizontal constraint
|
||||
signX == signY ? firstCurve + 7 : firstCurve + 5, // horizontal constraint
|
||||
signX == signY ? firstCurve + 1 : firstCurve + 3, // vertical constraint
|
||||
signX == signY ? firstCurve + 5 : firstCurve + 7, // vertical constraint
|
||||
firstCurve, firstCurve + 2, // equal 1
|
||||
firstCurve + 2, firstCurve + 4, // equal 2
|
||||
firstCurve + 4, firstCurve + 6, // equal 3
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch
|
||||
|
||||
// now add construction geometry - two points used to take suggested constraints
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"geoList = []\n"
|
||||
"geoList.append(Part.Point(App.Vector(%f, %f, 0)))\n"
|
||||
"geoList.append(Part.Point(App.Vector(%f, %f, 0)))\n"
|
||||
"%s.addGeometry(geoList, True)\n" // geometry as construction
|
||||
"conList = []\n"
|
||||
"conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
|
||||
"conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
|
||||
"conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
|
||||
"conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
|
||||
"%s.addConstraint(conList)\n"
|
||||
"del geoList, conList\n",
|
||||
StartPos.x, StartPos.y, // point at StartPos
|
||||
EndPos.x, EndPos.y, // point at EndPos
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
|
||||
firstCurve + 8, firstCurve + 1, // point on object constraint
|
||||
firstCurve + 8, firstCurve + 7, // point on object constraint
|
||||
firstCurve + 9, firstCurve + 3, // point on object constraint
|
||||
firstCurve + 9, firstCurve + 5, // point on object constraint
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// add auto constraints at the StartPos auxiliary point
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex() - 1, Sketcher::PointPos::start);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add auto constraints at the EndPos auxiliary point
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::start);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add rounded rectangle: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecompute(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
|
||||
}
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);
|
||||
|
||||
if (continuousMode) {
|
||||
// This code enables the continuous creation mode.
|
||||
Mode = STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(37);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else {
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Oblong");
|
||||
}
|
||||
|
||||
protected:
|
||||
BoxMode Mode;
|
||||
Base::Vector2d StartPos, EndPos;
|
||||
double lengthX, lengthY, radius;
|
||||
float signX, signY;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerRectangle_H
|
||||
|
||||
306
src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h
Normal file
306
src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerSlot_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerSlot_H
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class DrawSketchHandlerSlot : public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerSlot()
|
||||
: Mode(STATUS_SEEK_First)
|
||||
, SnapMode(SNAP_MODE_Free)
|
||||
, SnapDir(SNAP_DIR_Horz)
|
||||
, dx(0), dy(0), r(0)
|
||||
, EditCurve(35)
|
||||
{
|
||||
}
|
||||
virtual ~DrawSketchHandlerSlot() {}
|
||||
/// mode table
|
||||
enum BoxMode {
|
||||
STATUS_SEEK_First, /**< enum value ----. */
|
||||
STATUS_SEEK_Second, /**< enum value ----. */
|
||||
STATUS_End
|
||||
};
|
||||
|
||||
enum SNAP_MODE
|
||||
{
|
||||
SNAP_MODE_Free,
|
||||
SNAP_MODE_Straight
|
||||
};
|
||||
|
||||
enum SNAP_DIR
|
||||
{
|
||||
SNAP_DIR_Horz,
|
||||
SNAP_DIR_Vert
|
||||
};
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
|
||||
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) {
|
||||
dx = onSketchPos.x - StartPos.x;
|
||||
dy = onSketchPos.y - StartPos.y;
|
||||
|
||||
if(QApplication::keyboardModifiers() == Qt::ControlModifier)
|
||||
SnapMode = SNAP_MODE_Straight;
|
||||
else
|
||||
SnapMode = SNAP_MODE_Free;
|
||||
|
||||
double a = 0;
|
||||
double rev = 0;
|
||||
if (fabs(dx) > fabs(dy)) {
|
||||
r = fabs(dx) / 4;
|
||||
rev = Base::sgn(dx);
|
||||
SnapDir = SNAP_DIR_Horz;
|
||||
if (SnapMode == SNAP_MODE_Straight) dy = 0;
|
||||
}
|
||||
else {
|
||||
r = fabs(dy) / 4;
|
||||
a = 8;
|
||||
rev = Base::sgn(dy);
|
||||
SnapDir = SNAP_DIR_Vert;
|
||||
if (SnapMode == SNAP_MODE_Straight) dx = 0;
|
||||
}
|
||||
|
||||
// draw the arcs with each 16 segments
|
||||
for (int i = 0; i < 17; i++) {
|
||||
// first get the position at the arc
|
||||
// if a is 0, the end points of the arc are at the y-axis, if it is 8, they are on the x-axis
|
||||
double angle = (i + a) * M_PI / 16.0;
|
||||
double rx = -r * rev * sin(angle);
|
||||
double ry = r * rev * cos(angle);
|
||||
// now apply the rotation matrix according to the angle between StartPos and onSketchPos
|
||||
if (!(dx == 0 || dy == 0)) {
|
||||
double rotAngle = atan(dy / dx);
|
||||
if (a > 0)
|
||||
rotAngle = -atan(dx / dy);
|
||||
double rxRot = rx * cos(rotAngle) - ry * sin(rotAngle);
|
||||
double ryRot = rx * sin(rotAngle) + ry * cos(rotAngle);
|
||||
rx = rxRot;
|
||||
ry = ryRot;
|
||||
}
|
||||
EditCurve[i] = Base::Vector2d(StartPos.x + rx, StartPos.y + ry);
|
||||
EditCurve[17 + i] = Base::Vector2d(StartPos.x + dx - rx, StartPos.y + dy - ry);
|
||||
}
|
||||
EditCurve[34] = EditCurve[0];
|
||||
|
||||
SbString text;
|
||||
text.sprintf(" (%.1fR %.1fL)", r, sqrt(dx * dx + dy * dy));
|
||||
setPositionText(onSketchPos, text);
|
||||
|
||||
drawEdit(EditCurve);
|
||||
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(dx, dy), AutoConstraint::VERTEX_NO_TANGENCY)) {
|
||||
renderSuggestConstraintsCursor(sugConstr2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
applyCursor();
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
if (Mode == STATUS_SEEK_First) {
|
||||
StartPos = onSketchPos;
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else {
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
if (Mode == STATUS_End) {
|
||||
unsetCursor();
|
||||
resetPositionText();
|
||||
|
||||
int firstCurve = getHighestCurveIndex() + 1;
|
||||
// add the geometry to the sketch
|
||||
// first determine the rotation angle for the first arc
|
||||
double start, end;
|
||||
if (fabs(dx) > fabs(dy)) {
|
||||
if (dx > 0) {
|
||||
start = 0.5 * M_PI;
|
||||
end = 1.5 * M_PI;
|
||||
}
|
||||
else {
|
||||
start = 1.5 * M_PI;
|
||||
end = 0.5 * M_PI;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (dy > 0) {
|
||||
start = -M_PI;
|
||||
end = 0;
|
||||
}
|
||||
else {
|
||||
start = 0;
|
||||
end = -M_PI;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add slot"));
|
||||
|
||||
AutoConstraint lastCons = {Sketcher::None, Sketcher::GeoEnum::GeoUndef, Sketcher::PointPos::none};
|
||||
|
||||
if (!sugConstr2.empty()) lastCons = sugConstr2.back();
|
||||
|
||||
ostringstream snapCon = ostringstream("");
|
||||
if (SnapMode == SNAP_MODE_Straight) {
|
||||
snapCon << "conList.append(Sketcher.Constraint('";
|
||||
if (SnapDir == SNAP_DIR_Horz) {
|
||||
snapCon << "Horizontal";
|
||||
}
|
||||
else {
|
||||
snapCon << "Vertical";
|
||||
}
|
||||
snapCon << "'," << firstCurve + 2 << "))\n";
|
||||
|
||||
// If horizontal/vertical already applied because of snap, do not duplicate with Autocontraint
|
||||
if (lastCons.Type == Sketcher::Horizontal || lastCons.Type == Sketcher::Vertical)
|
||||
sugConstr2.pop_back();
|
||||
}
|
||||
else {
|
||||
// If horizontal/vertical Autoconstraint suggested, applied it on first line (rather than last arc)
|
||||
if (lastCons.Type == Sketcher::Horizontal || lastCons.Type == Sketcher::Vertical)
|
||||
sugConstr2.back().GeoId = firstCurve + 2;
|
||||
}
|
||||
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"geoList = []\n"
|
||||
"geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
|
||||
"geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f ,0), App.Vector(0, 0, 1), %f), %f, %f))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
|
||||
"geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
|
||||
"%s.addGeometry(geoList, %s)\n"
|
||||
"conList = []\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
|
||||
"conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
|
||||
"%s"
|
||||
"%s.addConstraint(conList)\n"
|
||||
"del geoList, conList\n",
|
||||
StartPos.x, StartPos.y, // center of the arc1
|
||||
r, // radius arc1
|
||||
start, end, // start and end angle of arc1
|
||||
StartPos.x + dx, StartPos.y + dy, // center of the arc2
|
||||
r, // radius arc2
|
||||
end, end + M_PI, // start and end angle of arc2
|
||||
EditCurve[16].x, EditCurve[16].y, EditCurve[17].x, EditCurve[17].y, // line1
|
||||
EditCurve[33].x, EditCurve[33].y, EditCurve[34].x, EditCurve[34].y, // line2
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
|
||||
geometryCreationMode == Construction ? "True" : "False", // geometry as construction or not
|
||||
firstCurve, firstCurve + 2, // tangent1
|
||||
firstCurve + 2, firstCurve + 1, // tangent2
|
||||
firstCurve + 1, firstCurve + 3, // tangent3
|
||||
firstCurve + 3, firstCurve, // tangent4
|
||||
firstCurve, firstCurve + 1, // equal constraint
|
||||
snapCon.str().c_str(), // horizontal/vertical constraint if snapping
|
||||
Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
|
||||
// add auto constraints at the center of the first arc
|
||||
if (sugConstr1.size() > 0) {
|
||||
createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3, Sketcher::PointPos::mid);
|
||||
sugConstr1.clear();
|
||||
}
|
||||
|
||||
// add auto constraints at the center of the second arc
|
||||
if (sugConstr2.size() > 0) {
|
||||
createAutoConstraints(sugConstr2, getHighestCurveIndex() - 2, Sketcher::PointPos::mid);
|
||||
sugConstr2.clear();
|
||||
}
|
||||
|
||||
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to add slot: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
|
||||
tryAutoRecompute(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
|
||||
}
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);
|
||||
|
||||
if (continuousMode) {
|
||||
// This code enables the continuous creation mode.
|
||||
Mode = STATUS_SEEK_First;
|
||||
EditCurve.clear();
|
||||
drawEdit(EditCurve);
|
||||
EditCurve.resize(35);
|
||||
applyCursor();
|
||||
/* this is ok not to call to purgeHandler
|
||||
* in continuous creation mode because the
|
||||
* handler is destroyed by the quit() method on pressing the
|
||||
* right button of the mouse */
|
||||
}
|
||||
else {
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
}
|
||||
SnapMode = SNAP_MODE_Straight;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
virtual QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QString::fromLatin1("Sketcher_Pointer_Slot");
|
||||
}
|
||||
|
||||
protected:
|
||||
BoxMode Mode;
|
||||
SNAP_MODE SnapMode;
|
||||
SNAP_DIR SnapDir;
|
||||
Base::Vector2d StartPos;
|
||||
double dx, dy, r;
|
||||
std::vector<Base::Vector2d> EditCurve;
|
||||
std::vector<AutoConstraint> sugConstr1, sugConstr2;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerSlot_H
|
||||
|
||||
131
src/Mod/Sketcher/Gui/DrawSketchHandlerSplitting.h
Normal file
131
src/Mod/Sketcher/Gui/DrawSketchHandlerSplitting.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerSplitting_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerSplitting_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
|
||||
class SplittingSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
SplittingSelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate(nullPointer()), object(obj)
|
||||
{}
|
||||
|
||||
bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
if (pObj != this->object)
|
||||
return false;
|
||||
if (!sSubName || sSubName[0] == '\0')
|
||||
return false;
|
||||
std::string element(sSubName);
|
||||
if (element.substr(0,4) == "Edge") {
|
||||
int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1;
|
||||
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
const Part::Geometry *geom = Sketch->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()
|
||||
|| geom->getTypeId() == Part::GeomCircle::getClassTypeId()
|
||||
|| geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class DrawSketchHandlerSplitting: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerSplitting() = default;
|
||||
virtual ~DrawSketchHandlerSplitting()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
int GeoId = getPreselectCurve();
|
||||
if (GeoId >= 0) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
|
||||
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()
|
||||
|| geom->getTypeId() == Part::GeomCircle::getClassTypeId()
|
||||
|| geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Split edge"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "split(%d,App.Vector(%f,%f,0))",
|
||||
GeoId, onSketchPos.x, onSketchPos.y);
|
||||
Gui::Command::commitCommand();
|
||||
tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to split edge: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
sketchgui->purgeHandler();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void activated() override
|
||||
{
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
Gui::Selection().addSelectionGate(new SplittingSelection(sketchgui->getObject()));
|
||||
}
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Splitting");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerSplitting_H
|
||||
|
||||
175
src/Mod/Sketcher/Gui/DrawSketchHandlerTrimming.h
Normal file
175
src/Mod/Sketcher/Gui/DrawSketchHandlerTrimming.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerTrimming_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerTrimming_H
|
||||
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp
|
||||
|
||||
class TrimmingSelection : public Gui::SelectionFilterGate
|
||||
{
|
||||
App::DocumentObject* object;
|
||||
public:
|
||||
TrimmingSelection(App::DocumentObject* obj)
|
||||
: Gui::SelectionFilterGate(nullPointer()), object(obj)
|
||||
{}
|
||||
|
||||
bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
|
||||
{
|
||||
if (pObj != this->object)
|
||||
return false;
|
||||
if (!sSubName || sSubName[0] == '\0')
|
||||
return false;
|
||||
std::string element(sSubName);
|
||||
if (element.substr(0,4) == "Edge") {
|
||||
int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1;
|
||||
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
|
||||
const Part::Geometry *geom = Sketch->getGeometry(GeoId);
|
||||
if (geom->getTypeId().isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId()) ||
|
||||
geom->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()
|
||||
) {
|
||||
// We do not trim internal geometry of complex geometries
|
||||
if( Sketcher::GeometryFacade::isInternalType(geom, Sketcher::InternalType::None))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class DrawSketchHandlerTrimming: public DrawSketchHandler
|
||||
{
|
||||
public:
|
||||
DrawSketchHandlerTrimming() = default;
|
||||
virtual ~DrawSketchHandlerTrimming()
|
||||
{
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
}
|
||||
|
||||
virtual void mouseMove(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
|
||||
int GeoId = getPreselectCurve();
|
||||
|
||||
if (GeoId > -1) {
|
||||
auto sk = static_cast<Sketcher::SketchObject *>(sketchgui->getObject());
|
||||
int GeoId1, GeoId2;
|
||||
Base::Vector3d intersect1, intersect2;
|
||||
if(sk->seekTrimPoints(GeoId, Base::Vector3d(onSketchPos.x,onSketchPos.y,0),
|
||||
GeoId1, intersect1,
|
||||
GeoId2, intersect2)) {
|
||||
|
||||
EditMarkers.resize(0);
|
||||
|
||||
if(GeoId1 != Sketcher::GeoEnum::GeoUndef)
|
||||
EditMarkers.emplace_back(intersect1.x, intersect1.y);
|
||||
else {
|
||||
auto start = sk->getPoint(GeoId, Sketcher::PointPos::start);
|
||||
EditMarkers.emplace_back(start.x, start.y);
|
||||
}
|
||||
|
||||
if(GeoId2 != Sketcher::GeoEnum::GeoUndef)
|
||||
EditMarkers.emplace_back(intersect2.x, intersect2.y);
|
||||
else {
|
||||
auto end = sk->getPoint(GeoId, Sketcher::PointPos::end);
|
||||
EditMarkers.emplace_back( end.x, end.y);
|
||||
}
|
||||
|
||||
drawEditMarkers(EditMarkers, 2); // maker augmented by two sizes (see supported marker sizes)
|
||||
}
|
||||
}
|
||||
else {
|
||||
EditMarkers.resize(0);
|
||||
drawEditMarkers(EditMarkers, 2);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool pressButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
Q_UNUSED(onSketchPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool releaseButton(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
int GeoId = getPreselectCurve();
|
||||
if (GeoId > -1) {
|
||||
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
|
||||
if (geom->getTypeId().isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId()) ||
|
||||
geom->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
||||
geom->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ) {
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Trim edge"));
|
||||
Gui::cmdAppObjectArgs(sketchgui->getObject(), "trim(%d,App.Vector(%f,%f,0))",
|
||||
GeoId, onSketchPos.x, onSketchPos.y);
|
||||
Gui::Command::commitCommand();
|
||||
tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Failed to trim edge: %s\n", e.what());
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
}
|
||||
|
||||
EditMarkers.resize(0);
|
||||
drawEditMarkers(EditMarkers);
|
||||
}
|
||||
else // exit the trimming tool if the user clicked on empty space
|
||||
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void activated() override
|
||||
{
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().rmvSelectionGate();
|
||||
Gui::Selection().addSelectionGate(new TrimmingSelection(sketchgui->getObject()));
|
||||
}
|
||||
|
||||
virtual QString getCrosshairCursorSVGName() const override {
|
||||
return QString::fromLatin1("Sketcher_Pointer_Trimming");
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Base::Vector2d> EditMarkers;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerTrimming_H
|
||||
|
||||
Reference in New Issue
Block a user