diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index b4e1727571..5a2c8c6878 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -88,6 +88,7 @@ SET(Creator_tools draftguitools/gui_splines.py draftguitools/gui_beziers.py draftguitools/gui_rectangles.py + draftguitools/gui_arcs.py ) SET(Draft_GUI_tools @@ -112,7 +113,6 @@ SET(Draft_GUI_tools draftguitools/gui_heal.py draftguitools/gui_dimension_ops.py draftguitools/gui_lineslope.py - draftguitools/gui_arcs.py ${Creator_tools} draftguitools/README.md ) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 4e22c37e71..4904044811 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -91,7 +91,6 @@ from draftguitools.gui_grid import ToggleGrid from draftguitools.gui_heal import Heal from draftguitools.gui_dimension_ops import Draft_FlipDimension from draftguitools.gui_lineslope import Draft_Slope -from draftguitools.gui_arcs import Draft_Arc_3Points import draftguitools.gui_arrays # import DraftFillet import drafttaskpanels.task_shapestring as task_shapestring @@ -157,334 +156,8 @@ from draftguitools.gui_beziers import BezCurve from draftguitools.gui_beziers import CubicBezCurve from draftguitools.gui_beziers import BezierGroup from draftguitools.gui_rectangles import Rectangle - - -class Arc(Creator): - """the Draft_Arc FreeCAD command definition""" - - def __init__(self): - self.closedCircle=False - self.featureName = "Arc" - - def GetResources(self): - return {'Pixmap' : 'Draft_Arc', - 'Accel' : "A, R", - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Arc", "Arc"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Arc", "Creates an arc by center point and radius. CTRL to snap, SHIFT to constrain")} - - def Activated(self): - Creator.Activated(self,self.featureName) - if self.ui: - self.step = 0 - self.center = None - self.rad = None - self.angle = 0 # angle inscribed by arc - self.tangents = [] - self.tanpoints = [] - if self.featureName == "Arc": self.ui.arcUi() - else: self.ui.circleUi() - self.altdown = False - self.ui.sourceCmd = self - self.linetrack = trackers.lineTracker(dotted=True) - self.arctrack = trackers.arcTracker() - self.call = self.view.addEventCallback("SoEvent",self.action) - FreeCAD.Console.PrintMessage(translate("draft", "Pick center point")+"\n") - - def finish(self,closed=False,cont=False): - """finishes the arc""" - Creator.finish(self) - if self.ui: - self.linetrack.finalize() - self.arctrack.finalize() - self.doc.recompute() - if self.ui: - if self.ui.continueMode: - self.Activated() - - def updateAngle(self, angle): - # previous absolute angle - lastangle = self.firstangle + self.angle - if lastangle <= -2*math.pi: lastangle += 2*math.pi - if lastangle >= 2*math.pi: lastangle -= 2*math.pi - # compute delta = change in angle: - d0 = angle-lastangle - d1 = d0 + 2*math.pi - d2 = d0 - 2*math.pi - if abs(d0) < min(abs(d1), abs(d2)): - delta = d0 - elif abs(d1) < abs(d2): - delta = d1 - else: - delta = d2 - newangle = self.angle + delta - # normalize angle, preserving direction - if newangle >= 2*math.pi: newangle -= 2*math.pi - if newangle <= -2*math.pi: newangle += 2*math.pi - self.angle = newangle - - def action(self,arg): - """scene event handler""" - import DraftGeomUtils - if arg["Type"] == "SoKeyboardEvent": - if arg["Key"] == "ESCAPE": - self.finish() - elif arg["Type"] == "SoLocation2Event": - self.point,ctrlPoint,info = getPoint(self,arg) - # this is to make sure radius is what you see on screen - if self.center and DraftVecUtils.dist(self.point,self.center) > 0: - viewdelta = DraftVecUtils.project(self.point.sub(self.center), plane.axis) - if not DraftVecUtils.isNull(viewdelta): - self.point = self.point.add(viewdelta.negative()) - if (self.step == 0): # choose center - if hasMod(arg,MODALT): - if not self.altdown: - self.altdown = True - self.ui.switchUi(True) - else: - if self.altdown: - self.altdown = False - self.ui.switchUi(False) - elif (self.step == 1): # choose radius - if len(self.tangents) == 2: - cir = DraftGeomUtils.circleFrom2tan1pt(self.tangents[0], self.tangents[1], self.point) - self.center = DraftGeomUtils.findClosestCircle(self.point,cir).Center - self.arctrack.setCenter(self.center) - elif self.tangents and self.tanpoints: - cir = DraftGeomUtils.circleFrom1tan2pt(self.tangents[0], self.tanpoints[0], self.point) - self.center = DraftGeomUtils.findClosestCircle(self.point,cir).Center - self.arctrack.setCenter(self.center) - if hasMod(arg,MODALT): - if not self.altdown: - self.altdown = True - if info: - ob = self.doc.getObject(info['Object']) - num = int(info['Component'].lstrip('Edge'))-1 - ed = ob.Shape.Edges[num] - if len(self.tangents) == 2: - cir = DraftGeomUtils.circleFrom3tan(self.tangents[0], self.tangents[1], ed) - cl = DraftGeomUtils.findClosestCircle(self.point,cir) - self.center = cl.Center - self.rad = cl.Radius - self.arctrack.setCenter(self.center) - else: - self.rad = self.center.add(DraftGeomUtils.findDistance(self.center,ed).sub(self.center)).Length - else: - self.rad = DraftVecUtils.dist(self.point,self.center) - else: - if self.altdown: - self.altdown = False - self.rad = DraftVecUtils.dist(self.point,self.center) - self.ui.setRadiusValue(self.rad, "Length") - self.arctrack.setRadius(self.rad) - self.linetrack.p1(self.center) - self.linetrack.p2(self.point) - self.linetrack.on() - elif (self.step == 2): # choose first angle - currentrad = DraftVecUtils.dist(self.point,self.center) - if currentrad != 0: - angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) - else: angle = 0 - self.linetrack.p2(DraftVecUtils.scaleTo(self.point.sub(self.center),self.rad).add(self.center)) - self.ui.setRadiusValue(math.degrees(angle),unit="Angle") - self.firstangle = angle - else: # choose second angle - currentrad = DraftVecUtils.dist(self.point,self.center) - if currentrad != 0: - angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) - else: angle = 0 - self.linetrack.p2(DraftVecUtils.scaleTo(self.point.sub(self.center),self.rad).add(self.center)) - self.updateAngle(angle) - self.ui.setRadiusValue(math.degrees(self.angle),unit="Angle") - self.arctrack.setApertureAngle(self.angle) - - redraw3DView() - - elif arg["Type"] == "SoMouseButtonEvent": - if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): - if self.point: - if (self.step == 0): # choose center - if not self.support: - getSupport(arg) - self.point,ctrlPoint,info = getPoint(self,arg) - if hasMod(arg,MODALT): - snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1])) - if snapped: - ob = self.doc.getObject(snapped['Object']) - num = int(snapped['Component'].lstrip('Edge'))-1 - ed = ob.Shape.Edges[num] - self.tangents.append(ed) - if len(self.tangents) == 2: - self.arctrack.on() - self.ui.radiusUi() - self.step = 1 - self.ui.setNextFocus() - self.linetrack.on() - FreeCAD.Console.PrintMessage(translate("draft", "Pick radius")+"\n") - else: - if len(self.tangents) == 1: - self.tanpoints.append(self.point) - else: - self.center = self.point - self.node = [self.point] - self.arctrack.setCenter(self.center) - self.linetrack.p1(self.center) - self.linetrack.p2(self.view.getPoint(arg["Position"][0],arg["Position"][1])) - self.arctrack.on() - self.ui.radiusUi() - self.step = 1 - self.ui.setNextFocus() - self.linetrack.on() - FreeCAD.Console.PrintMessage(translate("draft", "Pick radius")+"\n") - if self.planetrack: - self.planetrack.set(self.point) - elif (self.step == 1): # choose radius - if self.closedCircle: - self.drawArc() - else: - self.ui.labelRadius.setText(translate("draft","Start angle")) - self.ui.radiusValue.setToolTip(translate("draft","Start angle")) - self.ui.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Angle).UserString) - self.linetrack.p1(self.center) - self.linetrack.on() - self.step = 2 - FreeCAD.Console.PrintMessage(translate("draft", "Pick start angle")+"\n") - elif (self.step == 2): # choose first angle - self.ui.labelRadius.setText(translate("draft","Aperture angle")) - self.ui.radiusValue.setToolTip(translate("draft","Aperture angle")) - self.step = 3 - # scale center->point vector for proper display - # u = DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad) obsolete? - self.arctrack.setStartAngle(self.firstangle) - FreeCAD.Console.PrintMessage(translate("draft", "Pick aperture")+"\n") - else: # choose second angle - self.step = 4 - self.drawArc() - - def drawArc(self): - """actually draws the FreeCAD object""" - rot,sup,pts,fil = self.getStrings() - if self.closedCircle: - try: - FreeCADGui.addModule("Draft") - if Draft.getParam("UsePartPrimitives",False): - # use primitive - self.commit(translate("draft","Create Circle"), - ['circle = FreeCAD.ActiveDocument.addObject("Part::Circle","Circle")', - 'circle.Radius = '+str(self.rad), - 'pl = FreeCAD.Placement()', - 'pl.Rotation.Q = '+rot, - 'pl.Base = '+DraftVecUtils.toString(self.center), - 'circle.Placement = pl', - 'Draft.autogroup(circle)', - 'FreeCAD.ActiveDocument.recompute()']) - else: - # building command string - FreeCADGui.addModule("Draft") - self.commit(translate("draft","Create Circle"), - ['pl=FreeCAD.Placement()', - 'pl.Rotation.Q='+rot, - 'pl.Base='+DraftVecUtils.toString(self.center), - 'circle = Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',support='+sup+')', - 'Draft.autogroup(circle)', - 'FreeCAD.ActiveDocument.recompute()']) - except: - print("Draft: error delaying commit") - else: - sta = math.degrees(self.firstangle) - end = math.degrees(self.firstangle+self.angle) - if end < sta: sta,end = end,sta - while True: - if sta > 360: - sta = sta - 360 - elif end > 360: - end = end - 360 - else: - break - try: - FreeCADGui.addModule("Draft") - if Draft.getParam("UsePartPrimitives",False): - # use primitive - self.commit(translate("draft","Create Arc"), - ['circle = FreeCAD.ActiveDocument.addObject("Part::Circle","Circle")', - 'circle.Radius = '+str(self.rad), - 'circle.Angle0 = '+str(sta), - 'circle.Angle1 = '+str(end), - 'pl = FreeCAD.Placement()', - 'pl.Rotation.Q = '+rot, - 'pl.Base = '+DraftVecUtils.toString(self.center), - 'circle.Placement = pl', - 'Draft.autogroup(circle)', - 'FreeCAD.ActiveDocument.recompute()']) - else: - # building command string - self.commit(translate("draft","Create Arc"), - ['pl=FreeCAD.Placement()', - 'pl.Rotation.Q='+rot, - 'pl.Base='+DraftVecUtils.toString(self.center), - 'circle = Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',startangle='+str(sta)+',endangle='+str(end)+',support='+sup+')', - 'Draft.autogroup(circle)', - 'FreeCAD.ActiveDocument.recompute()']) - except: - print("Draft: error delaying commit") - self.finish(cont=True) - - def numericInput(self,numx,numy,numz): - """this function gets called by the toolbar when valid x, y, and z have been entered there""" - self.center = Vector(numx,numy,numz) - self.node = [self.center] - self.arctrack.setCenter(self.center) - self.arctrack.on() - self.ui.radiusUi() - self.step = 1 - self.ui.setNextFocus() - FreeCAD.Console.PrintMessage(translate("draft", "Pick radius")+"\n") - - def numericRadius(self,rad): - import DraftGeomUtils - """this function gets called by the toolbar when valid radius have been entered there""" - if (self.step == 1): - self.rad = rad - if len(self.tangents) == 2: - cir = DraftGeomUtils.circleFrom2tan1rad(self.tangents[0], self.tangents[1], rad) - if self.center: - self.center = DraftGeomUtils.findClosestCircle(self.center,cir).Center - else: - self.center = cir[-1].Center - elif self.tangents and self.tanpoints: - cir = DraftGeomUtils.circleFrom1tan1pt1rad(self.tangents[0],self.tanpoints[0],rad) - if self.center: - self.center = DraftGeomUtils.findClosestCircle(self.center,cir).Center - else: - self.center = cir[-1].Center - if self.closedCircle: - self.drawArc() - else: - self.step = 2 - self.arctrack.setCenter(self.center) - self.ui.labelRadius.setText(translate("draft", "Start angle")) - self.ui.radiusValue.setToolTip(translate("draft", "Start angle")) - self.linetrack.p1(self.center) - self.linetrack.on() - self.ui.radiusValue.setText("") - self.ui.radiusValue.setFocus() - FreeCAD.Console.PrintMessage(translate("draft", "Pick start angle")+"\n") - elif (self.step == 2): - self.ui.labelRadius.setText(translate("draft", "Aperture angle")) - self.ui.radiusValue.setToolTip(translate("draft", "Aperture angle")) - self.firstangle = math.radians(rad) - if DraftVecUtils.equals(plane.axis, Vector(1,0,0)): u = Vector(0,self.rad,0) - else: u = DraftVecUtils.scaleTo(Vector(1,0,0).cross(plane.axis), self.rad) - urotated = DraftVecUtils.rotate(u, math.radians(rad), plane.axis) - self.arctrack.setStartAngle(self.firstangle) - self.step = 3 - self.ui.radiusValue.setText("") - self.ui.radiusValue.setFocus() - FreeCAD.Console.PrintMessage(translate("draft", "Pick aperture angle")+"\n") - else: - self.updateAngle(rad) - self.angle = math.radians(rad) - self.step = 4 - self.drawArc() +from draftguitools.gui_arcs import Arc +from draftguitools.gui_arcs import Draft_Arc_3Points class Circle(Arc): @@ -4077,17 +3750,6 @@ from draftguitools.gui_snaps import ShowSnapBar # drawing commands FreeCADGui.addCommand('Draft_Circle',Circle()) -class CommandArcGroup: - def GetCommands(self): - return tuple(['Draft_Arc','Draft_Arc_3Points']) - def GetResources(self): - return { 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ArcTools",'Arc tools'), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_ArcTools",'Arc tools') - } - def IsActive(self): - return not FreeCAD.ActiveDocument is None -FreeCADGui.addCommand('Draft_Arc',Arc()) -FreeCADGui.addCommand('Draft_ArcTools', CommandArcGroup()) FreeCADGui.addCommand('Draft_Text',Text()) FreeCADGui.addCommand('Draft_Dimension',Dimension()) FreeCADGui.addCommand('Draft_Polygon',Polygon()) diff --git a/src/Mod/Draft/draftguitools/gui_arcs.py b/src/Mod/Draft/draftguitools/gui_arcs.py index eed62a60fb..a82c17009c 100644 --- a/src/Mod/Draft/draftguitools/gui_arcs.py +++ b/src/Mod/Draft/draftguitools/gui_arcs.py @@ -22,25 +22,457 @@ # * USA * # * * # *************************************************************************** -"""Provides tools for creating arcs with the Draft Workbench.""" +"""Provides tools for creating circular arcs with the Draft Workbench.""" ## @package gui_arcs # \ingroup DRAFT -# \brief Provides tools for creating arcs with the Draft Workbench. +# \brief Provides tools for creating circular arcs with the Draft Workbench. + +import math from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD as App import FreeCADGui as Gui +from FreeCAD import Units as U import Draft_rc -import draftobjects.arc_3points as arc3 +import DraftVecUtils +import draftguitools.gui_base_original as gui_base_original import draftguitools.gui_base as gui_base +import draftguitools.gui_tool_utils as gui_tool_utils import draftguitools.gui_trackers as trackers +import draftobjects.arc_3points as arc3 import draftutils.utils as utils -from draftutils.translate import _tr +from draftutils.messages import _msg, _err +from draftutils.translate import translate, _tr # The module is used to prevent complaints from code checkers (flake8) True if Draft_rc.__name__ else False +class Arc(gui_base_original.Creator): + """Gui command for the Circular Arc tool.""" + + def __init__(self): + self.closedCircle = False + self.featureName = "Arc" + + def GetResources(self): + """Set icon, menu and tooltip.""" + _tip = ("Creates a circular arc by a center point and a radius.\n" + "CTRL to snap, SHIFT to constrain.") + + return {'Pixmap': 'Draft_Arc', + 'Accel': "A, R", + 'MenuText': QT_TRANSLATE_NOOP("Draft_Arc", "Arc"), + 'ToolTip': QT_TRANSLATE_NOOP("Draft_Arc", _tip)} + + def Activated(self): + """Execute when the command is called.""" + super().Activated(name=_tr(self.featureName)) + if self.ui: + self.step = 0 + self.center = None + self.rad = None + self.angle = 0 # angle inscribed by arc + self.tangents = [] + self.tanpoints = [] + if self.featureName == "Arc": + self.ui.arcUi() + else: + self.ui.circleUi() + self.altdown = False + self.ui.sourceCmd = self + self.linetrack = trackers.lineTracker(dotted=True) + self.arctrack = trackers.arcTracker() + self.call = self.view.addEventCallback("SoEvent", self.action) + _msg(translate("draft", "Pick center point")) + + def finish(self, closed=False, cont=False): + """Terminate the operation and close the arc if asked. + + Parameters + ---------- + closed: bool, optional + Close the line if `True`. + """ + super().finish(self) + if self.ui: + self.linetrack.finalize() + self.arctrack.finalize() + self.doc.recompute() + if self.ui and self.ui.continueMode: + self.Activated() + + def updateAngle(self, angle): + """Update the angle with the new value.""" + # previous absolute angle + lastangle = self.firstangle + self.angle + if lastangle <= -2 * math.pi: + lastangle += 2 * math.pi + if lastangle >= 2 * math.pi: + lastangle -= 2 * math.pi + # compute delta = change in angle: + d0 = angle - lastangle + d1 = d0 + 2 * math.pi + d2 = d0 - 2 * math.pi + if abs(d0) < min(abs(d1), abs(d2)): + delta = d0 + elif abs(d1) < abs(d2): + delta = d1 + else: + delta = d2 + newangle = self.angle + delta + # normalize angle, preserving direction + if newangle >= 2 * math.pi: + newangle -= 2 * math.pi + if newangle <= -2 * math.pi: + newangle += 2 * math.pi + self.angle = newangle + + def action(self, arg): + """Handle the 3D scene events. + + This is installed as an EventCallback in the Inventor view. + + Parameters + ---------- + arg: dict + Dictionary with strings that indicates the type of event received + from the 3D view. + """ + import DraftGeomUtils + plane = App.DraftWorkingPlane + + if arg["Type"] == "SoKeyboardEvent": + if arg["Key"] == "ESCAPE": + self.finish() + elif arg["Type"] == "SoLocation2Event": # mouse movement detection + self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) + # this is to make sure radius is what you see on screen + if self.center and DraftVecUtils.dist(self.point, self.center) > 0: + viewdelta = DraftVecUtils.project(self.point.sub(self.center), + plane.axis) + if not DraftVecUtils.isNull(viewdelta): + self.point = self.point.add(viewdelta.negative()) + if self.step == 0: # choose center + if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): + if not self.altdown: + self.altdown = True + self.ui.switchUi(True) + else: + if self.altdown: + self.altdown = False + self.ui.switchUi(False) + elif self.step == 1: # choose radius + if len(self.tangents) == 2: + cir = DraftGeomUtils.circleFrom2tan1pt(self.tangents[0], + self.tangents[1], + self.point) + _c = DraftGeomUtils.findClosestCircle(self.point, cir) + self.center = _c.Center + self.arctrack.setCenter(self.center) + elif self.tangents and self.tanpoints: + cir = DraftGeomUtils.circleFrom1tan2pt(self.tangents[0], + self.tanpoints[0], + self.point) + _c = DraftGeomUtils.findClosestCircle(self.point, cir) + self.center = _c.Center + self.arctrack.setCenter(self.center) + if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): + if not self.altdown: + self.altdown = True + if info: + ob = self.doc.getObject(info['Object']) + num = int(info['Component'].lstrip('Edge')) - 1 + ed = ob.Shape.Edges[num] + if len(self.tangents) == 2: + cir = DraftGeomUtils.circleFrom3tan(self.tangents[0], + self.tangents[1], + ed) + cl = DraftGeomUtils.findClosestCircle(self.point, cir) + self.center = cl.Center + self.rad = cl.Radius + self.arctrack.setCenter(self.center) + else: + self.rad = self.center.add(DraftGeomUtils.findDistance(self.center, ed).sub(self.center)).Length + else: + self.rad = DraftVecUtils.dist(self.point, self.center) + else: + if self.altdown: + self.altdown = False + self.rad = DraftVecUtils.dist(self.point, self.center) + self.ui.setRadiusValue(self.rad, "Length") + self.arctrack.setRadius(self.rad) + self.linetrack.p1(self.center) + self.linetrack.p2(self.point) + self.linetrack.on() + elif (self.step == 2): # choose first angle + currentrad = DraftVecUtils.dist(self.point, self.center) + if currentrad != 0: + angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) + else: + angle = 0 + self.linetrack.p2(DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad).add(self.center)) + self.ui.setRadiusValue(math.degrees(angle), unit="Angle") + self.firstangle = angle + else: + # choose second angle + currentrad = DraftVecUtils.dist(self.point, self.center) + if currentrad != 0: + angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) + else: + angle = 0 + self.linetrack.p2(DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad).add(self.center)) + self.updateAngle(angle) + self.ui.setRadiusValue(math.degrees(self.angle), unit="Angle") + self.arctrack.setApertureAngle(self.angle) + + gui_tool_utils.redraw3DView() + + elif arg["Type"] == "SoMouseButtonEvent": # mouse click + if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1": + if self.point: + if self.step == 0: # choose center + if not self.support: + gui_tool_utils.getSupport(arg) + (self.point, + ctrlPoint, info) = gui_tool_utils.getPoint(self, arg) + if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): + snapped = self.view.getObjectInfo((arg["Position"][0], + arg["Position"][1])) + if snapped: + ob = self.doc.getObject(snapped['Object']) + num = int(snapped['Component'].lstrip('Edge')) - 1 + ed = ob.Shape.Edges[num] + self.tangents.append(ed) + if len(self.tangents) == 2: + self.arctrack.on() + self.ui.radiusUi() + self.step = 1 + self.ui.setNextFocus() + self.linetrack.on() + _msg(translate("draft", "Pick radius")) + else: + if len(self.tangents) == 1: + self.tanpoints.append(self.point) + else: + self.center = self.point + self.node = [self.point] + self.arctrack.setCenter(self.center) + self.linetrack.p1(self.center) + self.linetrack.p2(self.view.getPoint(arg["Position"][0], + arg["Position"][1])) + self.arctrack.on() + self.ui.radiusUi() + self.step = 1 + self.ui.setNextFocus() + self.linetrack.on() + _msg(translate("draft", "Pick radius")) + if self.planetrack: + self.planetrack.set(self.point) + elif self.step == 1: # choose radius + if self.closedCircle: + self.drawArc() + else: + self.ui.labelRadius.setText(translate("draft", "Start angle")) + self.ui.radiusValue.setToolTip(translate("draft", "Start angle")) + self.ui.radiusValue.setText(U.Quantity(0, U.Angle).UserString) + self.linetrack.p1(self.center) + self.linetrack.on() + self.step = 2 + _msg(translate("draft", "Pick start angle")) + elif self.step == 2: # choose first angle + self.ui.labelRadius.setText(translate("draft", "Aperture angle")) + self.ui.radiusValue.setToolTip(translate("draft", "Aperture angle")) + self.step = 3 + # scale center->point vector for proper display + # u = DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad) obsolete? + self.arctrack.setStartAngle(self.firstangle) + _msg(translate("draft", "Pick aperture")) + else: # choose second angle + self.step = 4 + self.drawArc() + + def drawArc(self): + """Actually draw the arc object.""" + rot, sup, pts, fil = self.getStrings() + if self.closedCircle: + try: + # The command to run is built as a series of text strings + # to be commited through the `draftutils.todo.ToDo` class. + if utils.getParam("UsePartPrimitives", False): + # Insert a Part::Primitive object + _base = DraftVecUtils.toString(self.center) + _cmd = 'FreeCAD.ActiveDocument.' + _cmd += 'addObject("Part::Circle", "Circle")' + _cmd_list = ['circle = ' + _cmd, + 'circle.Radius = ' + str(self.rad), + 'pl = FreeCAD.Placement()', + 'pl.Rotation.Q = ' + rot, + 'pl.Base = ' + _base, + 'circle.Placement = pl', + 'Draft.autogroup(circle)', + 'FreeCAD.ActiveDocument.recompute()'] + self.commit(translate("draft", "Create Circle (Part)"), + _cmd_list) + else: + # Insert a Draft circle + Gui.addModule("Draft") + _base = DraftVecUtils.toString(self.center) + _cmd = 'Draft.makeCircle' + _cmd += '(' + _cmd += 'radius=' + str(self.rad) + ', ' + _cmd += 'placement=pl, ' + _cmd += 'face=' + fil + ', ' + _cmd += 'support=' + sup + _cmd += ')' + _cmd_list = ['pl=FreeCAD.Placement()', + 'pl.Rotation.Q=' + rot, + 'pl.Base=' + _base, + 'circle = ' + _cmd, + 'Draft.autogroup(circle)', + 'FreeCAD.ActiveDocument.recompute()'] + self.commit(translate("draft", "Create Circle"), + _cmd_list) + except Exception: + _err("Draft: error delaying commit") + else: + # Not a closed circle, therfore a circular arc + sta = math.degrees(self.firstangle) + end = math.degrees(self.firstangle + self.angle) + if end < sta: + sta, end = end, sta + while True: + if sta > 360: + sta = sta - 360 + elif end > 360: + end = end - 360 + else: + break + try: + Gui.addModule("Draft") + if utils.getParam("UsePartPrimitives", False): + # Insert a Part::Primitive object + _base = DraftVecUtils.toString(self.center) + _cmd = 'FreeCAD.ActiveDocument.' + _cmd += 'addObject("Part::Circle", "Circle")' + _cmd_list = ['circle = ' + _cmd, + 'circle.Radius = ' + str(self.rad), + 'circle.Angle0 = ' + str(sta), + 'circle.Angle1 = ' + str(end), + 'pl = FreeCAD.Placement()', + 'pl.Rotation.Q = ' + rot, + 'pl.Base = ' + _base, + 'circle.Placement = pl', + 'Draft.autogroup(circle)', + 'FreeCAD.ActiveDocument.recompute()'] + self.commit(translate("draft", "Create Arc (Part)"), + _cmd_list) + else: + # Insert a Draft circle + _base = DraftVecUtils.toString(self.center) + _cmd = 'Draft.makeCircle' + _cmd += '(' + _cmd += 'radius=' + str(self.rad) + ', ' + _cmd += 'placement=pl, ' + _cmd += 'face=' + fil + ', ' + _cmd += 'startangle=' + str(sta) + ', ' + _cmd += 'endangle=' + str(end) + ', ' + _cmd += 'support=' + sup + _cmd += ')' + _cmd_list = ['pl = FreeCAD.Placement()', + 'pl.Rotation.Q = ' + rot, + 'pl.Base = ' + _base, + 'circle = ' + _cmd, + 'Draft.autogroup(circle)', + 'FreeCAD.ActiveDocument.recompute()'] + self.commit(translate("draft", "Create Arc"), + _cmd_list) + except Exception: + _err("Draft: error delaying commit") + + # Finalize full circle or cirular arc + self.finish(cont=True) + + def numericInput(self, numx, numy, numz): + """Validate the entry fields in the user interface. + + This function is called by the toolbar or taskpanel interface + when valid x, y, and z have been entered in the input fields. + """ + self.center = App.Vector(numx, numy, numz) + self.node = [self.center] + self.arctrack.setCenter(self.center) + self.arctrack.on() + self.ui.radiusUi() + self.step = 1 + self.ui.setNextFocus() + _msg(translate("draft", "Pick radius")) + + def numericRadius(self, rad): + """Validate the entry radius in the user interface. + + This function is called by the toolbar or taskpanel interface + when a valid radius has been entered in the input field. + """ + import DraftGeomUtils + plane = App.DraftWorkingPlane + + if self.step == 1: + self.rad = rad + if len(self.tangents) == 2: + cir = DraftGeomUtils.circleFrom2tan1rad(self.tangents[0], + self.tangents[1], + rad) + if self.center: + _c = DraftGeomUtils.findClosestCircle(self.center, cir) + self.center = _c.Center + else: + self.center = cir[-1].Center + elif self.tangents and self.tanpoints: + cir = DraftGeomUtils.circleFrom1tan1pt1rad(self.tangents[0], + self.tanpoints[0], + rad) + if self.center: + _c = DraftGeomUtils.findClosestCircle(self.center, cir) + self.center = _c.Center + else: + self.center = cir[-1].Center + if self.closedCircle: + self.drawArc() + else: + self.step = 2 + self.arctrack.setCenter(self.center) + self.ui.labelRadius.setText(translate("draft", "Start angle")) + self.ui.radiusValue.setToolTip(translate("draft", "Start angle")) + self.linetrack.p1(self.center) + self.linetrack.on() + self.ui.radiusValue.setText("") + self.ui.radiusValue.setFocus() + _msg(translate("draft", "Pick start angle")) + elif self.step == 2: + self.ui.labelRadius.setText(translate("draft", "Aperture angle")) + self.ui.radiusValue.setToolTip(translate("draft", "Aperture angle")) + self.firstangle = math.radians(rad) + if DraftVecUtils.equals(plane.axis, App.Vector(1, 0, 0)): + u = App.Vector(0, self.rad, 0) + else: + u = DraftVecUtils.scaleTo(App.Vector(1, 0, 0).cross(plane.axis), self.rad) + urotated = DraftVecUtils.rotate(u, math.radians(rad), plane.axis) + self.arctrack.setStartAngle(self.firstangle) + self.step = 3 + self.ui.radiusValue.setText("") + self.ui.radiusValue.setFocus() + _msg(translate("draft", "Pick aperture angle")) + else: + self.updateAngle(rad) + self.angle = math.radians(rad) + self.step = 4 + self.drawArc() + + +Gui.addCommand('Draft_Arc', Arc()) + + class Arc_3Points(gui_base.GuiCommandSimplest): """GuiCommand for the Draft_Arc_3Points tool.""" @@ -154,3 +586,32 @@ class Arc_3Points(gui_base.GuiCommandSimplest): Draft_Arc_3Points = Arc_3Points Gui.addCommand('Draft_Arc_3Points', Arc_3Points()) + + +class ArcGroup: + """Gui Command group for the Arc tools.""" + + def GetResources(self): + """Set icon, menu and tooltip.""" + _menu = "Arc tools" + _tip = ("Create various types of circular arcs.") + + return {'MenuText': QT_TRANSLATE_NOOP("Draft_ArcTools", _menu), + 'ToolTip': QT_TRANSLATE_NOOP("Draft_ArcTools", _tip)} + + def GetCommands(self): + """Return a tuple of commands in the group.""" + return ('Draft_Arc', 'Draft_Arc_3Points') + + def IsActive(self): + """Return True when this command should be available. + + It is `True` when there is a document. + """ + if Gui.ActiveDocument: + return True + else: + return False + + +Gui.addCommand('Draft_ArcTools', ArcGroup())