Files
create/src/Mod/Sketcher/Gui/DrawSketchHandlerSlot.h
2022-12-11 23:47:03 +01:00

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