310 lines
13 KiB
C++
310 lines
13 KiB
C++
/***************************************************************************
|
|
* 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"
|
|
|
|
|
|
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
|
|
};
|
|
|
|
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];
|
|
|
|
if (showCursorCoords()) {
|
|
SbString text;
|
|
std::string rString = lengthToDisplayFormat(r, 1);
|
|
std::string sqrtString = lengthToDisplayFormat(sqrt(dx * dx + dy * dy), 1);
|
|
text.sprintf(" (R%s L%s))", rString.c_str(), sqrtString.c_str());
|
|
setPositionText(onSketchPos, text);
|
|
}
|
|
|
|
drawEdit(EditCurve);
|
|
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(dx, dy), AutoConstraint::VERTEX_NO_TANGENCY)) {
|
|
renderSuggestConstraintsCursor(sugConstr2);
|
|
return;
|
|
}
|
|
}
|
|
applyCursor();
|
|
}
|
|
|
|
bool pressButton(Base::Vector2d onSketchPos) override
|
|
{
|
|
if (Mode == STATUS_SEEK_First) {
|
|
StartPos = onSketchPos;
|
|
Mode = STATUS_SEEK_Second;
|
|
}
|
|
else {
|
|
Mode = STATUS_End;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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.empty()) {
|
|
createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3, Sketcher::PointPos::mid);
|
|
sugConstr1.clear();
|
|
}
|
|
|
|
// add auto constraints at the center of the second arc
|
|
if (!sugConstr2.empty()) {
|
|
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:
|
|
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
|
|
|