/***************************************************************************
* 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_
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ViewProviderSketch.h"
#include "DrawSketchHandler.h"
#include
#include
#include
using namespace std;
using namespace SketcherGui;
/* helper functions ======================================================*/
void ActivateHandler(Gui::Document *doc,DrawSketchHandler *handler)
{
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId()))
dynamic_cast
(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(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(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)
{
setPositionText(onSketchPos);
if (Mode==STATUS_SEEK_First) {
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second){
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 EditCurve;
std::vector 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)
{
setPositionText(onSketchPos);
if (Mode==STATUS_SEEK_First) {
if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2D(0.f,0.f))) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
else if (Mode==STATUS_SEEK_Second) {
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.f,0.f))) {
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 EditCurve;
std::vector 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),
firstVertex(-1),firstCurve(-1),previousCurve(-1),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 (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;
setPositionText(onSketchPos);
if (Mode==STATUS_SEEK_First) {
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);
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);
float 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
float x1 = EditCurve[0].fX;
float y1 = EditCurve[0].fY;
float x2 = x1 + Tangent.fX;
float y2 = y1 + Tangent.fY;
float x3 = onSketchPos.fX;
float y3 = onSketchPos.fY;
if ((x2*y3-x3*y2)-(x1*y3-x3*y1)+(x1*y2-x2*y1) > 0)
arcRadius *= -1;
CenterPoint = EditCurve[0] + Base::Vector2D(arcRadius * Tangent.fY, -arcRadius * Tangent.fX);
float rx = EditCurve[0].fX - CenterPoint.fX;
float ry = EditCurve[0].fY - CenterPoint.fY;
startAngle = atan2(ry,rx);
float rxe = onSketchPos.fX - CenterPoint.fX;
float rye = onSketchPos.fY - CenterPoint.fY;
float arcAngle = atan2(-rxe*ry + rye*rx, rxe*rx + rye*ry);
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++) {
float angle = i*arcAngle/29.0;
float dx = rx * cos(angle) - ry * sin(angle);
float 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);
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 (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, EditMode[0]
if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
TransitionMode = TRANSITION_MODE_Tangent;
sugConstr1.erase(sugConstr1.begin()+i); // actually we should clear the vector completely
break;
}
}
// in case a transition is set up, firstCurve and firstVertex should
// remain set to -1 in order to disable closing the wire
if (previousCurve == -1) {
// remember our first point
firstVertex = getHighestVertexIndex() + 1;
firstCurve = getHighestCurveIndex() + 1;
}
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
}
if (sketchgui->getPreselectPoint() == firstVertex && firstVertex != -1)
Mode = STATUS_Close;
else
Mode = STATUS_Do;
}
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
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 (previousCurve != -1) {
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) {
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) {
int firstGeoId;
Sketcher::PointPos firstPosId;
sketchgui->getSketchObject()->getGeoVertexIndex(firstVertex, firstGeoId, firstPosId);
//assert(firstCurve == firstGeoId);
// 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 sugConstr;
for (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];
sketchgui->drawEdit(EditCurve);
}
}
return true;
}
protected:
SELECT_MODE Mode;
SEGMENT_MODE SegmentMode;
TRANSITION_MODE TransitionMode;
bool suppressTransition;
std::vector EditCurve;
int firstVertex;
int firstCurve;
int previousCurve;
Sketcher::PointPos previousPosId;
std::vector sugConstr1, sugConstr2;
Base::Vector2D CenterPoint;
Base::Vector3d dirVec;
float 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(geom);
EditCurve[0] = Base::Vector2D(lineSeg->getEndPoint().x, lineSeg->getEndPoint().y);
dirVec.Set(lineSeg->getEndPoint().x - lineSeg->getStartPoint().x,
lineSeg->getEndPoint().y - lineSeg->getStartPoint().y,
0.f);
if (PosId == Sketcher::start)
dirVec *= -1;
}
else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arcSeg = dynamic_cast(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");
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) {
float dx_ = onSketchPos.fX - EditCurve[0].fX;
float dy_ = onSketchPos.fY - EditCurve[0].fY;
for (int i=0; i < 16; i++) {
float angle = i*M_PI/16.0;
float dx = dx_ * cos(angle) + dy_ * sin(angle);
float 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_) * 180 / M_PI;
char buf[40];
sprintf( buf, " (R%.1f,%.1f)", radius, angle);
std::string text = buf;
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) {
float angle1 = atan2(onSketchPos.fY - CenterPoint.fY,
onSketchPos.fX - CenterPoint.fX) - startAngle;
float angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
for (int i=1; i <= 29; i++) {
float angle = i*arcAngle/29.0;
float dx = rx * cos(angle) - ry * sin(angle);
float dy = rx * sin(angle) + ry * cos(angle);
EditCurve[i] = Base::Vector2D(CenterPoint.fX + dx, CenterPoint.fY + dy);
}
// Display radius and end angle
float radius = (onSketchPos - EditCurve[0]).Length();
char buf[40];
sprintf( buf, " (R%.1f,%.1f)", radius, arcAngle * 180 / M_PI);
std::string text = buf;
setPositionText(onSketchPos, text);
sketchgui->drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2D(0.f,0.f))) {
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);
float angle1 = atan2(onSketchPos.fY - CenterPoint.fY,
onSketchPos.fX - CenterPoint.fX) - startAngle;
float 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 EditCurve;
Base::Vector2D CenterPoint;
float rx, ry, startAngle, endAngle, arcAngle;
std::vector 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");
sToolTipText = QT_TR_NOOP("Create an arc in the sketch");
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_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) {
float rx0 = onSketchPos.fX - EditCurve[0].fX;
float ry0 = onSketchPos.fY - EditCurve[0].fY;
for (int i=0; i < 16; i++) {
float angle = i*M_PI/16.0;
float rx = rx0 * cos(angle) + ry0 * sin(angle);
float 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();
char buf[40];
sprintf( buf, "R%.1f", radius);
std::string text = buf;
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) {
float rx = EditCurve[1].fX - EditCurve[0].fX;
float 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 EditCurve;
std::vector 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_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);
}
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 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 index=std::atoi(element.substr(4,4000).c_str());
Sketcher::SketchObject *Sketch = static_cast(object);
const Part::Geometry *geom = Sketch->getGeometry(index);
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId())
return true;
}
if (element.substr(0,6) == "Vertex") {
int index=std::atoi(element.substr(6,4000).c_str());
Sketcher::SketchObject *Sketch = static_cast(object);
std::vector GeoIdList;
std::vector PosIdList;
Sketch->getCoincidentPoints(index, 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 GeoIdList;
std::vector 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(geom1);
const Part::GeomLineSegment *lineSeg2 = dynamic_cast(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;
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
(sketchgui->getSketchObject()->getGeometry(firstCurve));
const Part::GeomLineSegment *lineSeg2 = dynamic_cast
(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 coincidental 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 index=std::atoi(element.substr(4,4000).c_str());
Sketcher::SketchObject *Sketch = static_cast(object);
const Part::Geometry *geom = Sketch->getGeometry(index);
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(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)
{
Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView();
Gui::View3DInventorViewer *viewer;
viewer = static_cast(mdi)->getViewer();
SoNode* root = viewer->getSceneGraph();
static_cast(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 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());
}
void CreateSketcherCommandsCreateGeo(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdSketcherCreatePoint());
rcCmdMgr.addCommand(new CmdSketcherCreateArc());
rcCmdMgr.addCommand(new CmdSketcherCreateCircle());
rcCmdMgr.addCommand(new CmdSketcherCreateLine());
rcCmdMgr.addCommand(new CmdSketcherCreatePolyline());
rcCmdMgr.addCommand(new CmdSketcherCreateRectangle());
rcCmdMgr.addCommand(new CmdSketcherCreateFillet());
//rcCmdMgr.addCommand(new CmdSketcherCreateText());
//rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine());
rcCmdMgr.addCommand(new CmdSketcherTrimming());
rcCmdMgr.addCommand(new CmdSketcherExternal());
}