FreeCAD/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp
2014-06-03 11:24:49 +02:00

3114 lines
120 KiB
C++

/***************************************************************************
* Copyright (c) 2010 Jürgen Riegel (juergen.riegel@web.de) *
* *
* 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 *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <Inventor/nodes/SoPickStyle.h>
# include <QApplication>
#endif
#include <boost/math/special_functions/fpclassify.hpp>
#include <Base/Console.h>
#include <Gui/Action.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Document.h>
#include <Gui/Command.h>
#include <Gui/MainWindow.h>
#include <Gui/DlgEditFileIncludeProptertyExternal.h>
#include <Gui/Selection.h>
#include <Gui/SelectionFilter.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include "ViewProviderSketch.h"
#include "DrawSketchHandler.h"
#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include <Gui/SoFCUnifiedSelection.h>
using namespace std;
using namespace SketcherGui;
/* helper functions ======================================================*/
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
double GetPointAngle (const Base::Vector2D &p1, const Base::Vector2D &p2)
{
double dX = p2.fX - p1.fX;
double dY = p2.fY - p1.fY;
return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2*M_PI;
}
/*
Find the centerpoint of a circle drawn through any 3 points:
Given points p1-3, draw 2 lines: S12 and S23 which each connect two points. From the
midpoint of each line, draw a perpendicular line (S12p/S23p) across the circle. These
lines will cross at the centerpoint.
Mathematically, line S12 will have a slope of m12 which can be determined. Therefore,
the slope m12p is -1/m12. Line S12p will have an equation of y = m12p*x + b12p. b12p can
be solved for using the midpoint of the line. This can be done for both lines. Since
both S12p and S23p cross at the centerpoint, solving the two equations together will give
the location of the centerpoint.
*/
Base::Vector2D GetCircleCenter (const Base::Vector2D &p1, const Base::Vector2D &p2, const Base::Vector2D &p3)
{
double m12p = (p1.fX - p2.fX) / (p2.fY - p1.fY);
double m23p = (p2.fX - p3.fX) / (p3.fY - p2.fY);
double x = 1/( 2*(m12p - m23p) ) * ( m12p*(p1.fX + p2.fX) -
m23p*(p2.fX + p3.fX) +
p3.fY - p1.fY );
double y = m12p * ( x - (p1.fX + p2.fX)/2 ) + (p1.fY + p2.fY)/2;
return Base::Vector2D(x, y);
}
void ActivateHandler(Gui::Document *doc,DrawSketchHandler *handler)
{
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId()))
dynamic_cast<SketcherGui::ViewProviderSketch*>
(doc->getInEdit())->activateHandler(handler);
}
}
bool isCreateGeoActive(Gui::Document *doc)
{
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId())) {
if (dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->
getSketchMode() == ViewProviderSketch::STATUS_NONE)
return true;
}
}
return false;
}
SketcherGui::ViewProviderSketch* getSketchViewprovider(Gui::Document *doc)
{
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId()) )
return dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
}
return 0;
}
/* Sketch commands =======================================================*/
/* XPM */
static const char *cursor_createline[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+...............###.......",
"......+...............#.#.......",
"......+...............###.......",
"......+..............#..........",
"......+.............#...........",
"....................#...........",
"...................#............",
"..................#.............",
"..................#.............",
".................#..............",
"................#...............",
"................#...............",
"...............#................",
"..............#.................",
"..............#.................",
".............#..................",
"..........###...................",
"..........#.#...................",
"..........###...................",
"................................",
"................................",
"................................",
"................................",
"................................"};
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 activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createline),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second){
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;
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
renderSuggestConstraintsCursor(sugConstr2);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First){
EditCurve[0] = onSketchPos;
Mode = STATUS_SEEK_Second;
}
else {
EditCurve[1] = onSketchPos;
sketchgui->drawEdit(EditCurve);
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_End){
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch line");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[0].fX,EditCurve[0].fY,EditCurve[1].fX,EditCurve[1].fY);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// add auto constraints for the line segment start
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::start);
sugConstr1.clear();
}
// add auto constraints for the line segment end
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::end);
sugConstr2.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
SelectMode Mode;
std::vector<Base::Vector2D> EditCurve;
std::vector<AutoConstraint> sugConstr1, sugConstr2;
};
DEF_STD_CMD_A(CmdSketcherCreateLine);
CmdSketcherCreateLine::CmdSketcherCreateLine()
: Command("Sketcher_CreateLine")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create line");
sToolTipText = QT_TR_NOOP("Create a line in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateLine";
sAccel = "L";
eType = ForEdit;
}
void CmdSketcherCreateLine::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerLine() );
}
bool CmdSketcherCreateLine::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
/* Create Box =======================================================*/
/* XPM */
static const char *cursor_createbox[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"................................",
"..........................###...",
"...........################.#...",
"...........#..............###...",
"...........#...............#....",
"...........#...............#....",
"...........#...............#....",
"...........#...............#....",
"...........#...............#....",
"...........#...............#....",
"..........###..............#....",
"..........#.################....",
"..........###...................",
"................................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandlerBox: public DrawSketchHandler
{
public:
DrawSketchHandlerBox():Mode(STATUS_SEEK_First),EditCurve(5){}
virtual ~DrawSketchHandlerBox(){}
/// mode table
enum BoxMode {
STATUS_SEEK_First, /**< enum value ----. */
STATUS_SEEK_Second, /**< enum value ----. */
STATUS_End
};
virtual void activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createbox),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second) {
float dx = onSketchPos.fX - EditCurve[0].fX;
float dy = onSketchPos.fY - EditCurve[0].fY;
SbString text;
text.sprintf(" (%.1f x %.1f)", dx, dy);
setPositionText(onSketchPos, text);
EditCurve[2] = onSketchPos;
EditCurve[1] = Base::Vector2D(onSketchPos.fX ,EditCurve[0].fY);
EditCurve[3] = Base::Vector2D(EditCurve[0].fX,onSketchPos.fY);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.0,0.0))) {
renderSuggestConstraintsCursor(sugConstr2);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First){
EditCurve[0] = onSketchPos;
EditCurve[4] = onSketchPos;
Mode = STATUS_SEEK_Second;
}
else {
EditCurve[2] = onSketchPos;
EditCurve[1] = Base::Vector2D(onSketchPos.fX ,EditCurve[0].fY);
EditCurve[3] = Base::Vector2D(EditCurve[0].fX,onSketchPos.fY);
sketchgui->drawEdit(EditCurve);
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_End){
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch box");
int firstCurve = getHighestCurveIndex() + 1;
// add the four line geos
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[0].fX,EditCurve[0].fY,EditCurve[1].fX,EditCurve[1].fY);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[1].fX,EditCurve[1].fY,EditCurve[2].fX,EditCurve[2].fY);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[2].fX,EditCurve[2].fY,EditCurve[3].fX,EditCurve[3].fY);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[3].fX,EditCurve[3].fY,EditCurve[0].fX,EditCurve[0].fY);
// add the four coincidents to ty them together
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve,firstCurve+1);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+1,firstCurve+2);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+2,firstCurve+3);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+3,firstCurve);
// add the horizontal constraints
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Horizontal',%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Horizontal',%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+2);
// add the vertical constraints
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Vertical',%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+1);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Vertical',%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+3);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// add auto constraints at the start of the first side
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3 , Sketcher::start);
sugConstr1.clear();
}
// add auto constraints at the end of the second side
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex() - 2, Sketcher::end);
sugConstr2.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
BoxMode Mode;
std::vector<Base::Vector2D> EditCurve;
std::vector<AutoConstraint> sugConstr1, sugConstr2;
};
DEF_STD_CMD_A(CmdSketcherCreateRectangle);
CmdSketcherCreateRectangle::CmdSketcherCreateRectangle()
: Command("Sketcher_CreateRectangle")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create rectangle");
sToolTipText = QT_TR_NOOP("Create a rectangle in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateRectangle";
sAccel = "R";
eType = ForEdit;
}
void CmdSketcherCreateRectangle::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerBox() );
}
bool CmdSketcherCreateRectangle::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
/* XPM */
static const char *cursor_createlineset[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+...............###.......",
"......+...............#.#.......",
"......+...............###.......",
"......+..............#..#.......",
"......+.............#....#......",
"....................#....#......",
"...................#......#.....",
"..................#.......#.....",
"..................#........#....",
".................#.........#....",
"................#..........###..",
"................#..........#.#..",
"......#........#...........###..",
".......#......#.................",
"........#.....#.................",
".........#...#..................",
"..........###...................",
"..........#.#...................",
"..........###...................",
"................................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandlerLineSet: public DrawSketchHandler
{
public:
DrawSketchHandlerLineSet()
: Mode(STATUS_SEEK_First),SegmentMode(SEGMENT_MODE_Line),
TransitionMode(TRANSITION_MODE_Free),suppressTransition(false),EditCurve(2),
firstCurve(-1),previousCurve(-1),
firstPosId(Sketcher::none),previousPosId(Sketcher::none) {}
virtual ~DrawSketchHandlerLineSet() {}
/// 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
};
virtual void registerPressedKey(bool pressed, int key)
{
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
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) + unexpexted 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 activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createlineset),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
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].ProjToLine(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].ProjToLine(EditCurve[2] - EditCurve[0], Perpendicular);
EditCurve[1] = EditCurve[0] + EditCurve[1];
}
sketchgui->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) {
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 torwards
// 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].fX;
double y1 = EditCurve[0].fY;
double x2 = x1 + Tangent.fX;
double y2 = y1 + Tangent.fY;
double x3 = onSketchPos.fX;
double y3 = onSketchPos.fY;
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.fY, -arcRadius * Tangent.fX);
double rx = EditCurve[0].fX - CenterPoint.fX;
double ry = EditCurve[0].fY - CenterPoint.fY;
startAngle = atan2(ry,rx);
double rxe = onSketchPos.fX - CenterPoint.fX;
double rye = onSketchPos.fY - CenterPoint.fY;
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;
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.fX + dx, CenterPoint.fY + dy);
}
EditCurve[30] = CenterPoint;
EditCurve[31] = EditCurve[0];
sketchgui->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)
{
if (Mode == STATUS_SEEK_First) {
EditCurve[0] = onSketchPos; // this may be overwritten if previousCurve is found
// 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::start ||
sugConstr1[i].PosId == Sketcher::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;
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::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();
EditCurve.clear();
resetPositionText();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
Mode = STATUS_Do;
if (sketchgui->getPreselectPoint() != -1 && firstPosId != Sketcher::none) {
int GeoId;
Sketcher::PointPos PosId;
sketchgui->getSketchObject()->getGeoVertexIndex(sketchgui->getPreselectPoint(),GeoId,PosId);
if (sketchgui->getSketchObject()->arePointsCoincident(GeoId,PosId,firstCurve,firstPosId))
Mode = STATUS_Close;
}
else if (sketchgui->getPreselectCross() == 0 && firstPosId != Sketcher::none) {
// close line started at root point
if (sketchgui->getSketchObject()->arePointsCoincident(-1,Sketcher::start,firstCurve,firstPosId))
Mode = STATUS_Close;
}
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
if (Mode == STATUS_Do || Mode == STATUS_Close) {
if (SegmentMode == SEGMENT_MODE_Line) {
// open the transaction
Gui::Command::openCommand("Add line to sketch wire");
// issue the geometry
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[0].fX,EditCurve[0].fY,EditCurve[1].fX,EditCurve[1].fY);
}
else if (SegmentMode == SEGMENT_MODE_Arc) { // We're dealing with an Arc
if (!boost::math::isnormal(arcRadius)) {
Mode = STATUS_SEEK_Second;
return true;
}
Gui::Command::openCommand("Add arc to sketch wire");
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle"
"(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f))",
sketchgui->getObject()->getNameInDocument(),
CenterPoint.fX, CenterPoint.fY, std::abs(arcRadius),
std::min(startAngle,endAngle), std::max(startAngle,endAngle));
}
// issue the constraint
if (previousPosId != Sketcher::none) {
int lastCurve = getHighestCurveIndex();
Sketcher::PointPos lastStartPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
Sketcher::end : Sketcher::start;
Sketcher::PointPos lastEndPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
Sketcher::start : Sketcher::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::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('%s',%i,%i,%i,%i)) ",
sketchgui->getObject()->getNameInDocument(), constrType.c_str(),
previousCurve, previousPosId, lastCurve, lastStartPosId);
if (Mode == STATUS_Close) {
// close the loop by constrain to the first curve point
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,%i,%i,%i)) ",
sketchgui->getObject()->getNameInDocument(),
lastCurve,lastEndPosId,firstCurve,firstPosId);
}
Gui::Command::commitCommand();
Gui::Command::updateActive();
}
if (Mode == STATUS_Close) {
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::end);
sugConstr2.clear();
}
unsetCursor();
EditCurve.clear();
resetPositionText();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
else {
Gui::Command::commitCommand();
Gui::Command::updateActive();
// Add auto constraints
if (sugConstr1.size() > 0) { // this is relevant only to the very first point
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::start);
sugConstr1.clear();
}
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::end);
sugConstr2.clear();
}
// remember the vertex for the next rounds constraint..
previousCurve = getHighestCurveIndex();
previousPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
Sketcher::start : Sketcher::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;
EditCurve[1] = EditCurve[0];
mouseMove(onSketchPos); // trigger an update of EditCurve
}
}
return true;
}
protected:
SELECT_MODE Mode;
SEGMENT_MODE SegmentMode;
TRANSITION_MODE TransitionMode;
bool suppressTransition;
std::vector<Base::Vector2D> EditCurve;
int firstCurve;
int previousCurve;
Sketcher::PointPos firstPosId;
Sketcher::PointPos previousPosId;
std::vector<AutoConstraint> sugConstr1, sugConstr2;
Base::Vector2D CenterPoint;
Base::Vector3d dirVec;
double startAngle, endAngle, arcRadius;
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 = dynamic_cast<const Part::GeomLineSegment *>(geom);
dirVec.Set(lineSeg->getEndPoint().x - lineSeg->getStartPoint().x,
lineSeg->getEndPoint().y - lineSeg->getStartPoint().y,
0.f);
if (PosId == Sketcher::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 = dynamic_cast<const Part::GeomArcOfCircle *>(geom);
if (PosId == Sketcher::start) {
EditCurve[0] = Base::Vector2D(arcSeg->getStartPoint().x,arcSeg->getStartPoint().y);
dirVec = Base::Vector3d(0.f,0.f,-1.0) % (arcSeg->getStartPoint()-arcSeg->getCenter());
}
else {
EditCurve[0] = Base::Vector2D(arcSeg->getEndPoint().x,arcSeg->getEndPoint().y);
dirVec = Base::Vector3d(0.f,0.f,1.0) % (arcSeg->getEndPoint()-arcSeg->getCenter());
}
}
dirVec.Normalize();
}
};
DEF_STD_CMD_A(CmdSketcherCreatePolyline);
CmdSketcherCreatePolyline::CmdSketcherCreatePolyline()
: Command("Sketcher_CreatePolyline")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create polyline");
sToolTipText = QT_TR_NOOP("Create a polyline in the sketch. 'M' Key cycles behaviour");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreatePolyline";
eType = ForEdit;
}
void CmdSketcherCreatePolyline::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerLineSet() );
}
bool CmdSketcherCreatePolyline::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
/* XPM */
static const char *cursor_createarc[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+...........###...........",
"......+...........#.#...........",
"......+...........###...........",
"......+..............##.........",
"......+...............##........",
".......................#........",
"+++++...+++++...........#.......",
"........................##......",
"......+..................#......",
"......+..................#......",
"......+...................#.....",
"......+...................#.....",
"......+...................#.....",
"..........................#.....",
"..........................#.....",
"..........................#.....",
"..........................#.....",
".........................#......",
".........................#......",
"........................#.......",
"........................#.......",
"...###.................#........",
"...#.#................#.........",
"...###...............#..........",
"......##...........##...........",
".......###.......##.............",
"..........#######...............",
"................................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandlerArc : public DrawSketchHandler
{
public:
DrawSketchHandlerArc()
: Mode(STATUS_SEEK_First),EditCurve(2){}
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 activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createarc),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second) {
double dx_ = onSketchPos.fX - EditCurve[0].fX;
double dy_ = onSketchPos.fY - EditCurve[0].fY;
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].fX + dx, EditCurve[0].fY + dy);
EditCurve[17+i] = Base::Vector2D(EditCurve[0].fX - dx, EditCurve[0].fY - 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);
sketchgui->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.fY - CenterPoint.fY,
onSketchPos.fX - CenterPoint.fX) - 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.fX + dx, CenterPoint.fY + 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);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.0,0.0))) {
renderSuggestConstraintsCursor(sugConstr3);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First){
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].fX - CenterPoint.fX;
ry = EditCurve[0].fY - CenterPoint.fY;
startAngle = atan2(ry, rx);
arcAngle = 0.;
Mode = STATUS_SEEK_Third;
}
else {
EditCurve.resize(30);
double angle1 = atan2(onSketchPos.fY - CenterPoint.fY,
onSketchPos.fX - CenterPoint.fX) - 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;
}
sketchgui->drawEdit(EditCurve);
applyCursor();
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_End) {
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch arc");
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle"
"(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),"
"%f,%f))",
sketchgui->getObject()->getNameInDocument(),
CenterPoint.fX, CenterPoint.fY, sqrt(rx*rx + ry*ry),
startAngle, endAngle); //arcAngle > 0 ? 0 : 1);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// Auto Constraint center point
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::mid);
sugConstr1.clear();
}
// Auto Constraint first picked point
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::start : Sketcher::end );
sugConstr2.clear();
}
// Auto Constraint second picked point
if (sugConstr3.size() > 0) {
createAutoConstraints(sugConstr3, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::end : Sketcher::start);
sugConstr3.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
SelectMode Mode;
std::vector<Base::Vector2D> EditCurve;
Base::Vector2D CenterPoint;
double rx, ry, startAngle, endAngle, arcAngle;
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
};
DEF_STD_CMD_A(CmdSketcherCreateArc);
CmdSketcherCreateArc::CmdSketcherCreateArc()
: Command("Sketcher_CreateArc")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create arc by center");
sToolTipText = QT_TR_NOOP("Create an arc by its center and by its end points");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateArc";
eType = ForEdit;
}
void CmdSketcherCreateArc::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArc() );
}
bool CmdSketcherCreateArc::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
/* XPM */
static const char *cursor_create3pointarc[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+...........###...........",
"......+...........#.#...........",
"......+...........###...........",
"......+..............##.........",
"......+...............##........",
".......................#........",
"+++++...+++++...........#.......",
"........................##......",
"......+..................#......",
"......+..................#......",
"......+...................#.....",
"......+...................#.....",
"......+...................#.....",
"..........................#.....",
"..........................#.....",
"..........................#.....",
"..........................#.....",
".........................#......",
".......................###......",
".......................#.#......",
".......................###......",
"...###.................#........",
"...#.#................#.........",
"...###...............#..........",
"......##...........##...........",
".......###.......##.............",
"..........#######...............",
"................................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandler3PointArc : public DrawSketchHandler
{
public:
DrawSketchHandler3PointArc()
: Mode(STATUS_SEEK_First),EditCurve(2){}
virtual ~DrawSketchHandler3PointArc(){}
/// mode table
enum SelectMode {
STATUS_SEEK_First, /**< enum value ----. */
STATUS_SEEK_Second, /**< enum value ----. */
STATUS_SEEK_Third, /**< enum value ----. */
STATUS_End
};
virtual void activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_create3pointarc),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second) {
CenterPoint = EditCurve[0] = (onSketchPos - FirstPoint)/2 + FirstPoint;
EditCurve[1] = EditCurve[33] = onSketchPos;
radius = (onSketchPos - CenterPoint).Length();
double lineAngle = GetPointAngle(CenterPoint, onSketchPos);
// Build a 32 point circle ignoring already constructed points
for (int i=1; i <= 32; i++) {
// Start at current angle
double angle = (i-1)*2*M_PI/32.0 + lineAngle; // N point closed circle has N segments
if (i != 1 && i != 17 ) {
EditCurve[i] = Base::Vector2D(CenterPoint.fX + radius*cos(angle),
CenterPoint.fY + radius*sin(angle));
}
}
// Display radius and start angle
// This lineAngle will report counter-clockwise from +X, not relatively
SbString text;
text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) lineAngle * 180 / M_PI);
setPositionText(onSketchPos, text);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr2);
return;
}
}
else if (Mode==STATUS_SEEK_Third) {
/*
Centerline inverts when the arc flips sides. Easily taken care of by replacing
centerline with a point. It happens because the direction the curve is being drawn
reverses.
*/
CenterPoint = EditCurve[30] = GetCircleCenter(FirstPoint, SecondPoint, onSketchPos);
radius = (SecondPoint - CenterPoint).Length();
double angle1 = GetPointAngle(CenterPoint, FirstPoint);
double angle2 = GetPointAngle(CenterPoint, SecondPoint);
double angle3 = GetPointAngle(CenterPoint, onSketchPos);
// 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::start;
arcPos2 = Sketcher::end;
}
else {
EditCurve[0] = SecondPoint;
EditCurve[29] = FirstPoint;
arcPos1 = Sketcher::end;
arcPos2 = Sketcher::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::end;
arcPos2 = Sketcher::start;
}
else {
EditCurve[0] = FirstPoint;
EditCurve[29] = SecondPoint;
arcPos1 = Sketcher::start;
arcPos2 = Sketcher::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.fX + radius*cos(angle),
CenterPoint.fY + radius*sin(angle));
}
SbString text;
text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) arcAngle * 180 / M_PI);
setPositionText(onSketchPos, text);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.0,0.0),
AutoConstraint::CURVE)) {
renderSuggestConstraintsCursor(sugConstr3);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First){
// 32 point curve + center + endpoint
EditCurve.resize(34);
// 17 is circle halfway point (1+32/2)
FirstPoint = EditCurve[17] = onSketchPos;
Mode = STATUS_SEEK_Second;
}
else if (Mode==STATUS_SEEK_Second){
// 30 point arc and center point
EditCurve.resize(31);
SecondPoint = onSketchPos;
Mode = STATUS_SEEK_Third;
}
else {
EditCurve.resize(30);
sketchgui->drawEdit(EditCurve);
applyCursor();
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
// Need to look at. rx might need fixing.
if (Mode==STATUS_End) {
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch arc");
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle"
"(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),"
"%f,%f))",
sketchgui->getObject()->getNameInDocument(),
CenterPoint.fX, CenterPoint.fY, radius,
startAngle, endAngle);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// Auto Constraint 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::none);
sugConstr3.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
SelectMode Mode;
std::vector<Base::Vector2D> EditCurve;
Base::Vector2D CenterPoint, FirstPoint, SecondPoint;
double radius, startAngle, endAngle, arcAngle;
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
Sketcher::PointPos arcPos1, arcPos2;
};
DEF_STD_CMD_A(CmdSketcherCreate3PointArc);
CmdSketcherCreate3PointArc::CmdSketcherCreate3PointArc()
: Command("Sketcher_Create3PointArc")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create arc by three points");
sToolTipText = QT_TR_NOOP("Create an arc by its end points and a point along the arc");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Create3PointArc";
eType = ForEdit;
}
void CmdSketcherCreate3PointArc::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointArc() );
}
bool CmdSketcherCreate3PointArc::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
DEF_STD_CMD_ACL(CmdSketcherCompCreateArc);
CmdSketcherCompCreateArc::CmdSketcherCompCreateArc()
: Command("Sketcher_CompCreateArc")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create arc");
sToolTipText = QT_TR_NOOP("Create an arc in the sketcher");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
eType = ForEdit;
}
void CmdSketcherCompCreateArc::activated(int iMsg)
{
if (iMsg==0)
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArc());
else if (iMsg==1)
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointArc());
else
return;
// Since the default icon is reset when enabing/disabling the command we have
// to explicitly set the icon of the used command.
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
assert(iMsg < a.size());
pcAction->setIcon(a[iMsg]->icon());
}
Gui::Action * CmdSketcherCompCreateArc::createAction(void)
{
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
pcAction->setDropDownMenu(true);
applyCommandData(pcAction);
QAction* arc1 = pcAction->addAction(QString());
arc1->setIcon(Gui::BitmapFactory().pixmapFromSvg("Sketcher_CreateArc", QSize(24,24)));
QAction* arc2 = pcAction->addAction(QString());
arc2->setIcon(Gui::BitmapFactory().pixmapFromSvg("Sketcher_Create3PointArc", QSize(24,24)));
_pcAction = pcAction;
languageChange();
pcAction->setIcon(arc1->icon());
int defaultId = 0;
pcAction->setProperty("defaultAction", QVariant(defaultId));
return pcAction;
}
void CmdSketcherCompCreateArc::languageChange()
{
Command::languageChange();
if (!_pcAction)
return;
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
QAction* arc1 = a[0];
arc1->setText(QApplication::translate("CmdSketcherCompCreateArc","Center and end points"));
arc1->setToolTip(QApplication::translate("Sketcher_CreateArc","Create an arc by its center and by its end points"));
arc1->setStatusTip(QApplication::translate("Sketcher_CreateArc","Create an arc by its center and by its end points"));
QAction* arc2 = a[1];
arc2->setText(QApplication::translate("CmdSketcherCompCreateArc","End points and rim point"));
arc2->setToolTip(QApplication::translate("Sketcher_Create3PointArc","Create an arc by its end points and a point along the arc"));
arc2->setStatusTip(QApplication::translate("Sketcher_Create3PointArc","Create an arc by its end points and a point along the arc"));
}
bool CmdSketcherCompCreateArc::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
/* XPM */
static const char *cursor_createcircle[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+........#######..........",
"......+......##.......##........",
"......+.....#...........#.......",
"......+....#.............#......",
"......+...#...............#.....",
".........#.................#....",
"........#...................#...",
"........#...................#...",
".......#.....................#..",
".......#.....................#..",
".......#.........###.........#..",
".......#.........#.#.........#..",
".......#.........###.........#..",
".......#.....................#..",
".......#.....................#..",
"........#...................#...",
"........#...................#...",
".........#.................#....",
"..........#...............#.....",
"...........#.............#......",
"............#...........#.......",
".............##.......##........",
"...............#######..........",
"................................"};
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 activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createcircle),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second) {
double rx0 = onSketchPos.fX - EditCurve[0].fX;
double ry0 = onSketchPos.fY - EditCurve[0].fY;
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].fX + rx, EditCurve[0].fY + ry);
EditCurve[17+i] = Base::Vector2D(EditCurve[0].fX - rx, EditCurve[0].fY - ry);
}
EditCurve[33] = EditCurve[1];
// Display radius for user
float radius = (onSketchPos - EditCurve[0]).Length();
SbString text;
text.sprintf(" (%.1fR)", radius);
setPositionText(onSketchPos, text);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f),
AutoConstraint::CURVE)) {
renderSuggestConstraintsCursor(sugConstr2);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
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)
{
if (Mode==STATUS_Close) {
double rx = EditCurve[1].fX - EditCurve[0].fX;
double ry = EditCurve[1].fY - EditCurve[0].fY;
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch circle");
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addGeometry(Part.Circle"
"(App.Vector(%f,%f,0),App.Vector(0,0,1),%f))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[0].fX, EditCurve[0].fY,
sqrt(rx*rx + ry*ry));
Gui::Command::commitCommand();
Gui::Command::updateActive();
// add auto constraints for the center point
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::mid);
sugConstr1.clear();
}
// add suggested constraints for circumference
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::none);
sugConstr2.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
SelectMode Mode;
std::vector<Base::Vector2D> EditCurve;
std::vector<AutoConstraint> sugConstr1, sugConstr2;
};
DEF_STD_CMD_A(CmdSketcherCreateCircle);
CmdSketcherCreateCircle::CmdSketcherCreateCircle()
: Command("Sketcher_CreateCircle")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create circle");
sToolTipText = QT_TR_NOOP("Create a circle in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateCircle";
eType = ForEdit;
}
void CmdSketcherCreateCircle::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerCircle() );
}
bool CmdSketcherCreateCircle::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
/* XPM */
static const char *cursor_create3pointcircle[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+........#######..........",
"......+......##.......##........",
"......+.....#...........#.......",
"......+....#.............#......",
"......+...#...............#.....",
".........#.................#....",
".......###.................###..",
".......#.#.................#.#..",
".......###.................###..",
".......#.....................#..",
".......#.........###.........#..",
".......#.........#.#.........#..",
".......#.........###.........#..",
".......#.....................#..",
".......#.....................#..",
"........#...................#...",
"........#...................#...",
".........#.................#....",
"..........#...............#.....",
"...........#.............#......",
"............#...........#.......",
".............##..###..##........",
"...............###.###..........",
".................###............"};
class DrawSketchHandler3PointCircle : public DrawSketchHandler
{
public:
DrawSketchHandler3PointCircle()
: Mode(STATUS_SEEK_First),EditCurve(2),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 activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_create3pointcircle),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode == STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f),
AutoConstraint::CURVE)) {
// Disable tangent snap on 1st point
if (sugConstr1.back().Type == Sketcher::Tangent)
sugConstr1.pop_back();
else
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode == STATUS_SEEK_Second || Mode == STATUS_SEEK_Third) {
if (Mode == STATUS_SEEK_Second)
CenterPoint = EditCurve[N+1] = (onSketchPos - FirstPoint)/2 + FirstPoint;
else
CenterPoint = EditCurve[N+1] = 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.fX + radius*cos(angle),
CenterPoint.fY + 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);
sketchgui->drawEdit(EditCurve);
if (Mode == STATUS_SEEK_Second) {
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f),
AutoConstraint::CURVE)) {
// Disable tangent snap on 2nd point
if (sugConstr2.back().Type == Sketcher::Tangent)
sugConstr2.pop_back();
else
renderSuggestConstraintsCursor(sugConstr2);
return;
}
}
else {
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.0,0.0),
AutoConstraint::CURVE)) {
renderSuggestConstraintsCursor(sugConstr3);
return;
}
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
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);
sketchgui->drawEdit(EditCurve);
applyCursor();
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
// Need to look at. rx might need fixing.
if (Mode==STATUS_End) {
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch circle");
Gui::Command::doCommand(Gui::Command::Doc,
"App.ActiveDocument.%s.addGeometry(Part.Circle"
"(App.Vector(%f,%f,0),App.Vector(0,0,1),%f))",
sketchgui->getObject()->getNameInDocument(),
CenterPoint.fX, CenterPoint.fY,
radius);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// Auto Constraint first picked point
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::none);
sugConstr1.clear();
}
// Auto Constraint second picked point
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::none);
sugConstr2.clear();
}
// Auto Constraint third picked point
if (sugConstr3.size() > 0) {
createAutoConstraints(sugConstr3, getHighestCurveIndex(), Sketcher::none);
sugConstr3.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
SelectMode Mode;
std::vector<Base::Vector2D> EditCurve;
Base::Vector2D CenterPoint, FirstPoint, SecondPoint;
double radius, N; // N should be even
std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
};
DEF_STD_CMD_A(CmdSketcherCreate3PointCircle);
CmdSketcherCreate3PointCircle::CmdSketcherCreate3PointCircle()
: Command("Sketcher_Create3PointCircle")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create circle by three points");
sToolTipText = QT_TR_NOOP("Create a circle by 3 perimeter points");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Create3PointCircle";
eType = ForEdit;
}
void CmdSketcherCreate3PointCircle::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointCircle() );
}
bool CmdSketcherCreate3PointCircle::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
DEF_STD_CMD_ACL(CmdSketcherCompCreateCircle);
CmdSketcherCompCreateCircle::CmdSketcherCompCreateCircle()
: Command("Sketcher_CompCreateCircle")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create circle");
sToolTipText = QT_TR_NOOP("Create a circle in the sketcher");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
eType = ForEdit;
}
void CmdSketcherCompCreateCircle::activated(int iMsg)
{
if (iMsg==0)
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerCircle());
else if (iMsg==1)
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointCircle());
else
return;
// Since the default icon is reset when enabing/disabling the command we have
// to explicitly set the icon of the used command.
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
assert(iMsg < a.size());
pcAction->setIcon(a[iMsg]->icon());
}
Gui::Action * CmdSketcherCompCreateCircle::createAction(void)
{
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
pcAction->setDropDownMenu(true);
applyCommandData(pcAction);
QAction* arc1 = pcAction->addAction(QString());
arc1->setIcon(Gui::BitmapFactory().pixmapFromSvg("Sketcher_CreateCircle", QSize(24,24)));
QAction* arc2 = pcAction->addAction(QString());
arc2->setIcon(Gui::BitmapFactory().pixmapFromSvg("Sketcher_Create3PointCircle", QSize(24,24)));
_pcAction = pcAction;
languageChange();
pcAction->setIcon(arc1->icon());
int defaultId = 0;
pcAction->setProperty("defaultAction", QVariant(defaultId));
return pcAction;
}
void CmdSketcherCompCreateCircle::languageChange()
{
Command::languageChange();
if (!_pcAction)
return;
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
QAction* arc1 = a[0];
arc1->setText(QApplication::translate("CmdSketcherCompCreateCircle", "Center and rim point"));
arc1->setToolTip(QApplication::translate("Sketcher_CreateCircle", "Create a circle by its center and by a rim point"));
arc1->setStatusTip(QApplication::translate("Sketcher_CreateCircle", "Create a circle by its center and by a rim point"));
QAction* arc2 = a[1];
arc2->setText(QApplication::translate("CmdSketcherCompCreateCircle", "3 rim points"));
arc2->setToolTip(QApplication::translate("Sketcher_Create3PointCircle", "Create a circle by 3 rim points"));
arc2->setStatusTip(QApplication::translate("Sketcher_Create3PointCircle", "Create a circle by 3 rim points"));
}
bool CmdSketcherCompCreateCircle::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
/* XPM */
static const char *cursor_createpoint[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"................................",
"................................",
".................++++...........",
"................++++++..........",
"...............++++++++.........",
"...............++++++++.........",
"...............++++++++.........",
"...............++++++++.........",
"................++++++..........",
".................++++...........",
"................................",
"................................",
"................................",
"................................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandlerPoint: public DrawSketchHandler
{
public:
DrawSketchHandlerPoint() : selectionDone(false) {}
virtual ~DrawSketchHandlerPoint() {}
virtual void activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createpoint),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr);
return;
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
EditPoint = onSketchPos;
selectionDone = true;
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
if (selectionDone){
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add sketch point");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Point(App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditPoint.fX,EditPoint.fY);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// add auto constraints for the line segment start
if (sugConstr.size() > 0) {
createAutoConstraints(sugConstr, getHighestCurveIndex(), Sketcher::start);
sugConstr.clear();
}
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
bool selectionDone;
Base::Vector2D EditPoint;
std::vector<AutoConstraint> sugConstr;
};
DEF_STD_CMD_A(CmdSketcherCreatePoint);
CmdSketcherCreatePoint::CmdSketcherCreatePoint()
: Command("Sketcher_CreatePoint")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create point");
sToolTipText = QT_TR_NOOP("Create a point in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreatePoint";
eType = ForEdit;
}
void CmdSketcherCreatePoint::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPoint());
}
bool CmdSketcherCreatePoint::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
DEF_STD_CMD_A(CmdSketcherCreateText);
CmdSketcherCreateText::CmdSketcherCreateText()
: Command("Sketcher_CreateText")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create text");
sToolTipText = QT_TR_NOOP("Create text in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateText";
eType = ForEdit;
}
void CmdSketcherCreateText::activated(int iMsg)
{
}
bool CmdSketcherCreateText::isActive(void)
{
return false;
}
// ======================================================================================
DEF_STD_CMD_A(CmdSketcherCreateDraftLine);
CmdSketcherCreateDraftLine::CmdSketcherCreateDraftLine()
: Command("Sketcher_CreateDraftLine")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create draft line");
sToolTipText = QT_TR_NOOP("Create a draft line in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_DraftLine";
eType = ForEdit;
}
void CmdSketcherCreateDraftLine::activated(int iMsg)
{
}
bool CmdSketcherCreateDraftLine::isActive(void)
{
return false;
}
// ======================================================================================
namespace SketcherGui {
class FilletSelection : public Gui::SelectionFilterGate
{
App::DocumentObject* object;
public:
FilletSelection(App::DocumentObject* obj)
: Gui::SelectionFilterGate((Gui::SelectionFilter*)0), 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())
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->getCoincidentPoints(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;
}
};
};
/* XPM */
static const char *cursor_createfillet[]={
"32 32 3 1",
"+ c white",
"* c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+..*......................",
".........*......................",
".........*......................",
".........*......................",
".........*......................",
".........*......................",
".........*.........***..........",
".........*.........*.*..........",
".........*.........***..........",
".........*......................",
".........*......................",
"..........*.....................",
"..........*.....................",
"...........*....................",
"............*...................",
".............*..................",
"..............*.................",
"...............**...............",
".................**************.",
"................................"};
class DrawSketchHandlerFillet: public DrawSketchHandler
{
public:
DrawSketchHandlerFillet() : Mode(STATUS_SEEK_First) {}
virtual ~DrawSketchHandlerFillet()
{
Gui::Selection().rmvSelectionGate();
}
enum SelectMode{
STATUS_SEEK_First,
STATUS_SEEK_Second
};
virtual void activated(ViewProviderSketch *sketchgui)
{
Gui::Selection().rmvSelectionGate();
Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject()));
setCursor(QPixmap(cursor_createfillet),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
int VtId = sketchgui->getPreselectPoint();
if (Mode == STATUS_SEEK_First && VtId != -1) {
int GeoId;
Sketcher::PointPos PosId=Sketcher::none;
sketchgui->getSketchObject()->getGeoVertexIndex(VtId,GeoId,PosId);
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
(PosId == Sketcher::start || PosId == Sketcher::end)) {
// guess fillet radius
double radius=-1;
std::vector<int> GeoIdList;
std::vector<Sketcher::PointPos> PosIdList;
sketchgui->getSketchObject()->getCoincidentPoints(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]);
if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg1 = dynamic_cast<const Part::GeomLineSegment *>(geom1);
const Part::GeomLineSegment *lineSeg2 = dynamic_cast<const Part::GeomLineSegment *>(geom2);
Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint();
Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint();
if (PosIdList[0] == Sketcher::end)
dir1 *= -1;
if (PosIdList[1] == Sketcher::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;
// create fillet at point
Gui::Command::openCommand("Create fillet");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.fillet(%d,%d,%f)",
sketchgui->getObject()->getNameInDocument(),
GeoId, PosId, radius);
Gui::Command::commitCommand();
Gui::Command::updateActive();
}
return true;
}
int GeoId = sketchgui->getPreselectCurve();
if (GeoId > -1) {
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
if (geom->getTypeId() == Part::GeomLineSegment::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.fX
,onSketchPos.fY
,0.f);
}
else if (Mode==STATUS_SEEK_Second) {
int secondCurve = GeoId;
Base::Vector2D secondPos = onSketchPos;
// guess fillet radius
const Part::GeomLineSegment *lineSeg1 = dynamic_cast<const Part::GeomLineSegment *>
(sketchgui->getSketchObject()->getGeometry(firstCurve));
const Part::GeomLineSegment *lineSeg2 = dynamic_cast<const Part::GeomLineSegment *>
(sketchgui->getSketchObject()->getGeometry(secondCurve));
Base::Vector3d refPnt1(firstPos.fX, firstPos.fY, 0.f);
Base::Vector3d refPnt2(secondPos.fX, secondPos.fY, 0.f);
double radius = Part::suggestFilletRadius(lineSeg1, lineSeg2, refPnt1, refPnt2);
if (radius < 0)
return false;
// create fillet between lines
Gui::Command::openCommand("Create fillet");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f)",
sketchgui->getObject()->getNameInDocument(),
firstCurve, secondCurve,
firstPos.fX, firstPos.fY,
secondPos.fX, secondPos.fY, radius);
Gui::Command::commitCommand();
Gui::Command::updateActive();
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;
}
protected:
SelectMode Mode;
int firstCurve;
Base::Vector2D firstPos;
};
DEF_STD_CMD_A(CmdSketcherCreateFillet);
CmdSketcherCreateFillet::CmdSketcherCreateFillet()
: Command("Sketcher_CreateFillet")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create fillet");
sToolTipText = QT_TR_NOOP("Create a fillet between two lines or at a coincident point");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateFillet";
sAccel = "F";
eType = ForEdit;
}
void CmdSketcherCreateFillet::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerFillet());
}
bool CmdSketcherCreateFillet::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
namespace SketcherGui {
class TrimmingSelection : public Gui::SelectionFilterGate
{
App::DocumentObject* object;
public:
TrimmingSelection(App::DocumentObject* obj)
: Gui::SelectionFilterGate((Gui::SelectionFilter*)0), 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;
}
};
};
/* XPM */
static const char *cursor_trimming[]={
"32 32 3 1",
"+ c white",
"* c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+.........................",
"......+.........................",
"......+......................*..",
"......+....................**...",
"......+...................**....",
".*..............................",
"..*.....................*.......",
"...*..................**........",
".....*...............**.........",
"......*.........................",
".......*..........*.............",
".........*......**..............",
"..........*....**...............",
"...........****.................",
"............*.*.................",
"............***.................",
"..........*....*................",
".........*.......*..............",
".......*..........*.............",
"......*............*............",
"....*................*..........",
"...*..................*.........",
".*.....................*........",
".........................*......"};
class DrawSketchHandlerTrimming: public DrawSketchHandler
{
public:
DrawSketchHandlerTrimming() {}
virtual ~DrawSketchHandlerTrimming()
{
Gui::Selection().rmvSelectionGate();
}
virtual void activated(ViewProviderSketch *sketchgui)
{
Gui::Selection().clearSelection();
Gui::Selection().rmvSelectionGate();
Gui::Selection().addSelectionGate(new TrimmingSelection(sketchgui->getObject()));
setCursor(QPixmap(cursor_trimming),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
int GeoId = sketchgui->getPreselectCurve();
if (GeoId > -1) {
const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
geom->getTypeId() == Part::GeomCircle::getClassTypeId()
) {
try {
Gui::Command::openCommand("Trim edge");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.trim(%d,App.Vector(%f,%f,0))",
sketchgui->getObject()->getNameInDocument(),
GeoId, onSketchPos.fX, onSketchPos.fY);
Gui::Command::commitCommand();
Gui::Command::updateActive();
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
}
}
}
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;
}
};
DEF_STD_CMD_A(CmdSketcherTrimming);
CmdSketcherTrimming::CmdSketcherTrimming()
: Command("Sketcher_Trimming")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Trim edge");
sToolTipText = QT_TR_NOOP("Trim an edge with respect to the picked position");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Trimming";
sAccel = "T";
eType = ForEdit;
}
void CmdSketcherTrimming::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerTrimming());
}
bool CmdSketcherTrimming::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================
namespace SketcherGui {
class ExternalSelection : public Gui::SelectionFilterGate
{
App::DocumentObject* object;
public:
ExternalSelection(App::DocumentObject* obj)
: Gui::SelectionFilterGate((Gui::SelectionFilter*)0), object(obj)
{}
bool allow(App::Document *pDoc, App::DocumentObject *pObj, const char *sSubName)
{
Sketcher::SketchObject *sketch = static_cast<Sketcher::SketchObject*>(object);
App::DocumentObject *support = sketch->Support.getValue();
// for the moment we allow external constraints only from the support
if (pObj != support)
return false;
if (!sSubName || sSubName[0] == '\0')
return false;
std::string element(sSubName);
// for the moment we allow only edges and vertices
if ((element.size() > 4 && element.substr(0,4) == "Edge") ||
(element.size() > 6 && element.substr(0,6) == "Vertex")) {
return true;
}
return false;
}
};
};
/* XPM */
static const char *cursor_external[]={
"32 32 3 1",
"+ c white",
"* c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+....***************......",
".........**...............***...",
"........**................***...",
".......**................**.*...",
"......*.................*...*...",
"....**................**....*...",
"...**................**.....*...",
"..**................**......*...",
"..******************........*...",
"..*................*........*...",
"..*................*........*...",
"..*................*........*...",
"..*................*............",
"..*................*............",
"..*................*............",
"..*................*............",
"..*................*............",
"..*................*............",
"................................",
"................................"};
class DrawSketchHandlerExternal: public DrawSketchHandler
{
public:
DrawSketchHandlerExternal() {}
virtual ~DrawSketchHandlerExternal()
{
Gui::Selection().rmvSelectionGate();
}
virtual void activated(ViewProviderSketch *sketchgui)
{
sketchgui->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()));
setCursor(QPixmap(cursor_external),7,7);
}
virtual void deactivated(ViewProviderSketch *sketchgui)
{
sketchgui->setAxisPickStyle(true);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Gui::Selection().getPreselection().pObjectName)
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
return true;
}
virtual bool onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (msg.Type == Gui::SelectionChanges::AddSelection) {
std::string subName(msg.pSubName);
if ((subName.size() > 4 && subName.substr(0,4) == "Edge") ||
(subName.size() > 6 && subName.substr(0,6) == "Vertex")) {
try {
Gui::Command::openCommand("Add external geometry");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addExternal(\"%s\",\"%s\")",
sketchgui->getObject()->getNameInDocument(),
msg.pObjectName, msg.pSubName);
Gui::Command::commitCommand();
Gui::Command::updateActive();
Gui::Selection().clearSelection();
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
}
return true;
}
}
return false;
}
};
DEF_STD_CMD_A(CmdSketcherExternal);
CmdSketcherExternal::CmdSketcherExternal()
: Command("Sketcher_External")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("External geometry");
sToolTipText = QT_TR_NOOP("Create an edge linked to an external geometry");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_External";
sAccel = "E";
eType = ForEdit;
}
void CmdSketcherExternal::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerExternal());
}
bool CmdSketcherExternal::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
/* Create Slot =======================================================*/
/* XPM */
static const char *cursor_creatslot[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"+++++...+++++...................",
"................................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"......+.........................",
"................................",
"................................",
"..........................###...",
"........###################.##..",
".......#..................###.#.",
"......#........................#",
".....#.........................#",
"....#.....###..................#",
"....#.....#.#..................#",
".....#....###.................#.",
"......#.......................#.",
".......#.....................#..",
"........#####################...",
"................................",
"................................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandlerSlot: public DrawSketchHandler
{
public:
DrawSketchHandlerSlot():Mode(STATUS_SEEK_First),EditCurve(36){}
virtual ~DrawSketchHandlerSlot(){}
/// mode table
enum BoxMode {
STATUS_SEEK_First, /**< enum value ----. */
STATUS_SEEK_Second, /**< enum value ----. */
STATUS_End
};
virtual void activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_creatslot),7,7);
}
virtual void mouseMove(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
setPositionText(onSketchPos);
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second) {
float dx = onSketchPos.fX - StartPos.fX;
float dy = onSketchPos.fY - StartPos.fY;
lx=0;ly=0;a=0;
if(fabs(dx) > fabs(dy)){
lx = dx;
r = dy;
rev = dx/fabs(dx);
}else{
ly = dy;
r = dx;
a = 8;
rev = dy/fabs(dy);
}
for (int i=0; i < 17; i++) {
double angle = (i+a)*M_PI/16.0;
double rx = -fabs(r)* rev * sin(angle) ;
double ry = fabs(r) * rev *cos(angle) ;
EditCurve[i] = Base::Vector2D(StartPos.fX + rx, StartPos.fY + ry);
EditCurve[18+i] = Base::Vector2D(StartPos.fX - rx+lx, StartPos.fY - ry+ly);
}
EditCurve[17] = EditCurve[16] + Base::Vector2D(lx,ly);
EditCurve[35] = EditCurve[0] ;
//EditCurve[34] = EditCurve[0];
// Display radius for user
float radius = (onSketchPos - EditCurve[0]).Length();
SbString text;
text.sprintf(" (%.1fR %.1fL)", r,lx);
setPositionText(onSketchPos, text);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2D(0.f,0.f),
AutoConstraint::CURVE)) {
renderSuggestConstraintsCursor(sugConstr2);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_SEEK_First){
StartPos = onSketchPos;
Mode = STATUS_SEEK_Second;
}
else {
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2D onSketchPos)
{
if (Mode==STATUS_End){
unsetCursor();
resetPositionText();
Gui::Command::openCommand("Add slot");
int firstCurve = getHighestCurveIndex() + 1;
// add the geometry to the sketch
double start, end;
if(fabs(lx)>fabs(ly)){
start = M_PI/2;
end = -M_PI/2;
}else{
start = 0;
end = M_PI;
}
if(ly>0 || lx <0){
double temp = start;
start = end;
end = temp;
}
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f))",
sketchgui->getObject()->getNameInDocument(),
StartPos.fX,StartPos.fY, // center of the arc
fabs(r), // radius
start,end // start and end angle
);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f))",
sketchgui->getObject()->getNameInDocument(),
StartPos.fX+lx,StartPos.fY+ly, // center of the arc
fabs(r), // radius
end,start // start and end angle
);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[16].fX,EditCurve[16].fY,EditCurve[17].fX,EditCurve[17].fY);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addGeometry(Part.Line(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))",
sketchgui->getObject()->getNameInDocument(),
EditCurve[0].fX,EditCurve[0].fY,EditCurve[34].fX,EditCurve[34].fY);
// add the four coincidents to ty them together
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,1,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve,firstCurve+3);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve,firstCurve+2);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,1)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+2,firstCurve+1);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,2,%i,2)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+3,firstCurve+1);
//// add the either horizontal or vertical constraints
if(fabs(lx)>fabs(ly))
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Horizontal',%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+2);
else
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Vertical',%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+2);
//// add the tnagent constraints
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve,firstCurve+2);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve,firstCurve+3);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+1,firstCurve+2);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve+1,firstCurve+3);
// make the two arcs equal
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Equal',%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,firstCurve,firstCurve+1);
Gui::Command::commitCommand();
Gui::Command::updateActive();
// add auto constraints at the start of the first side
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3 , Sketcher::mid);
sugConstr1.clear();
}
// add auto constraints at the end of the second side
if (sugConstr2.size() > 0) {
createAutoConstraints(sugConstr2, getHighestCurveIndex() - 2, Sketcher::end);
sugConstr2.clear();
}
EditCurve.clear();
sketchgui->drawEdit(EditCurve);
sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
}
return true;
}
protected:
BoxMode Mode;
Base::Vector2D StartPos;
double lx,ly,r,a,rev;
std::vector<Base::Vector2D> EditCurve;
std::vector<AutoConstraint> sugConstr1, sugConstr2;
};
DEF_STD_CMD_A(CmdSketcherCreateSlot);
CmdSketcherCreateSlot::CmdSketcherCreateSlot()
: Command("Sketcher_CreateSlot")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create slot");
sToolTipText = QT_TR_NOOP("Create a slot in the sketch");
sWhatsThis = sToolTipText;
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CreateSlot";
sAccel = "";
eType = ForEdit;
}
void CmdSketcherCreateSlot::activated(int iMsg)
{
ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerSlot() );
}
bool CmdSketcherCreateSlot::isActive(void)
{
return isCreateGeoActive(getActiveGuiDocument());
}
void CreateSketcherCommandsCreateGeo(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdSketcherCreatePoint());
rcCmdMgr.addCommand(new CmdSketcherCreateArc());
rcCmdMgr.addCommand(new CmdSketcherCreate3PointArc());
rcCmdMgr.addCommand(new CmdSketcherCompCreateArc());
rcCmdMgr.addCommand(new CmdSketcherCreateCircle());
rcCmdMgr.addCommand(new CmdSketcherCreate3PointCircle());
rcCmdMgr.addCommand(new CmdSketcherCompCreateCircle());
rcCmdMgr.addCommand(new CmdSketcherCreateLine());
rcCmdMgr.addCommand(new CmdSketcherCreatePolyline());
rcCmdMgr.addCommand(new CmdSketcherCreateRectangle());
rcCmdMgr.addCommand(new CmdSketcherCreateSlot());
rcCmdMgr.addCommand(new CmdSketcherCreateFillet());
//rcCmdMgr.addCommand(new CmdSketcherCreateText());
//rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine());
rcCmdMgr.addCommand(new CmdSketcherTrimming());
rcCmdMgr.addCommand(new CmdSketcherExternal());
}