/*************************************************************************** * 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()); }