00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 __title__="FreeCAD Draft Workbench GUI Tools"
00025 __author__ = "Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline, Dmitry Chigrin"
00026 __url__ = "http://free-cad.sourceforge.net"
00027
00028
00029
00030
00031
00032 import os, FreeCAD, FreeCADGui, Part, WorkingPlane, math, re, importSVG, Draft, Draft_rc
00033 from functools import partial
00034 from draftlibs import fcvec,fcgeo
00035 from FreeCAD import Vector
00036 from draftGui import todo,QtCore,QtGui
00037 from pivy import coin
00038
00039
00040
00041
00042
00043
00044 FreeCADGui.updateLocale()
00045
00046 def translate(context,text):
00047 "convenience function for Qt translator"
00048 return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8).toUtf8()
00049
00050 def msg(text=None,mode=None):
00051 "prints the given message on the FreeCAD status bar"
00052 if not text: FreeCAD.Console.PrintMessage("")
00053 else:
00054 if mode == 'warning':
00055 FreeCAD.Console.PrintWarning(text)
00056 elif mode == 'error':
00057 FreeCAD.Console.PrintError(text)
00058 else:
00059 FreeCAD.Console.PrintMessage(text)
00060
00061
00062 FreeCAD.svgpatterns = importSVG.getContents(Draft_rc.qt_resource_data,'pattern',True)
00063 altpat = Draft.getParam("patternFile")
00064 if os.path.isdir(altpat):
00065 for f in os.listdir(altpat):
00066 if '.svg' in f:
00067 p = importSVG.getContents(altpat+os.sep+f,'pattern')
00068 if p: FreeCAD.svgpatterns[p[0]]=p[1]
00069
00070
00071 plane = WorkingPlane.plane()
00072 FreeCAD.DraftWorkingPlane = plane
00073 defaultWP = Draft.getParam("defaultWP")
00074 if defaultWP == 1: plane.alignToPointAndAxis(Vector(0,0,0), Vector(0,0,1), 0)
00075 elif defaultWP == 2: plane.alignToPointAndAxis(Vector(0,0,0), Vector(0,1,0), 0)
00076 elif defaultWP == 3: plane.alignToPointAndAxis(Vector(0,0,0), Vector(1,0,0), 0)
00077
00078
00079 lastObj = [0,0]
00080
00081
00082 MODS = ["shift","ctrl","alt"]
00083 MODCONSTRAIN = MODS[Draft.getParam("modconstrain")]
00084 MODSNAP = MODS[Draft.getParam("modsnap")]
00085 MODALT = MODS[Draft.getParam("modalt")]
00086
00087
00088
00089
00090
00091 def snapPoint(target,point,cursor,ctrl=False):
00092 '''
00093 Snap function used by the Draft tools
00094
00095 Currently has two modes: passive and active. Pressing CTRL while
00096 clicking puts you in active mode:
00097
00098 - In passive mode (an open circle appears), your point is
00099 snapped to the nearest point on any underlying geometry.
00100
00101 - In active mode (ctrl pressed, a filled circle appears), your point
00102 can currently be snapped to the following points:
00103 - Nodes and midpoints of all Part shapes
00104 - Nodes and midpoints of lines/wires
00105 - Centers and quadrant points of circles
00106 - Endpoints of arcs
00107 - Intersection between line, wires segments, arcs and circles
00108 - When constrained (SHIFT pressed), Intersections between
00109 constraining axis and lines/wires
00110 '''
00111
00112 def getConstrainedPoint(edge,last,constrain):
00113 "check for constrained snappoint"
00114 p1 = edge.Vertexes[0].Point
00115 p2 = edge.Vertexes[-1].Point
00116 ar = []
00117 if (constrain == 0):
00118 if ((last.y > p1.y) and (last.y < p2.y) or (last.y > p2.y) and (last.y < p1.y)):
00119 pc = (last.y-p1.y)/(p2.y-p1.y)
00120 cp = (Vector(p1.x+pc*(p2.x-p1.x),p1.y+pc*(p2.y-p1.y),p1.z+pc*(p2.z-p1.z)))
00121 ar.append([cp,1,cp])
00122 if (constrain == 1):
00123 if ((last.x > p1.x) and (last.x < p2.x) or (last.x > p2.x) and (last.x < p1.x)):
00124 pc = (last.x-p1.x)/(p2.x-p1.x)
00125 cp = (Vector(p1.x+pc*(p2.x-p1.x),p1.y+pc*(p2.y-p1.y),p1.z+pc*(p2.z-p1.z)))
00126 ar.append([cp,1,cp])
00127 return ar
00128
00129 def getPassivePoint(info):
00130 "returns a passive snap point"
00131 cur = Vector(info['x'],info['y'],info['z'])
00132 return [cur,2,cur]
00133
00134 def getScreenDist(dist,cursor):
00135 "returns a 3D distance from a screen pixels distance"
00136 p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint(cursor)
00137 p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((cursor[0]+dist,cursor[1]))
00138 return (p2.sub(p1)).Length
00139
00140 def getGridSnap(target,point):
00141 "returns a grid snap point if available"
00142 if target.grid:
00143 return target.grid.getClosestNode(point)
00144 return None
00145
00146 def getPerpendicular(edge,last):
00147 "returns a point on an edge, perpendicular to the given point"
00148 dv = last.sub(edge.Vertexes[0].Point)
00149 nv = fcvec.project(dv,fcgeo.vec(edge))
00150 np = (edge.Vertexes[0].Point).add(nv)
00151 return np
00152
00153
00154 extractrl = False
00155 if Draft.getParam("alwaysSnap"):
00156 extractrl = ctrl
00157 ctrl = True
00158
00159
00160 radius = getScreenDist(Draft.getParam("snapRange"),cursor)
00161
00162
00163 target.snap.off()
00164 target.extsnap.off()
00165 if (len(target.node) > 0):
00166 for o in [lastObj[1],lastObj[0]]:
00167 if o:
00168 ob = target.doc.getObject(o)
00169 if ob:
00170 edges = ob.Shape.Edges
00171 if len(edges)<10:
00172 for e in edges:
00173 if isinstance(e.Curve,Part.Line):
00174 last = target.node[len(target.node)-1]
00175 de = Part.Line(last,last.add(fcgeo.vec(e))).toShape()
00176 np = getPerpendicular(e,point)
00177 if (np.sub(point)).Length < radius:
00178 target.snap.coords.point.setValue((np.x,np.y,np.z))
00179 target.snap.setMarker("circle")
00180 target.snap.on()
00181 target.extsnap.p1(e.Vertexes[0].Point)
00182 target.extsnap.p2(np)
00183 target.extsnap.on()
00184 point = np
00185 else:
00186 last = target.node[len(target.node)-1]
00187 de = Part.Line(last,last.add(fcgeo.vec(e))).toShape()
00188 np = getPerpendicular(de,point)
00189 if (np.sub(point)).Length < radius:
00190 target.snap.coords.point.setValue((np.x,np.y,np.z))
00191 target.snap.setMarker("circle")
00192 target.snap.on()
00193 point = np
00194
00195
00196 snapped=target.view.getObjectInfo((cursor[0],cursor[1]))
00197
00198 if (snapped == None):
00199
00200 gpt = getGridSnap(target,point)
00201 if gpt:
00202 if radius != 0:
00203 dv = point.sub(gpt)
00204 if dv.Length <= radius:
00205 target.snap.coords.point.setValue((gpt.x,gpt.y,gpt.z))
00206 target.snap.setMarker("point")
00207 target.snap.on()
00208 return gpt
00209 return point
00210 else:
00211
00212 obj = target.doc.getObject(snapped['Object'])
00213 if hasattr(obj.ViewObject,"Selectable"):
00214 if not obj.ViewObject.Selectable:
00215 return point
00216 if not ctrl:
00217
00218 snapArray = [getPassivePoint(snapped)]
00219 else:
00220 snapArray = []
00221 comp = snapped['Component']
00222 if obj.isDerivedFrom("Part::Feature"):
00223 if "Edge" in comp:
00224
00225 intedges = []
00226 if lastObj[0]:
00227 lo = target.doc.getObject(lastObj[0])
00228 if lo:
00229 if lo.isDerivedFrom("Part::Feature"):
00230 intedges = lo.Shape.Edges
00231
00232 nr = int(comp[4:])-1
00233 edge = obj.Shape.Edges[nr]
00234 for v in edge.Vertexes:
00235 snapArray.append([v.Point,0,v.Point])
00236 if isinstance(edge.Curve,Part.Line):
00237
00238 midpoint = fcgeo.findMidpoint(edge)
00239 snapArray.append([midpoint,1,midpoint])
00240 if (len(target.node) > 0):
00241 last = target.node[len(target.node)-1]
00242 snapArray.extend(getConstrainedPoint(edge,last,target.constrain))
00243 np = getPerpendicular(edge,last)
00244 snapArray.append([np,1,np])
00245
00246 elif isinstance (edge.Curve,Part.Circle):
00247
00248 rad = edge.Curve.Radius
00249 pos = edge.Curve.Center
00250 for i in [0,30,45,60,90,120,135,150,180,210,225,240,270,300,315,330]:
00251 ang = math.radians(i)
00252 cur = Vector(math.sin(ang)*rad+pos.x,math.cos(ang)*rad+pos.y,pos.z)
00253 snapArray.append([cur,1,cur])
00254 for i in [15,37.5,52.5,75,105,127.5,142.5,165,195,217.5,232.5,255,285,307.5,322.5,345]:
00255 ang = math.radians(i)
00256 cur = Vector(math.sin(ang)*rad+pos.x,math.cos(ang)*rad+pos.y,pos.z)
00257 snapArray.append([cur,0,pos])
00258
00259 for e in intedges:
00260
00261 pt = fcgeo.findIntersection(e,edge)
00262 if pt:
00263 for p in pt:
00264 snapArray.append([p,3,p])
00265 elif "Vertex" in comp:
00266
00267 p = Vector(snapped['x'],snapped['y'],snapped['z'])
00268 snapArray.append([p,0,p])
00269 elif comp == '':
00270
00271 p = Vector(snapped['x'],snapped['y'],snapped['z'])
00272 snapArray.append([p,2,p])
00273 else:
00274 snapArray = [getPassivePoint(snapped)]
00275 elif Draft.getType(obj) == "Dimension":
00276 for pt in [obj.Start,obj.End,obj.Dimline]:
00277 snapArray.append([pt,0,pt])
00278 elif Draft.getType(obj) == "Mesh":
00279 for v in obj.Mesh.Points:
00280 snapArray.append([v.Vector,0,v.Vector])
00281 if not lastObj[0]:
00282 lastObj[0] = obj.Name
00283 lastObj[1] = obj.Name
00284 if (lastObj[1] != obj.Name):
00285 lastObj[0] = lastObj[1]
00286 lastObj[1] = obj.Name
00287
00288
00289 shortest = 1000000000000000000
00290 spt = Vector(snapped['x'],snapped['y'],snapped['z'])
00291 newpoint = [Vector(0,0,0),0,Vector(0,0,0)]
00292 for pt in snapArray:
00293 if pt[0] == None: print "snapPoint: debug 'i[0]' is 'None'"
00294 di = pt[0].sub(spt)
00295 if di.Length < shortest:
00296 shortest = di.Length
00297 newpoint = pt
00298 if radius != 0:
00299 dv = point.sub(newpoint[2])
00300 if (not extractrl) and (dv.Length > radius):
00301 newpoint = getPassivePoint(snapped)
00302 target.snap.coords.point.setValue((newpoint[2].x,newpoint[2].y,newpoint[2].z))
00303 if (newpoint[1] == 1):
00304 target.snap.setMarker("square")
00305 elif (newpoint[1] == 0):
00306 target.snap.setMarker("point")
00307 elif (newpoint[1] == 3):
00308 target.snap.setMarker("square")
00309 else:
00310 target.snap.setMarker("circle")
00311 target.snap.on()
00312 return newpoint[2]
00313
00314 def constrainPoint (target,pt,mobile=False,sym=False):
00315 '''
00316 Constrain function used by the Draft tools
00317 On commands that need to enter several points (currently only line/wire),
00318 you can constrain the next point to be picked to the last drawn point by
00319 pressing SHIFT. The vertical or horizontal constraining depends on the
00320 position of your mouse in relation to last point at the moment you press
00321 SHIFT. if mobile=True, mobile behaviour applies. If sym=True, x alway = y
00322 '''
00323 point = Vector(pt)
00324 if len(target.node) > 0:
00325 last = target.node[-1]
00326 dvec = point.sub(last)
00327 affinity = plane.getClosestAxis(dvec)
00328 if ((target.constrain == None) or mobile):
00329 if affinity == "x":
00330 dv = fcvec.project(dvec,plane.u)
00331 point = last.add(dv)
00332 if sym:
00333 l = dv.Length
00334 if dv.getAngle(plane.u) > 1:
00335 l = -l
00336 point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
00337 target.constrain = 0
00338 target.ui.xValue.setEnabled(True)
00339 target.ui.yValue.setEnabled(False)
00340 target.ui.zValue.setEnabled(False)
00341 target.ui.xValue.setFocus()
00342 elif affinity == "y":
00343 dv = fcvec.project(dvec,plane.v)
00344 point = last.add(dv)
00345 if sym:
00346 l = dv.Length
00347 if dv.getAngle(plane.v) > 1:
00348 l = -l
00349 point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
00350 target.constrain = 1
00351 target.ui.xValue.setEnabled(False)
00352 target.ui.yValue.setEnabled(True)
00353 target.ui.zValue.setEnabled(False)
00354 target.ui.yValue.setFocus()
00355 elif affinity == "z":
00356 dv = fcvec.project(dvec,plane.axis)
00357 point = last.add(dv)
00358 if sym:
00359 l = dv.Length
00360 if dv.getAngle(plane.axis) > 1:
00361 l = -l
00362 point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
00363 target.constrain = 2
00364 target.ui.xValue.setEnabled(False)
00365 target.ui.yValue.setEnabled(False)
00366 target.ui.zValue.setEnabled(True)
00367 target.ui.zValue.setFocus()
00368 else: target.constrain = 3
00369 elif (target.constrain == 0):
00370 dv = fcvec.project(dvec,plane.u)
00371 point = last.add(dv)
00372 if sym:
00373 l = dv.Length
00374 if dv.getAngle(plane.u) > 1:
00375 l = -l
00376 point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
00377 elif (target.constrain == 1):
00378 dv = fcvec.project(dvec,plane.v)
00379 point = last.add(dv)
00380 if sym:
00381 l = dv.Length
00382 if dv.getAngle(plane.u) > 1:
00383 l = -l
00384 point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
00385 elif (target.constrain == 2):
00386 dv = fcvec.project(dvec,plane.axis)
00387 point = last.add(dv)
00388 if sym:
00389 l = dv.Length
00390 if dv.getAngle(plane.u) > 1:
00391 l = -l
00392 point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
00393 return point
00394
00395 def selectObject(arg):
00396 '''this is a scene even handler, to be called from the Draft tools
00397 when they need to select an object'''
00398 if (arg["Type"] == "SoKeyboardEvent"):
00399 if (arg["Key"] == "ESCAPE"):
00400 FreeCAD.activeDraftCommand.finish()
00401
00402 if (arg["Type"] == "SoMouseButtonEvent"):
00403 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
00404 cursor = arg["Position"]
00405 snapped = FreeCADGui.ActiveDocument.ActiveView.getObjectInfo((cursor[0],cursor[1]))
00406 if snapped:
00407 obj = FreeCAD.ActiveDocument.getObject(snapped['Object'])
00408 FreeCADGui.Selection.addSelection(obj)
00409 FreeCAD.activeDraftCommand.component=snapped['Component']
00410 FreeCAD.activeDraftCommand.proceed()
00411
00412 def getPoint(target,args,mobile=False,sym=False,workingplane=True):
00413 '''
00414 Function used by the Draft Tools.
00415 returns a constrained 3d point and its original point.
00416 if mobile=True, the constraining occurs from the location of
00417 mouse cursor when Shift is pressed, otherwise from last entered
00418 point. If sym=True, x and y values stay always equal. If workingplane=False,
00419 the point wont be projected on the Working Plane.
00420 '''
00421 ui = FreeCADGui.draftToolBar
00422 view = FreeCADGui.ActiveDocument.ActiveView
00423 point = view.getPoint(args["Position"][0],args["Position"][1])
00424 point = snapPoint(target,point,args["Position"],hasMod(args,MODSNAP))
00425
00426 if (not plane.weak) and workingplane:
00427
00428 viewDirection = view.getViewDirection()
00429 if FreeCADGui.ActiveDocument.ActiveView.getCameraType() == "Perspective":
00430 camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode()
00431 p = camera.getField("position").getValue()
00432
00433 viewDirection = point.sub(Vector(p[0],p[1],p[2]))
00434
00435
00436 if view.getObjectInfo((args["Position"][0],args["Position"][1])):
00437 pass
00438
00439 else:
00440 point = plane.projectPoint(point, viewDirection)
00441 ctrlPoint = Vector(point.x,point.y,point.z)
00442 if (hasMod(args,MODCONSTRAIN)):
00443 if mobile and (target.constrain == None):
00444 target.node.append(point)
00445 point = constrainPoint(target,point,mobile=mobile,sym=sym)
00446 else:
00447 target.constrain = None
00448 ui.xValue.setEnabled(True)
00449 ui.yValue.setEnabled(True)
00450 ui.zValue.setEnabled(True)
00451 if target.node:
00452 if target.featureName == "Rectangle":
00453 ui.displayPoint(point, target.node[0], plane=plane)
00454 else:
00455 ui.displayPoint(point, target.node[-1], plane=plane)
00456 else: ui.displayPoint(point, plane=plane)
00457 return point,ctrlPoint
00458
00459 def getSupport(args):
00460 "returns the supporting object and sets the working plane"
00461 snapped = FreeCADGui.ActiveDocument.ActiveView.getObjectInfo((args["Position"][0],args["Position"][1]))
00462 if not snapped: return None
00463 obj = None
00464 plane.save()
00465 try:
00466 obj = FreeCAD.ActiveDocument.getObject(snapped['Object'])
00467 shape = obj.Shape
00468 component = getattr(shape,snapped["Component"])
00469 if plane.alignToFace(component, 0) \
00470 or plane.alignToCurve(component, 0):
00471 self.display(plane.axis)
00472 except:
00473 pass
00474 return obj
00475
00476 def hasMod(args,mod):
00477 "checks if args has a specific modifier"
00478 if mod == "shift":
00479 return args["ShiftDown"]
00480 elif mod == "ctrl":
00481 return args["CtrlDown"]
00482 elif mod == "alt":
00483 return args["AltDown"]
00484
00485 def setMod(args,mod,state):
00486 "sets a specific modifier state in args"
00487 if mod == "shift":
00488 args["ShiftDown"] = state
00489 elif mod == "ctrl":
00490 args["CtrlDown"] = state
00491 elif mod == "alt":
00492 args["AltDown"] = state
00493
00494
00495
00496
00497
00498 class Tracker:
00499 "A generic Draft Tracker, to be used by other specific trackers"
00500 def __init__(self,dotted=False,scolor=None,swidth=None,children=[],ontop=False):
00501 self.ontop = ontop
00502 color = coin.SoBaseColor()
00503 color.rgb = scolor or FreeCADGui.draftToolBar.getDefaultColor("ui")
00504 drawstyle = coin.SoDrawStyle()
00505 if swidth:
00506 drawstyle.lineWidth = swidth
00507 if dotted:
00508 drawstyle.style = coin.SoDrawStyle.LINES
00509 drawstyle.lineWeight = 3
00510 drawstyle.linePattern = 0x0f0f
00511 node = coin.SoSeparator()
00512 for c in [drawstyle, color] + children:
00513 node.addChild(c)
00514 self.switch = coin.SoSwitch()
00515 self.switch.addChild(node)
00516 self.switch.whichChild = -1
00517 self.Visible = False
00518 todo.delay(self._insertSwitch, self.switch)
00519
00520 def finalize(self):
00521 todo.delay(self._removeSwitch, self.switch)
00522 self.switch = None
00523
00524 def _insertSwitch(self, switch):
00525 '''insert self.switch into the scene graph. Must not be called
00526 from an event handler (or other scene graph traversal).'''
00527 sg=FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
00528 if self.ontop:
00529 sg.insertChild(switch,0)
00530 else:
00531 sg.addChild(switch)
00532
00533 def _removeSwitch(self, switch):
00534 '''remove self.switch from the scene graph. As with _insertSwitch,
00535 must not be called during scene graph traversal).'''
00536 sg=FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
00537 sg.removeChild(switch)
00538
00539 def on(self):
00540 self.switch.whichChild = 0
00541 self.Visible = True
00542
00543 def off(self):
00544 self.switch.whichChild = -1
00545 self.Visible = False
00546
00547 class snapTracker(Tracker):
00548 "A Snap Mark tracker, used by tools that support snapping"
00549 def __init__(self):
00550 color = coin.SoBaseColor()
00551 color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
00552 self.marker = coin.SoMarkerSet()
00553 self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
00554 self.coords = coin.SoCoordinate3()
00555 self.coords.point.setValue((0,0,0))
00556 node = coin.SoAnnotation()
00557 node.addChild(self.coords)
00558 node.addChild(color)
00559 node.addChild(self.marker)
00560 Tracker.__init__(self,children=[node])
00561
00562 def setMarker(self,style):
00563 if (style == "point"):
00564 self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
00565 elif (style == "square"):
00566 self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
00567 elif (style == "circle"):
00568 self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_LINE_9_9
00569
00570 class lineTracker(Tracker):
00571 "A Line tracker, used by the tools that need to draw temporary lines"
00572 def __init__(self,dotted=False,scolor=None,swidth=None):
00573 line = coin.SoLineSet()
00574 line.numVertices.setValue(2)
00575 self.coords = coin.SoCoordinate3()
00576 self.coords.point.setValues(0,2,[[0,0,0],[1,0,0]])
00577 Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
00578
00579 def p1(self,point=None):
00580 "sets or gets the first point of the line"
00581 if point:
00582 self.coords.point.set1Value(0,point.x,point.y,point.z)
00583 else:
00584 return Vector(self.coords.point.getValues()[0].getValue())
00585
00586 def p2(self,point=None):
00587 "sets or gets the second point of the line"
00588 if point:
00589 self.coords.point.set1Value(1,point.x,point.y,point.z)
00590 else:
00591 return Vector(self.coords.point.getValues()[-1].getValue())
00592
00593 def getLength(self):
00594 "returns the length of the line"
00595 p1 = Vector(self.coords.point.getValues()[0].getValue())
00596 p2 = Vector(self.coords.point.getValues()[-1].getValue())
00597 return (p2.sub(p1)).Length
00598
00599 class rectangleTracker(Tracker):
00600 "A Rectangle tracker, used by the rectangle tool"
00601 def __init__(self,dotted=False,scolor=None,swidth=None):
00602 self.origin = Vector(0,0,0)
00603 line = coin.SoLineSet()
00604 line.numVertices.setValue(5)
00605 self.coords = coin.SoCoordinate3()
00606 self.coords.point.setValues(0,50,[[0,0,0],[2,0,0],[2,2,0],[0,2,0],[0,0,0]])
00607 Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
00608 self.u = plane.u
00609 self.v = plane.v
00610
00611 def setorigin(self,point):
00612 "sets the base point of the rectangle"
00613 self.coords.point.set1Value(0,point.x,point.y,point.z)
00614 self.coords.point.set1Value(4,point.x,point.y,point.z)
00615 self.origin = point
00616
00617 def update(self,point):
00618 "sets the opposite (diagonal) point of the rectangle"
00619 diagonal = point.sub(self.origin)
00620 inpoint1 = self.origin.add(fcvec.project(diagonal,self.v))
00621 inpoint2 = self.origin.add(fcvec.project(diagonal,self.u))
00622 self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z)
00623 self.coords.point.set1Value(2,point.x,point.y,point.z)
00624 self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z)
00625
00626 def setPlane(self,u,v=None):
00627 '''sets given (u,v) vectors as working plane. You can give only u
00628 and v will be deduced automatically given current workplane'''
00629 self.u = u
00630 if v:
00631 self.v = v
00632 else:
00633 norm = plane.u.cross(plane.v)
00634 self.v = self.u.cross(norm)
00635
00636 def p1(self,point=None):
00637 "sets or gets the base point of the rectangle"
00638 if point:
00639 self.setorigin(point)
00640 else:
00641 return Vector(self.coords.point.getValues()[0].getValue())
00642
00643 def p2(self):
00644 "gets the second point (on u axis) of the rectangle"
00645 return Vector(self.coords.point.getValues()[3].getValue())
00646
00647 def p3(self,point=None):
00648 "sets or gets the opposite (diagonal) point of the rectangle"
00649 if point:
00650 self.update(point)
00651 else:
00652 return Vector(self.coords.point.getValues()[2].getValue())
00653
00654 def p4(self):
00655 "gets the fourth point (on v axis) of the rectangle"
00656 return Vector(self.coords.point.getValues()[1].getValue())
00657
00658 def getSize(self):
00659 "returns (length,width) of the rectangle"
00660 p1 = Vector(self.coords.point.getValues()[0].getValue())
00661 p2 = Vector(self.coords.point.getValues()[2].getValue())
00662 diag = p2.sub(p1)
00663 return ((fcvec.project(diag,self.u)).Length,(fcvec.project(diag,self.v)).Length)
00664
00665 def getNormal(self):
00666 "returns the normal of the rectangle"
00667 return (self.u.cross(self.v)).normalize()
00668
00669 class dimTracker(Tracker):
00670 "A Dimension tracker, used by the dimension tool"
00671 def __init__(self,dotted=False,scolor=None,swidth=None):
00672 line = coin.SoLineSet()
00673 line.numVertices.setValue(4)
00674 self.coords = coin.SoCoordinate3()
00675 self.coords.point.setValues(0,4,[[0,0,0],[0,0,0],[0,0,0],[0,0,0]])
00676 Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
00677 self.p1 = self.p2 = self.p3 = None
00678
00679 def update(self,pts):
00680 if len(pts) == 1:
00681 self.p3 = pts[0]
00682 else:
00683 self.p1 = pts[0]
00684 self.p2 = pts[1]
00685 if len(pts) > 2:
00686 self.p3 = pts[2]
00687 self.calc()
00688
00689 def calc(self):
00690 if (self.p1 != None) and (self.p2 != None):
00691 points = [fcvec.tup(self.p1,True),fcvec.tup(self.p2,True),\
00692 fcvec.tup(self.p1,True),fcvec.tup(self.p2,True)]
00693 if self.p3 != None:
00694 p1 = self.p1
00695 p4 = self.p2
00696 if fcvec.equals(p1,p4):
00697 proj = None
00698 else:
00699 base = Part.Line(p1,p4).toShape()
00700 proj = fcgeo.findDistance(self.p3,base)
00701 if not proj:
00702 p2 = p1
00703 p3 = p4
00704 else:
00705 p2 = p1.add(fcvec.neg(proj))
00706 p3 = p4.add(fcvec.neg(proj))
00707 points = [fcvec.tup(p1),fcvec.tup(p2),fcvec.tup(p3),fcvec.tup(p4)]
00708 self.coords.point.setValues(0,4,points)
00709
00710 class bsplineTracker(Tracker):
00711 "A bspline tracker"
00712 def __init__(self,dotted=False,scolor=None,swidth=None,points = []):
00713 self.bspline = None
00714 self.points = points
00715 self.trans = coin.SoTransform()
00716 self.sep = coin.SoSeparator()
00717 self.recompute()
00718 Tracker.__init__(self,dotted,scolor,swidth,[self.trans,self.sep])
00719
00720 def update(self, points):
00721 self.points = points
00722 self.recompute()
00723
00724 def recompute(self):
00725 if (len(self.points) >= 2):
00726 if self.bspline: self.sep.removeChild(self.bspline)
00727 self.bspline = None
00728 c = Part.BSplineCurve()
00729
00730 if ( len(self.points) >= 3 ) and ( (self.points[0] - self.points[-1]).Length < Draft.tolerance() ):
00731
00732 try:
00733 c.interpolate(self.points[:-1], True)
00734 except:
00735 pass
00736 elif self.points:
00737 try:
00738 c.interpolate(self.points, False)
00739 except:
00740 pass
00741 c = c.toShape()
00742 buf=c.writeInventor(2,0.01)
00743
00744
00745
00746 ivin = coin.SoInput()
00747 ivin.setBuffer(buf)
00748 ivob = coin.SoDB.readAll(ivin)
00749
00750 if ivob and ivob.getNumChildren() > 1:
00751 self.bspline = ivob.getChild(1).getChild(0)
00752 self.bspline.removeChild(self.bspline.getChild(0))
00753 self.bspline.removeChild(self.bspline.getChild(0))
00754 self.sep.addChild(self.bspline)
00755 else:
00756 FreeCAD.Console.PrintWarning("bsplineTracker.recompute() failed to read-in Inventor string\n")
00757
00758 class arcTracker(Tracker):
00759 "An arc tracker"
00760 def __init__(self,dotted=False,scolor=None,swidth=None,start=0,end=math.pi*2):
00761 self.circle = None
00762 self.startangle = math.degrees(start)
00763 self.endangle = math.degrees(end)
00764 self.trans = coin.SoTransform()
00765 self.trans.translation.setValue([0,0,0])
00766 self.sep = coin.SoSeparator()
00767 self.recompute()
00768 Tracker.__init__(self,dotted,scolor,swidth,[self.trans, self.sep])
00769
00770 def setCenter(self,cen):
00771 "sets the center point"
00772 self.trans.translation.setValue([cen.x,cen.y,cen.z])
00773
00774 def setRadius(self,rad):
00775 "sets the radius"
00776 self.trans.scaleFactor.setValue([rad,rad,rad])
00777
00778 def getRadius(self):
00779 "returns the current radius"
00780 return self.trans.scaleFactor.getValue()[0]
00781
00782 def setStartAngle(self,ang):
00783 "sets the start angle"
00784 self.startangle = math.degrees(ang)
00785 self.recompute()
00786
00787 def setEndAngle(self,ang):
00788 "sets the end angle"
00789 self.endangle = math.degrees(ang)
00790 self.recompute()
00791
00792 def getAngle(self,pt):
00793 "returns the angle of a given vector"
00794 c = self.trans.translation.getValue()
00795 center = Vector(c[0],c[1],c[2])
00796 base = plane.u
00797 rad = pt.sub(center)
00798 return(fcvec.angle(rad,base,plane.axis))
00799
00800 def getAngles(self):
00801 "returns the start and end angles"
00802 return(self.startangle,self.endangle)
00803
00804 def setStartPoint(self,pt):
00805 "sets the start angle from a point"
00806 self.setStartAngle(-self.getAngle(pt))
00807
00808 def setEndPoint(self,pt):
00809 "sets the end angle from a point"
00810 self.setEndAngle(self.getAngle(pt))
00811
00812 def setApertureAngle(self,ang):
00813 "sets the end angle by giving the aperture angle"
00814 ap = math.degrees(ang)
00815 self.endangle = self.startangle + ap
00816 self.recompute()
00817
00818 def recompute(self):
00819 if self.circle: self.sep.removeChild(self.circle)
00820 self.circle = None
00821 if self.endangle < self.startangle:
00822 c = Part.makeCircle(1,Vector(0,0,0),plane.axis,self.endangle,self.startangle)
00823 else:
00824 c = Part.makeCircle(1,Vector(0,0,0),plane.axis,self.startangle,self.endangle)
00825 buf=c.writeInventor(2,0.01)
00826 ivin = coin.SoInput()
00827 ivin.setBuffer(buf)
00828 ivob = coin.SoDB.readAll(ivin)
00829
00830 if ivob and ivob.getNumChildren() > 1:
00831 self.circle = ivob.getChild(1).getChild(0)
00832 self.circle.removeChild(self.circle.getChild(0))
00833 self.circle.removeChild(self.circle.getChild(0))
00834 self.sep.addChild(self.circle)
00835 else:
00836 FreeCAD.Console.PrintWarning("arcTracker.recompute() failed to read-in Inventor string\n")
00837
00838 class ghostTracker(Tracker):
00839 '''A Ghost tracker, that allows to copy whole object representations.
00840 You can pass it an object or a list of objects, or a shape.'''
00841 def __init__(self,sel):
00842 self.trans = coin.SoTransform()
00843 self.trans.translation.setValue([0,0,0])
00844 self.children = [self.trans]
00845 self.ivsep = coin.SoSeparator()
00846 try:
00847 if isinstance(sel,Part.Shape):
00848 ivin = coin.SoInput()
00849 ivin.setBuffer(sel.writeInventor())
00850 ivob = coin.SoDB.readAll(ivin)
00851 self.ivsep.addChild(ivob.getChildren()[1])
00852 else:
00853 if not isinstance(sel,list):
00854 sel = [sel]
00855 for obj in sel:
00856 self.ivsep.addChild(obj.ViewObject.RootNode.copy())
00857 except:
00858 print "draft: Couldn't create ghost"
00859 self.children.append(self.ivsep)
00860 Tracker.__init__(self,children=self.children)
00861
00862 def update(self,obj):
00863 obj.ViewObject.show()
00864 self.finalize()
00865 self.ivsep = coin.SoSeparator()
00866 self.ivsep.addChild(obj.ViewObject.RootNode.copy())
00867 Tracker.__init__(self,children=[self.ivsep])
00868 self.on()
00869 obj.ViewObject.hide()
00870
00871 class editTracker(Tracker):
00872 "A node edit tracker"
00873 def __init__(self,pos=Vector(0,0,0),name="None",idx=0,objcol=None):
00874 color = coin.SoBaseColor()
00875 if objcol:
00876 color.rgb = objcol[:3]
00877 else:
00878 color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
00879 self.marker = coin.SoMarkerSet()
00880 self.marker.markerIndex = coin.SoMarkerSet.SQUARE_FILLED_9_9
00881 self.coords = coin.SoCoordinate3()
00882 self.coords.point.setValue((pos.x,pos.y,pos.z))
00883 selnode = coin.SoType.fromName("SoFCSelection").createInstance()
00884 selnode.documentName.setValue(FreeCAD.ActiveDocument.Name)
00885 selnode.objectName.setValue(name)
00886 selnode.subElementName.setValue("EditNode"+str(idx))
00887 node = coin.SoAnnotation()
00888 selnode.addChild(self.coords)
00889 selnode.addChild(color)
00890 selnode.addChild(self.marker)
00891 node.addChild(selnode)
00892 Tracker.__init__(self,children=[node],ontop=True)
00893 self.on()
00894
00895 def set(self,pos):
00896 self.coords.point.setValue((pos.x,pos.y,pos.z))
00897
00898 def get(self):
00899 p = self.coords.point.getValues()[0]
00900 return Vector(p[0],p[1],p[2])
00901
00902 def move(self,delta):
00903 self.set(self.get().add(delta))
00904
00905 class PlaneTracker(Tracker):
00906 "A working plane tracker"
00907 def __init__(self):
00908
00909 p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint((100,100))
00910 p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((110,100))
00911 bl = (p2.sub(p1)).Length * (Draft.getParam("snapRange")/2)
00912 self.trans = coin.SoTransform()
00913 self.trans.translation.setValue([0,0,0])
00914 m1 = coin.SoMaterial()
00915 m1.transparency.setValue(0.8)
00916 m1.diffuseColor.setValue([0.4,0.4,0.6])
00917 c1 = coin.SoCoordinate3()
00918 c1.point.setValues([[-bl,-bl,0],[bl,-bl,0],[bl,bl,0],[-bl,bl,0]])
00919 f = coin.SoIndexedFaceSet()
00920 f.coordIndex.setValues([0,1,2,3])
00921 m2 = coin.SoMaterial()
00922 m2.transparency.setValue(0.7)
00923 m2.diffuseColor.setValue([0.2,0.2,0.3])
00924 c2 = coin.SoCoordinate3()
00925 c2.point.setValues([[0,bl,0],[0,0,0],[bl,0,0],[-.05*bl,.95*bl,0],[0,bl,0],
00926 [.05*bl,.95*bl,0],[.95*bl,.05*bl,0],[bl,0,0],[.95*bl,-.05*bl,0]])
00927 l = coin.SoLineSet()
00928 l.numVertices.setValues([3,3,3])
00929 s = coin.SoSeparator()
00930 s.addChild(self.trans)
00931 s.addChild(m1)
00932 s.addChild(c1)
00933 s.addChild(f)
00934 s.addChild(m2)
00935 s.addChild(c2)
00936 s.addChild(l)
00937 Tracker.__init__(self,children=[s])
00938
00939 def set(self,pos=None):
00940 if pos:
00941 Q = plane.getRotation().Rotation.Q
00942 else:
00943 plm = plane.getPlacement()
00944 Q = plm.Rotation.Q
00945 pos = plm.Base
00946 self.trans.translation.setValue([pos.x,pos.y,pos.z])
00947 self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
00948 self.on()
00949
00950 class wireTracker(Tracker):
00951 "A wire tracker"
00952 def __init__(self,wire):
00953 self.line = coin.SoLineSet()
00954 self.closed = fcgeo.isReallyClosed(wire)
00955 if self.closed:
00956 self.line.numVertices.setValue(len(wire.Vertexes)+1)
00957 else:
00958 self.line.numVertices.setValue(len(wire.Vertexes))
00959 self.coords = coin.SoCoordinate3()
00960 self.update(wire)
00961 Tracker.__init__(self,children=[self.coords,self.line])
00962
00963 def update(self,wire):
00964 if wire:
00965 self.line.numVertices.setValue(len(wire.Vertexes))
00966 for i in range(len(wire.Vertexes)):
00967 p=wire.Vertexes[i].Point
00968 self.coords.point.set1Value(i,[p.x,p.y,p.z])
00969 if self.closed:
00970 t = len(wire.Vertexes)
00971 p = wire.Vertexes[0].Point
00972 self.coords.point.set1Value(t,[p.x,p.y,p.z])
00973
00974 class gridTracker(Tracker):
00975 "A grid tracker"
00976 def __init__(self):
00977
00978 self.space = Draft.getParam("gridSpacing")
00979
00980 self.mainlines = Draft.getParam("gridEvery")
00981 self.numlines = 100
00982 col = [0.2,0.2,0.3]
00983
00984 self.trans = coin.SoTransform()
00985 self.trans.translation.setValue([0,0,0])
00986
00987 bound = (self.numlines/2)*self.space
00988 pts = []
00989 mpts = []
00990 for i in range(self.numlines+1):
00991 curr = -bound + i*self.space
00992 z = 0
00993 if i/float(self.mainlines) == i/self.mainlines:
00994 mpts.extend([[-bound,curr,z],[bound,curr,z]])
00995 mpts.extend([[curr,-bound,z],[curr,bound,z]])
00996 else:
00997 pts.extend([[-bound,curr,z],[bound,curr,z]])
00998 pts.extend([[curr,-bound,z],[curr,bound,z]])
00999 idx = []
01000 midx = []
01001 for p in range(0,len(pts),2):
01002 idx.append(2)
01003 for mp in range(0,len(mpts),2):
01004 midx.append(2)
01005
01006 mat1 = coin.SoMaterial()
01007 mat1.transparency.setValue(0.7)
01008 mat1.diffuseColor.setValue(col)
01009 self.coords1 = coin.SoCoordinate3()
01010 self.coords1.point.setValues(pts)
01011 lines1 = coin.SoLineSet()
01012 lines1.numVertices.setValues(idx)
01013 mat2 = coin.SoMaterial()
01014 mat2.transparency.setValue(0.3)
01015 mat2.diffuseColor.setValue(col)
01016 self.coords2 = coin.SoCoordinate3()
01017 self.coords2.point.setValues(mpts)
01018 lines2 = coin.SoLineSet()
01019 lines2.numVertices.setValues(midx)
01020 s = coin.SoSeparator()
01021 s.addChild(self.trans)
01022 s.addChild(mat1)
01023 s.addChild(self.coords1)
01024 s.addChild(lines1)
01025 s.addChild(mat2)
01026 s.addChild(self.coords2)
01027 s.addChild(lines2)
01028 Tracker.__init__(self,children=[s])
01029 self.update()
01030
01031 def update(self):
01032 bound = (self.numlines/2)*self.space
01033 pts = []
01034 mpts = []
01035 for i in range(self.numlines+1):
01036 curr = -bound + i*self.space
01037 if i/float(self.mainlines) == i/self.mainlines:
01038 mpts.extend([[-bound,curr,0],[bound,curr,0]])
01039 mpts.extend([[curr,-bound,0],[curr,bound,0]])
01040 else:
01041 pts.extend([[-bound,curr,0],[bound,curr,0]])
01042 pts.extend([[curr,-bound,0],[curr,bound,0]])
01043 self.coords1.point.setValues(pts)
01044 self.coords2.point.setValues(mpts)
01045
01046 def setSpacing(self,space):
01047 self.space = space
01048 self.update()
01049
01050 def setMainlines(self,ml):
01051 self.mainlines = ml
01052 self.update()
01053
01054 def set(self):
01055 Q = plane.getRotation().Rotation.Q
01056 self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
01057 self.on()
01058
01059 def getClosestNode(self,point):
01060 "returns the closest node from the given point"
01061
01062 point = plane.projectPoint(point)
01063 u = fcvec.project(point,plane.u)
01064 lu = u.Length
01065 if u.getAngle(plane.u) > 1.5:
01066 lu = -lu
01067 v = fcvec.project(point,plane.v)
01068 lv = v.Length
01069 if v.getAngle(plane.v) > 1.5:
01070 lv = -lv
01071
01072
01073 pu = (round(lu/self.space,0))*self.space
01074 pv = (round(lv/self.space,0))*self.space
01075 rot = FreeCAD.Rotation()
01076 rot.Q = self.trans.rotation.getValue().getValue()
01077 return rot.multVec(Vector(pu,pv,0))
01078
01079
01080
01081
01082
01083
01084 class SelectPlane:
01085 "The Draft_SelectPlane FreeCAD command definition"
01086
01087 def GetResources(self):
01088 return {'Pixmap' : 'Draft_SelectPlane',
01089 'Accel' : "W, P",
01090 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "SelectPlane"),
01091 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "Select a working plane for geometry creation")}
01092
01093 def IsActive(self):
01094 if FreeCADGui.ActiveDocument:
01095 return True
01096 else:
01097 return False
01098
01099 def Activated(self):
01100 if FreeCAD.activeDraftCommand:
01101 FreeCAD.activeDraftCommand.finish()
01102 self.offset = 0
01103 self.ui = None
01104 self.call = None
01105 self.doc = FreeCAD.ActiveDocument
01106 if self.doc:
01107 FreeCAD.activeDraftCommand = self
01108 self.view = FreeCADGui.ActiveDocument.ActiveView
01109 self.ui = FreeCADGui.draftToolBar
01110 self.ui.selectPlaneUi()
01111 msg(translate("draft", "Pick a face to define the drawing plane\n"))
01112 self.ui.sourceCmd = self
01113 if plane.alignToSelection(self.offset):
01114 FreeCADGui.Selection.clearSelection()
01115 self.display(plane.axis)
01116 self.finish()
01117 else:
01118 self.call = self.view.addEventCallback("SoEvent", self.action)
01119
01120 def action(self, arg):
01121 if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
01122 self.finish()
01123 if arg["Type"] == "SoMouseButtonEvent":
01124 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
01125 cursor = arg["Position"]
01126 doc = FreeCADGui.ActiveDocument
01127 info = doc.ActiveView.getObjectInfo((cursor[0],cursor[1]))
01128 if info:
01129 try:
01130 shape = doc.getObject(info["Object"]).Object.Shape
01131 component = getattr(shape, info["Component"])
01132 if plane.alignToFace(component, self.offset) \
01133 or plane.alignToCurve(component, self.offset):
01134 self.display(plane.axis)
01135 self.finish()
01136 except:
01137 pass
01138
01139 def selectHandler(self, arg):
01140 try:
01141 self.offset = float(self.ui.offsetValue.text())
01142 except:
01143 self.offset = 0
01144 if arg == "XY":
01145 plane.alignToPointAndAxis(Vector(0,0,0), Vector(0,0,1), self.offset)
01146 self.display('top')
01147 self.finish()
01148 elif arg == "XZ":
01149 plane.alignToPointAndAxis(Vector(0,0,0), Vector(0,-1,0), self.offset)
01150 self.display('front')
01151 self.finish()
01152 elif arg == "YZ":
01153 plane.alignToPointAndAxis(Vector(0,0,0), Vector(1,0,0), self.offset)
01154 self.display('side')
01155 self.finish()
01156 elif arg == "currentView":
01157 viewDirection = fcvec.neg(self.view.getViewDirection())
01158 plane.alignToPointAndAxis(Vector(0,0,0), viewDirection, self.offset)
01159 self.display(viewDirection)
01160 self.finish()
01161 elif arg == "reset":
01162 plane.reset()
01163 self.display('None')
01164 self.finish()
01165
01166 def offsetHandler(self, arg):
01167 self.offset = arg
01168
01169 def display(self,arg):
01170 if self.offset:
01171 if self.offset > 0: suffix = ' + '+str(self.offset)
01172 else: suffix = ' - '+str(self.offset)
01173 else: suffix = ''
01174 if type(arg).__name__ == 'str':
01175 self.ui.wplabel.setText(arg+suffix)
01176 elif type(arg).__name__ == 'Vector':
01177 plv = 'd('+str(arg.x)+','+str(arg.y)+','+str(arg.z)+')'
01178 self.ui.wplabel.setText(plv+suffix)
01179
01180 def finish(self):
01181 if self.call:
01182 self.view.removeEventCallback("SoEvent",self.call)
01183 FreeCAD.activeDraftCommand = None
01184 if self.ui:
01185 self.ui.offUi()
01186
01187
01188
01189
01190
01191
01192 class Creator:
01193 "A generic Draft Creator Tool used by creation tools such as line or arc"
01194
01195 def __init__(self):
01196 self.commitList = []
01197
01198 def Activated(self,name="None"):
01199 if FreeCAD.activeDraftCommand:
01200 FreeCAD.activeDraftCommand.finish()
01201 self.ui = None
01202 self.call = None
01203 self.doc = None
01204 self.support = None
01205 self.commitList = []
01206 self.doc = FreeCAD.ActiveDocument
01207 self.view = FreeCADGui.ActiveDocument.ActiveView
01208 self.featureName = name
01209 if not self.doc:
01210 self.finish()
01211 else:
01212 FreeCAD.activeDraftCommand = self
01213 self.ui = FreeCADGui.draftToolBar
01214 self.ui.cross(True)
01215 self.ui.sourceCmd = self
01216 self.ui.setTitle(name)
01217 self.ui.show()
01218 rot = self.view.getCameraNode().getField("orientation").getValue()
01219 upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue())
01220 plane.setup(fcvec.neg(self.view.getViewDirection()), Vector(0,0,0), upv)
01221 self.node = []
01222 self.pos = []
01223 self.constrain = None
01224 self.obj = None
01225 self.snap = snapTracker()
01226 self.extsnap = lineTracker(dotted=True)
01227 self.planetrack = PlaneTracker()
01228 if Draft.getParam("grid"):
01229 self.grid = gridTracker()
01230 self.grid.set()
01231 else:
01232 self.grid = None
01233
01234 def IsActive(self):
01235 if FreeCADGui.ActiveDocument:
01236 return True
01237 else:
01238 return False
01239
01240 def finish(self):
01241 self.snap.finalize()
01242 self.extsnap.finalize()
01243 self.node=[]
01244 self.planetrack.finalize()
01245 if self.grid: self.grid.finalize()
01246 if self.support: plane.restore()
01247 FreeCAD.activeDraftCommand = None
01248 if self.ui:
01249 self.ui.offUi()
01250 self.ui.cross(False)
01251 self.ui.sourceCmd = None
01252 msg("")
01253 if self.call:
01254 self.view.removeEventCallback("SoEvent",self.call)
01255 self.call = None
01256 if self.commitList:
01257 todo.delayCommit(self.commitList)
01258 self.commitList = []
01259
01260 def commit(self,name,func):
01261 "stores partial actions to be committed to the FreeCAD document"
01262 self.commitList.append((name,func))
01263
01264 class Line(Creator):
01265 "The Line FreeCAD command definition"
01266
01267 def __init__(self, wiremode=False):
01268 self.isWire = wiremode
01269
01270 def GetResources(self):
01271 return {'Pixmap' : 'Draft_Line',
01272 'Accel' : "L,I",
01273 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Line", "Line"),
01274 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Line", "Creates a 2-point line. CTRL to snap, SHIFT to constrain")}
01275
01276 def Activated(self,name="Line"):
01277 Creator.Activated(self,name)
01278 if self.doc:
01279 self.obj = None
01280 self.ui.lineUi()
01281 self.linetrack = lineTracker()
01282 self.constraintrack = lineTracker(dotted=True)
01283 self.obj=self.doc.addObject("Part::Feature",self.featureName)
01284
01285 Draft.formatObject(self.obj)
01286 if not Draft.getParam("UiMode"): self.makeDumbTask()
01287 self.call = self.view.addEventCallback("SoEvent",self.action)
01288 msg(translate("draft", "Pick first point:\n"))
01289
01290 def makeDumbTask(self):
01291 "create a dumb taskdialog to prevent deleting the temp object"
01292 class TaskPanel:
01293 def __init__(self):
01294 pass
01295 def getStandardButtons(self):
01296 return 0
01297 panel = TaskPanel()
01298 FreeCADGui.Control.showDialog(panel)
01299
01300 def finish(self,closed=False,cont=False):
01301 "terminates the operation and closes the poly if asked"
01302 if not Draft.getParam("UiMode"):
01303 FreeCADGui.Control.closeDialog()
01304 if self.obj:
01305 old = self.obj.Name
01306 todo.delay(self.doc.removeObject,old)
01307 self.obj = None
01308 if (len(self.node) > 1):
01309 self.commit(translate("draft","Create Wire"),
01310 partial(Draft.makeWire,self.node,closed,
01311 face=self.ui.fillmode,support=self.support))
01312 if self.ui:
01313 self.linetrack.finalize()
01314 self.constraintrack.finalize()
01315 Creator.finish(self)
01316 if cont and self.ui:
01317 if self.ui.continueMode:
01318 self.Activated()
01319
01320 def action(self,arg):
01321 "scene event handler"
01322 if arg["Type"] == "SoKeyboardEvent":
01323 if arg["Key"] == "ESCAPE":
01324 self.finish()
01325 elif arg["Type"] == "SoLocation2Event":
01326 point,ctrlPoint = getPoint(self,arg)
01327 self.ui.cross(True)
01328 self.linetrack.p2(point)
01329
01330 if hasMod(arg,MODCONSTRAIN):
01331 self.constraintrack.p1(point)
01332 self.constraintrack.p2(ctrlPoint)
01333 self.constraintrack.on()
01334 else:
01335 self.constraintrack.off()
01336 elif arg["Type"] == "SoMouseButtonEvent":
01337 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
01338 if (arg["Position"] == self.pos):
01339 self.finish(False,cont=True)
01340 else:
01341 if not self.node: self.support = getSupport(arg)
01342 point,ctrlPoint = getPoint(self,arg)
01343 self.pos = arg["Position"]
01344 self.node.append(point)
01345 self.linetrack.p1(point)
01346 self.drawSegment(point)
01347 if (not self.isWire and len(self.node) == 2):
01348 self.finish(False,cont=True)
01349 if (len(self.node) > 2):
01350
01351
01352
01353
01354
01355 if ((point-self.node[0]).Length < Draft.tolerance()):
01356 self.undolast()
01357 self.finish(True,cont=True)
01358 msg(translate("draft", "Wire has been closed\n"))
01359
01360 def undolast(self):
01361 "undoes last line segment"
01362 if (len(self.node) > 1):
01363 self.node.pop()
01364 last = self.node[len(self.node)-1]
01365 self.linetrack.p1(last)
01366 if self.obj.Shape.Edges:
01367 edges = self.obj.Shape.Edges
01368 if len(edges) > 1:
01369 edges.pop()
01370 newshape = Part.Wire(edges)
01371 else:
01372 newshape = Part.Shape()
01373 self.obj.Shape = newshape
01374
01375 msg(translate("draft", "Last point has been removed\n"))
01376
01377 def drawSegment(self,point):
01378 "draws a new segment"
01379 if (len(self.node) == 1):
01380 self.linetrack.on()
01381 msg(translate("draft", "Pick next point:\n"))
01382 self.planetrack.set(self.node[0])
01383 elif (len(self.node) == 2):
01384 last = self.node[len(self.node)-2]
01385 newseg = Part.Line(last,point).toShape()
01386 self.obj.Shape = newseg
01387 self.obj.ViewObject.Visibility = True
01388 if self.isWire:
01389 msg(translate("draft", "Pick next point, or (F)inish or (C)lose:\n"))
01390 else:
01391 currentshape = self.obj.Shape
01392 last = self.node[len(self.node)-2]
01393 newseg = Part.Line(last,point).toShape()
01394 newshape=currentshape.fuse(newseg)
01395 self.obj.Shape = newshape
01396 msg(translate("draft", "Pick next point, or (F)inish or (C)lose:\n"))
01397
01398 def wipe(self):
01399 "removes all previous segments and starts from last point"
01400 if len(self.node) > 1:
01401 print "nullifying"
01402
01403 self.obj.ViewObject.Visibility = False
01404 self.node = [self.node[-1]]
01405 print "setting trackers"
01406 self.linetrack.p1(self.node[0])
01407 self.planetrack.set(self.node[0])
01408 msg(translate("draft", "Pick next point:\n"))
01409 print "done"
01410
01411 def numericInput(self,numx,numy,numz):
01412 "this function gets called by the toolbar when valid x, y, and z have been entered there"
01413 point = Vector(numx,numy,numz)
01414 self.node.append(point)
01415 self.linetrack.p1(point)
01416 self.drawSegment(point)
01417 if (not self.isWire and len(self.node) == 2):
01418 self.finish(False,cont=True)
01419 self.ui.setNextFocus()
01420
01421 class Wire(Line):
01422 "a FreeCAD command for creating a wire"
01423 def __init__(self):
01424 Line.__init__(self,wiremode=True)
01425 def GetResources(self):
01426 return {'Pixmap' : 'Draft_Wire',
01427 'Accel' : "W, I",
01428 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Wire", "Wire"),
01429 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Wire", "Creates a multiple-point wire. CTRL to snap, SHIFT to constrain")}
01430
01431
01432 class BSpline(Line):
01433 "a FreeCAD command for creating a b-spline"
01434
01435 def __init__(self):
01436 Line.__init__(self,wiremode=True)
01437
01438 def GetResources(self):
01439 return {'Pixmap' : 'Draft_BSpline',
01440 'Accel' : "B, S",
01441 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_BSpline", "B-Spline"),
01442 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_BSpline", "Creates a multiple-point b-spline. CTRL to snap, SHIFT to constrain")}
01443
01444 def Activated(self):
01445 Line.Activated(self,"BSpline")
01446 if self.doc:
01447 self.bsplinetrack = bsplineTracker()
01448
01449 def action(self,arg):
01450 "scene event handler"
01451 if arg["Type"] == "SoKeyboardEvent":
01452 if arg["Key"] == "ESCAPE":
01453 self.finish()
01454 elif arg["Type"] == "SoLocation2Event":
01455 point,ctrlPoint = getPoint(self,arg)
01456 self.ui.cross(True)
01457 self.bsplinetrack.update(self.node + [point])
01458
01459 if hasMod(arg,MODCONSTRAIN):
01460 self.constraintrack.p1(point)
01461 self.constraintrack.p2(ctrlPoint)
01462 self.constraintrack.on()
01463 else: self.constraintrack.off()
01464 elif arg["Type"] == "SoMouseButtonEvent":
01465 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
01466 if (arg["Position"] == self.pos):
01467 self.finish(False,cont=True)
01468 else:
01469 if not self.node: self.support = getSupport(arg)
01470 point,ctrlPoint = getPoint(self,arg)
01471 self.pos = arg["Position"]
01472 self.node.append(point)
01473 self.drawUpdate(point)
01474 if (not self.isWire and len(self.node) == 2):
01475 self.finish(False,cont=True)
01476 if (len(self.node) > 2):
01477
01478
01479
01480
01481 if ((point-self.node[0]).Length < Draft.tolerance()):
01482 self.undolast()
01483 self.finish(True,cont=True)
01484 msg(translate("draft", "Spline has been closed\n"))
01485
01486 def undolast(self):
01487 "undoes last line segment"
01488 if (len(self.node) > 1):
01489 self.node.pop()
01490 self.bsplinetrack.update(self.node)
01491 spline = Part.BSplineCurve()
01492 spline.interpolate(self.node, False)
01493 self.obj.Shape = spline.toShape()
01494 msg(translate("draft", "Last point has been removed\n"))
01495
01496 def drawUpdate(self,point):
01497 if (len(self.node) == 1):
01498 self.bsplinetrack.on()
01499 self.planetrack.set(self.node[0])
01500 msg(translate("draft", "Pick next point:\n"))
01501 else:
01502 spline = Part.BSplineCurve()
01503 spline.interpolate(self.node, False)
01504 self.obj.Shape = spline.toShape()
01505 msg(translate("draft", "Pick next point, or (F)inish or (C)lose:\n"))
01506
01507 def finish(self,closed=False,cont=False):
01508 "terminates the operation and closes the poly if asked"
01509 if not Draft.getParam("UiMode"):
01510 FreeCADGui.Control.closeDialog()
01511 if (len(self.node) > 1):
01512 old = self.obj.Name
01513 self.doc.removeObject(old)
01514 try:
01515 self.commit(translate("draft","Create BSpline"),
01516 partial(Draft.makeBSpline,self.node,closed,
01517 face=self.ui.fillmode,support=self.support))
01518 except:
01519 print "Draft: error delaying commit"
01520 if self.ui:
01521 self.bsplinetrack.finalize()
01522 self.constraintrack.finalize()
01523 Creator.finish(self)
01524 if cont and self.ui:
01525 if self.ui.continueMode:
01526 self.Activated()
01527
01528
01529 class FinishLine:
01530 "a FreeCAD command to finish any running Line drawing operation"
01531
01532 def Activated(self):
01533 if (FreeCAD.activeDraftCommand != None):
01534 if (FreeCAD.activeDraftCommand.featureName == "Line"):
01535 FreeCAD.activeDraftCommand.finish(False)
01536 def GetResources(self):
01537 return {'Pixmap' : 'Draft_Finish',
01538 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_FinishLine", "Finish line"),
01539 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_FinishLine", "Finishes a line without closing it")}
01540 def IsActive(self):
01541 if FreeCAD.activeDraftCommand:
01542 if FreeCAD.activeDraftCommand.featureName == "Line":
01543 return True
01544 return False
01545
01546
01547 class CloseLine:
01548 "a FreeCAD command to close any running Line drawing operation"
01549
01550 def Activated(self):
01551 if (FreeCAD.activeDraftCommand != None):
01552 if (FreeCAD.activeDraftCommand.featureName == "Line"):
01553 FreeCAD.activeDraftCommand.finish(True)
01554
01555 def GetResources(self):
01556 return {'Pixmap' : 'Draft_Lock',
01557 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_CloseLine", "Close Line"),
01558 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_CloseLine", "Closes the line being drawn")}
01559
01560 def IsActive(self):
01561 if FreeCAD.activeDraftCommand:
01562 if FreeCAD.activeDraftCommand.featureName == "Line":
01563 return True
01564 return False
01565
01566
01567 class UndoLine:
01568 "a FreeCAD command to undo last drawn segment of a line"
01569
01570 def Activated(self):
01571 if (FreeCAD.activeDraftCommand != None):
01572 if (FreeCAD.activeDraftCommand.featureName == "Line"):
01573 FreeCAD.activeDraftCommand.undolast()
01574
01575 def GetResources(self):
01576 return {'Pixmap' : 'Draft_Rotate',
01577 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_UndoLine", "Undo last segment"),
01578 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_UndoLine", "Undoes the last drawn segment of the line being drawn")}
01579
01580 def IsActive(self):
01581 if FreeCAD.activeDraftCommand:
01582 if FreeCAD.activeDraftCommand.featureName == "Line":
01583 return True
01584 return False
01585
01586
01587 class Rectangle(Creator):
01588 "the Draft_Rectangle FreeCAD command definition"
01589
01590 def GetResources(self):
01591 return {'Pixmap' : 'Draft_Rectangle',
01592 'Accel' : "R, E",
01593 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Rectangle", "Rectangle"),
01594 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Rectangle", "Creates a 2-point rectangle. CTRL to snap")}
01595
01596 def Activated(self):
01597 Creator.Activated(self,"Rectangle")
01598 if self.ui:
01599 self.refpoint = None
01600 self.ui.pointUi()
01601 self.ui.extUi()
01602 self.call = self.view.addEventCallback("SoEvent",self.action)
01603 self.rect = rectangleTracker()
01604 msg(translate("draft", "Pick first point:\n"))
01605
01606 def finish(self,closed=False,cont=False):
01607 "terminates the operation and closes the poly if asked"
01608 Creator.finish(self)
01609 if self.ui:
01610 self.rect.off()
01611 self.rect.finalize()
01612 if cont and self.ui:
01613 if self.ui.continueMode:
01614 self.Activated()
01615
01616 def createObject(self):
01617 "creates the final object in the current doc"
01618 p1 = self.node[0]
01619 p3 = self.node[-1]
01620 diagonal = p3.sub(p1)
01621 p2 = p1.add(fcvec.project(diagonal, plane.v))
01622 p4 = p1.add(fcvec.project(diagonal, plane.u))
01623 length = p4.sub(p1).Length
01624 if abs(fcvec.angle(p4.sub(p1),plane.u,plane.axis)) > 1: length = -length
01625 height = p2.sub(p1).Length
01626 if abs(fcvec.angle(p2.sub(p1),plane.v,plane.axis)) > 1: height = -height
01627 p = plane.getRotation()
01628 p.move(p1)
01629 try:
01630 self.commit(translate("draft","Create Rectangle"),
01631 partial(Draft.makeRectangle,length,height,
01632 p,self.ui.fillmode,support=self.support))
01633 except:
01634 print "Draft: error delaying commit"
01635 self.finish(cont=True)
01636
01637 def action(self,arg):
01638 "scene event handler"
01639 if arg["Type"] == "SoKeyboardEvent":
01640 if arg["Key"] == "ESCAPE":
01641 self.finish()
01642 elif arg["Type"] == "SoLocation2Event":
01643 point,ctrlPoint = getPoint(self,arg,mobile=True)
01644 self.rect.update(point)
01645 self.ui.cross(True)
01646 elif arg["Type"] == "SoMouseButtonEvent":
01647 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
01648 if (arg["Position"] == self.pos):
01649 self.finish()
01650 else:
01651 if not self.node: self.support = getSupport(arg)
01652 point,ctrlPoint = getPoint(self,arg)
01653 self.appendPoint(point)
01654
01655 def numericInput(self,numx,numy,numz):
01656 "this function gets called by the toolbar when valid x, y, and z have been entered there"
01657 point = Vector(numx,numy,numz)
01658 self.appendPoint(point)
01659
01660 def appendPoint(self,point):
01661 self.node.append(point)
01662 if (len(self.node) > 1):
01663 self.rect.update(point)
01664 self.createObject()
01665 else:
01666 msg(translate("draft", "Pick opposite point:\n"))
01667 self.ui.setRelative()
01668 self.rect.setorigin(point)
01669 self.rect.on()
01670 self.planetrack.set(point)
01671
01672
01673 class Arc(Creator):
01674 "the Draft_Arc FreeCAD command definition"
01675
01676 def __init__(self):
01677 self.closedCircle=False
01678 self.featureName = "Arc"
01679
01680 def GetResources(self):
01681 return {'Pixmap' : 'Draft_Arc',
01682 'Accel' : "A, R",
01683 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Arc", "Arc"),
01684 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Arc", "Creates an arc. CTRL to snap, SHIFT to constrain")}
01685
01686 def Activated(self):
01687 Creator.Activated(self,self.featureName)
01688 if self.ui:
01689 self.step = 0
01690 self.center = None
01691 self.rad = None
01692 self.angle = 0
01693 self.tangents = []
01694 self.tanpoints = []
01695 if self.featureName == "Arc": self.ui.arcUi()
01696 else: self.ui.circleUi()
01697 self.altdown = False
01698 self.ui.sourceCmd = self
01699 self.linetrack = lineTracker(dotted=True)
01700 self.constraintrack = lineTracker(dotted=True)
01701 self.arctrack = arcTracker()
01702 self.call = self.view.addEventCallback("SoEvent",self.action)
01703 msg(translate("draft", "Pick center point:\n"))
01704
01705 def finish(self,closed=False,cont=False):
01706 "finishes the arc"
01707 Creator.finish(self)
01708 if self.ui:
01709 self.linetrack.finalize()
01710 self.constraintrack.finalize()
01711 self.arctrack.finalize()
01712 self.doc.recompute()
01713 if cont and self.ui:
01714 if self.ui.continueMode:
01715 self.Activated()
01716
01717 def updateAngle(self, angle):
01718
01719 lastangle = self.firstangle + self.angle
01720 if lastangle <= -2*math.pi: lastangle += 2*math.pi
01721 if lastangle >= 2*math.pi: lastangle -= 2*math.pi
01722
01723 d0 = angle-lastangle
01724 d1 = d0 + 2*math.pi
01725 d2 = d0 - 2*math.pi
01726 if abs(d0) < min(abs(d1), abs(d2)):
01727 delta = d0
01728 elif abs(d1) < abs(d2):
01729 delta = d1
01730 else:
01731 delta = d2
01732 newangle = self.angle + delta
01733
01734 if newangle >= 2*math.pi: newangle -= 2*math.pi
01735 if newangle <= -2*math.pi: newangle += 2*math.pi
01736 self.angle = newangle
01737
01738 def action(self,arg):
01739 "scene event handler"
01740 if arg["Type"] == "SoKeyboardEvent":
01741 if arg["Key"] == "ESCAPE":
01742 self.finish()
01743 elif arg["Type"] == "SoLocation2Event":
01744 point,ctrlPoint = getPoint(self,arg)
01745
01746 self.ui.cross(True)
01747 if self.center and fcvec.dist(point,self.center) > 0:
01748 viewdelta = fcvec.project(point.sub(self.center), plane.axis)
01749 if not fcvec.isNull(viewdelta):
01750 point = point.add(fcvec.neg(viewdelta))
01751 if (self.step == 0):
01752 if hasMod(arg,MODALT):
01753 if not self.altdown:
01754 self.ui.cross(False)
01755 self.altdown = True
01756 self.ui.switchUi(True)
01757 else:
01758 if self.altdown:
01759 self.ui.cross(True)
01760 self.altdown = False
01761 self.ui.switchUi(False)
01762 elif (self.step == 1):
01763 if len(self.tangents) == 2:
01764 cir = fcgeo.circleFrom2tan1pt(self.tangents[0], self.tangents[1], point)
01765 self.center = fcgeo.findClosestCircle(point,cir).Center
01766 self.arctrack.setCenter(self.center)
01767 elif self.tangents and self.tanpoints:
01768 cir = fcgeo.circleFrom1tan2pt(self.tangents[0], self.tanpoints[0], point)
01769 self.center = fcgeo.findClosestCircle(point,cir).Center
01770 self.arctrack.setCenter(self.center)
01771 if hasMod(arg,MODALT):
01772 if not self.altdown:
01773 self.ui.cross(False)
01774 self.altdown = True
01775 snapped = self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
01776 if snapped:
01777 ob = self.doc.getObject(snapped['Object'])
01778 num = int(snapped['Component'].lstrip('Edge'))-1
01779 ed = ob.Shape.Edges[num]
01780 if len(self.tangents) == 2:
01781 cir = fcgeo.circleFrom3tan(self.tangents[0], self.tangents[1], ed)
01782 cl = fcgeo.findClosestCircle(point,cir)
01783 self.center = cl.Center
01784 self.rad = cl.Radius
01785 self.arctrack.setCenter(self.center)
01786 else:
01787 self.rad = self.center.add(fcgeo.findDistance(self.center,ed).sub(self.center)).Length
01788 else:
01789 self.rad = fcvec.dist(point,self.center)
01790 else:
01791 if self.altdown:
01792 self.ui.cross(True)
01793 self.altdown = False
01794 self.rad = fcvec.dist(point,self.center)
01795 self.ui.setRadiusValue(self.rad)
01796 self.arctrack.setRadius(self.rad)
01797
01798 if hasMod(arg,MODCONSTRAIN):
01799 self.constraintrack.p1(point)
01800 self.constraintrack.p2(ctrlPoint)
01801 self.constraintrack.on()
01802 else:
01803 self.constraintrack.off()
01804 self.linetrack.p1(self.center)
01805 self.linetrack.p2(point)
01806 self.linetrack.on()
01807 elif (self.step == 2):
01808 currentrad = fcvec.dist(point,self.center)
01809 if currentrad != 0:
01810 angle = fcvec.angle(plane.u, point.sub(self.center), plane.axis)
01811 else: angle = 0
01812 self.linetrack.p2(fcvec.scaleTo(point.sub(self.center),self.rad).add(self.center))
01813
01814 if hasMod(arg,MODCONSTRAIN):
01815 self.constraintrack.p1(point)
01816 self.constraintrack.p2(ctrlPoint)
01817 self.constraintrack.on()
01818 else:
01819 self.constraintrack.off()
01820 self.ui.setRadiusValue(math.degrees(angle))
01821 self.firstangle = angle
01822 else:
01823 currentrad = fcvec.dist(point,self.center)
01824 if currentrad != 0:
01825 angle = fcvec.angle(plane.u, point.sub(self.center), plane.axis)
01826 else: angle = 0
01827 self.linetrack.p2(fcvec.scaleTo(point.sub(self.center),self.rad).add(self.center))
01828
01829 if hasMod(arg,MODCONSTRAIN):
01830 self.constraintrack.p1(point)
01831 self.constraintrack.p2(ctrlPoint)
01832 self.constraintrack.on()
01833 else:
01834 self.constraintrack.off()
01835 self.ui.setRadiusValue(math.degrees(angle))
01836 self.updateAngle(angle)
01837 self.arctrack.setApertureAngle(self.angle)
01838
01839 elif arg["Type"] == "SoMouseButtonEvent":
01840 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
01841 point,ctrlPoint = getPoint(self,arg)
01842
01843 if self.center and fcvec.dist(point,self.center) > 0:
01844 viewdelta = fcvec.project(point.sub(self.center), plane.axis)
01845 if not fcvec.isNull(viewdelta):
01846 point = point.add(fcvec.neg(viewdelta))
01847 if (self.step == 0):
01848 self.support = getSupport(arg)
01849 if hasMod(arg,MODALT):
01850 snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
01851 if snapped:
01852 ob = self.doc.getObject(snapped['Object'])
01853 num = int(snapped['Component'].lstrip('Edge'))-1
01854 ed = ob.Shape.Edges[num]
01855 self.tangents.append(ed)
01856 if len(self.tangents) == 2:
01857 self.arctrack.on()
01858 self.ui.radiusUi()
01859 self.step = 1
01860 self.linetrack.on()
01861 msg(translate("draft", "Pick radius:\n"))
01862 else:
01863 if len(self.tangents) == 1:
01864 self.tanpoints.append(point)
01865 else:
01866 self.center = point
01867 self.node = [point]
01868 self.arctrack.setCenter(self.center)
01869 self.linetrack.p1(self.center)
01870 self.linetrack.p2(self.view.getPoint(arg["Position"][0],arg["Position"][1]))
01871 self.arctrack.on()
01872 self.ui.radiusUi()
01873 self.step = 1
01874 self.linetrack.on()
01875 msg(translate("draft", "Pick radius:\n"))
01876 self.planetrack.set(point)
01877 elif (self.step == 1):
01878 if self.closedCircle:
01879 self.ui.cross(False)
01880 self.drawArc()
01881 else:
01882 self.ui.labelRadius.setText("Start angle")
01883 self.linetrack.p1(self.center)
01884 self.linetrack.on()
01885 self.step = 2
01886 msg(translate("draft", "Pick start angle:\n"))
01887 elif (self.step == 2):
01888 self.ui.labelRadius.setText("Aperture")
01889 self.step = 3
01890
01891 u = fcvec.scaleTo(point.sub(self.center), self.rad)
01892 self.arctrack.setStartAngle(self.firstangle)
01893 msg(translate("draft", "Pick aperture:\n"))
01894 else:
01895 self.step = 4
01896 self.drawArc()
01897
01898 def drawArc(self):
01899 "actually draws the FreeCAD object"
01900 p = plane.getRotation()
01901 p.move(self.center)
01902 if self.closedCircle:
01903 try:
01904 self.commit(translate("draft","Create Circle"),
01905 partial(Draft.makeCircle,self.rad,p,
01906 self.ui.fillmode,support=self.support))
01907 except:
01908 print "Draft: error delaying commit"
01909 else:
01910 sta = math.degrees(self.firstangle)
01911 end = math.degrees(self.firstangle+self.angle)
01912 if end < sta: sta,end = end,sta
01913 try:
01914 self.commit(translate("draft","Create Arc"),
01915 partial(Draft.makeCircle,self.rad,p,self.ui.fillmode,
01916 sta,end,support=self.support))
01917 except:
01918 print "Draft: error delaying commit"
01919 self.finish(cont=True)
01920
01921 def numericInput(self,numx,numy,numz):
01922 "this function gets called by the toolbar when valid x, y, and z have been entered there"
01923 self.center = Vector(numx,numy,numz)
01924 self.node = [self.center]
01925 self.arctrack.setCenter(self.center)
01926 self.arctrack.on()
01927 self.ui.radiusUi()
01928 self.step = 1
01929 self.ui.setNextFocus()
01930 msg(translate("draft", "Pick radius:\n"))
01931
01932 def numericRadius(self,rad):
01933 "this function gets called by the toolbar when valid radius have been entered there"
01934 if (self.step == 1):
01935 self.rad = rad
01936 if len(self.tangents) == 2:
01937 cir = fcgeo.circleFrom2tan1rad(self.tangents[0], self.tangents[1], rad)
01938 if self.center:
01939 self.center = fcgeo.findClosestCircle(self.center,cir).Center
01940 else:
01941 self.center = cir[-1].Center
01942 elif self.tangents and self.tanpoints:
01943 cir = fcgeo.circleFrom1tan1pt1rad(self.tangents[0],self.tanpoints[0],rad)
01944 if self.center:
01945 self.center = fcgeo.findClosestCircle(self.center,cir).Center
01946 else:
01947 self.center = cir[-1].Center
01948 if self.closedCircle:
01949 self.drawArc()
01950 else:
01951 self.step = 2
01952 self.arctrack.setCenter(self.center)
01953 self.ui.labelRadius.setText(str(translate("draft", "Start Angle")))
01954 self.linetrack.p1(self.center)
01955 self.linetrack.on()
01956 self.ui.radiusValue.setText("")
01957 self.ui.radiusValue.setFocus()
01958 msg(translate("draft", "Pick start angle:\n"))
01959 elif (self.step == 2):
01960 self.ui.labelRadius.setText(str(translate("draft", "Aperture")))
01961 self.firstangle = math.radians(rad)
01962 if fcvec.equals(plane.axis, Vector(1,0,0)): u = Vector(0,self.rad,0)
01963 else: u = fcvec.scaleTo(Vector(1,0,0).cross(plane.axis), self.rad)
01964 urotated = fcvec.rotate(u, math.radians(rad), plane.axis)
01965 self.arctrack.setStartAngle(self.firstangle)
01966 self.step = 3
01967 self.ui.radiusValue.setText("")
01968 self.ui.radiusValue.setFocus()
01969 msg(translate("draft", "Aperture angle:\n"))
01970 else:
01971 self.updateAngle(rad)
01972 self.angle = math.radians(rad)
01973 self.step = 4
01974 self.drawArc()
01975
01976
01977 class Circle(Arc):
01978 "The Draft_Circle FreeCAD command definition"
01979
01980 def __init__(self):
01981 self.closedCircle=True
01982 self.featureName = "Circle"
01983
01984 def GetResources(self):
01985 return {'Pixmap' : 'Draft_Circle',
01986 'Accel' : "C, I",
01987 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Circle", "Circle"),
01988 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Circle", "Creates a circle. CTRL to snap, ALT to select tangent objects")}
01989
01990
01991 class Polygon(Creator):
01992 "the Draft_Polygon FreeCAD command definition"
01993
01994 def GetResources(self):
01995 return {'Pixmap' : 'Draft_Polygon',
01996 'Accel' : "P, G",
01997 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Polygon", "Polygon"),
01998 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Polygon", "Creates a regular polygon. CTRL to snap, SHIFT to constrain")}
01999
02000 def Activated(self):
02001 Creator.Activated(self,"Polygon")
02002 if self.ui:
02003 self.step = 0
02004 self.center = None
02005 self.rad = None
02006 self.tangents = []
02007 self.tanpoints = []
02008 self.ui.pointUi()
02009 self.ui.extUi()
02010 self.ui.numFaces.show()
02011 self.altdown = False
02012 self.ui.sourceCmd = self
02013 self.linetrack = lineTracker(dotted=True)
02014 self.constraintrack = lineTracker(dotted=True)
02015 self.arctrack = arcTracker()
02016 self.call = self.view.addEventCallback("SoEvent",self.action)
02017 msg(translate("draft", "Pick center point:\n"))
02018
02019 def finish(self,closed=False,cont=False):
02020 "finishes the arc"
02021 Creator.finish(self)
02022 if self.ui:
02023 self.linetrack.finalize()
02024 self.constraintrack.finalize()
02025 self.arctrack.finalize()
02026 self.doc.recompute()
02027 if cont and self.ui:
02028 if self.ui.continueMode:
02029 self.Activated()
02030
02031 def action(self,arg):
02032 "scene event handler"
02033 if arg["Type"] == "SoKeyboardEvent":
02034 if arg["Key"] == "ESCAPE":
02035 self.finish()
02036 elif arg["Type"] == "SoLocation2Event":
02037 point,ctrlPoint = getPoint(self,arg)
02038
02039 self.ui.cross(True)
02040 if self.center and fcvec.dist(point,self.center) > 0:
02041 viewdelta = fcvec.project(point.sub(self.center), plane.axis)
02042 if not fcvec.isNull(viewdelta):
02043 point = point.add(fcvec.neg(viewdelta))
02044 if (self.step == 0):
02045 if hasMod(arg,MODALT):
02046 if not self.altdown:
02047 self.ui.cross(False)
02048 self.altdown = True
02049 self.ui.switchUi(True)
02050 else:
02051 if self.altdown:
02052 self.ui.cross(True)
02053 self.altdown = False
02054 self.ui.switchUi(False)
02055 else:
02056 if len(self.tangents) == 2:
02057 cir = fcgeo.circleFrom2tan1pt(self.tangents[0], self.tangents[1], point)
02058 self.center = fcgeo.findClosestCircle(point,cir).Center
02059 self.arctrack.setCenter(self.center)
02060 elif self.tangents and self.tanpoints:
02061 cir = fcgeo.circleFrom1tan2pt(self.tangents[0], self.tanpoints[0], point)
02062 self.center = fcgeo.findClosestCircle(point,cir).Center
02063 self.arctrack.setCenter(self.center)
02064 if hasMod(arg,MODALT):
02065 if not self.altdown:
02066 self.ui.cross(False)
02067 self.altdown = True
02068 snapped = self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
02069 if snapped:
02070 ob = self.doc.getObject(snapped['Object'])
02071 num = int(snapped['Component'].lstrip('Edge'))-1
02072 ed = ob.Shape.Edges[num]
02073 if len(self.tangents) == 2:
02074 cir = fcgeo.circleFrom3tan(self.tangents[0], self.tangents[1], ed)
02075 cl = fcgeo.findClosestCircle(point,cir)
02076 self.center = cl.Center
02077 self.rad = cl.Radius
02078 self.arctrack.setCenter(self.center)
02079 else:
02080 self.rad = self.center.add(fcgeo.findDistance(self.center,ed).sub(self.center)).Length
02081 else:
02082 self.rad = fcvec.dist(point,self.center)
02083 else:
02084 if self.altdown:
02085 self.ui.cross(True)
02086 self.altdown = False
02087 self.rad = fcvec.dist(point,self.center)
02088 self.ui.setRadiusValue(self.rad)
02089 self.arctrack.setRadius(self.rad)
02090
02091 if hasMod(arg,MODCONSTRAIN):
02092 self.constraintrack.p1(point)
02093 self.constraintrack.p2(ctrlPoint)
02094 self.constraintrack.on()
02095 else: self.constraintrack.off()
02096 self.linetrack.p1(self.center)
02097 self.linetrack.p2(point)
02098 self.linetrack.on()
02099
02100 elif arg["Type"] == "SoMouseButtonEvent":
02101 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
02102 point,ctrlPoint = getPoint(self,arg)
02103
02104 if self.center and fcvec.dist(point,self.center) > 0:
02105 viewdelta = fcvec.project(point.sub(self.center), plane.axis)
02106 if not fcvec.isNull(viewdelta):
02107 point = point.add(fcvec.neg(viewdelta))
02108 if (self.step == 0):
02109 if not self.node: self.support = getSupport(arg)
02110 if hasMod(arg,MODALT):
02111 snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
02112 if snapped:
02113 ob = self.doc.getObject(snapped['Object'])
02114 num = int(snapped['Component'].lstrip('Edge'))-1
02115 ed = ob.Shape.Edges[num]
02116 self.tangents.append(ed)
02117 if len(self.tangents) == 2:
02118 self.arctrack.on()
02119 self.ui.radiusUi()
02120 self.step = 1
02121 self.linetrack.on()
02122 msg(translate("draft", "Pick radius:\n"))
02123 else:
02124 if len(self.tangents) == 1:
02125 self.tanpoints.append(point)
02126 else:
02127 self.center = point
02128 self.node = [point]
02129 self.arctrack.setCenter(self.center)
02130 self.linetrack.p1(self.center)
02131 self.linetrack.p2(self.view.getPoint(arg["Position"][0],arg["Position"][1]))
02132 self.arctrack.on()
02133 self.ui.radiusUi()
02134 self.step = 1
02135 self.linetrack.on()
02136 msg(translate("draft", "Pick radius:\n"))
02137 self.planetrack.set(point)
02138 elif (self.step == 1):
02139 self.ui.cross(False)
02140 self.drawPolygon()
02141
02142 def drawPolygon(self):
02143 "actually draws the FreeCAD object"
02144 p = plane.getRotation()
02145 p.move(self.center)
02146 self.commit(translate("draft","Create Polygon"),
02147 partial(Draft.makePolygon,self.ui.numFaces.value(),self.rad,
02148 True,p,face=self.ui.fillmode,support=self.support))
02149 self.finish(cont=True)
02150
02151 def numericInput(self,numx,numy,numz):
02152 "this function gets called by the toolbar when valid x, y, and z have been entered there"
02153 self.center = Vector(numx,numy,numz)
02154 self.node = [self.center]
02155 self.arctrack.setCenter(self.center)
02156 self.arctrack.on()
02157 self.ui.radiusUi()
02158 self.step = 1
02159 self.ui.radiusValue.setFocus()
02160 msg(translate("draft", "Pick radius:\n"))
02161
02162 def numericRadius(self,rad):
02163 "this function gets called by the toolbar when valid radius have been entered there"
02164 self.rad = rad
02165 if len(self.tangents) == 2:
02166 cir = fcgeo.circleFrom2tan1rad(self.tangents[0], self.tangents[1], rad)
02167 if self.center:
02168 self.center = fcgeo.findClosestCircle(self.center,cir).Center
02169 else:
02170 self.center = cir[-1].Center
02171 elif self.tangents and self.tanpoints:
02172 cir = fcgeo.circleFrom1tan1pt1rad(self.tangents[0],self.tanpoints[0],rad)
02173 if self.center:
02174 self.center = fcgeo.findClosestCircle(self.center,cir).Center
02175 else:
02176 self.center = cir[-1].Center
02177 self.drawPolygon()
02178
02179
02180 class Text(Creator):
02181 "This class creates an annotation feature."
02182
02183 def GetResources(self):
02184 return {'Pixmap' : 'Draft_Text',
02185 'Accel' : "T, E",
02186 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Text", "Text"),
02187 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Text", "Creates an annotation. CTRL to snap")}
02188
02189 def Activated(self):
02190 Creator.Activated(self,"Text")
02191 if self.ui:
02192 self.dialog = None
02193 self.text = ''
02194 self.ui.sourceCmd = self
02195 self.ui.pointUi()
02196 self.call = self.view.addEventCallback("SoEvent",self.action)
02197 self.ui.xValue.setFocus()
02198 self.ui.xValue.selectAll()
02199 msg(translate("draft", "Pick location point:\n"))
02200 FreeCADGui.draftToolBar.show()
02201
02202 def finish(self,closed=False,cont=False):
02203 "terminates the operation"
02204 Creator.finish(self)
02205 if self.ui:
02206 del self.dialog
02207 if cont and self.ui:
02208 if self.ui.continueMode:
02209 self.Activated()
02210
02211 def createObject(self):
02212 "creates an object in the current doc"
02213 self.commit(translate("draft","Create Text"),
02214 partial(Draft.makeText,self.text,self.node[0]))
02215 self.finish(cont=True)
02216
02217 def action(self,arg):
02218 "scene event handler"
02219 if arg["Type"] == "SoKeyboardEvent":
02220 if arg["Key"] == "ESCAPE":
02221 self.finish()
02222 elif arg["Type"] == "SoLocation2Event":
02223 point,ctrlPoint = getPoint(self,arg)
02224 elif arg["Type"] == "SoMouseButtonEvent":
02225 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
02226 point,dtrlPoint = getPoint(self,arg)
02227 self.node.append(point)
02228 self.ui.textUi()
02229 self.ui.textValue.setFocus()
02230 self.ui.cross(False)
02231
02232 def numericInput(self,numx,numy,numz):
02233 '''this function gets called by the toolbar when valid
02234 x, y, and z have been entered there'''
02235 point = Vector(numx,numy,numz)
02236 self.node.append(point)
02237 self.ui.textUi()
02238 self.ui.textValue.setFocus()
02239 self.ui.cross(False)
02240
02241
02242 class Dimension(Creator):
02243 "The Draft_Dimension FreeCAD command definition"
02244
02245 def __init__(self):
02246 self.max=2
02247 self.cont = None
02248 self.dir = None
02249
02250 def GetResources(self):
02251 return {'Pixmap' : 'Draft_Dimension',
02252 'Accel' : "D, I",
02253 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Dimension", "Dimension"),
02254 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Dimension", "Creates a dimension. CTRL to snap, SHIFT to constrain, ALT to select a segment")}
02255
02256 def Activated(self):
02257 if self.cont:
02258 self.finish()
02259 elif self.hasMeasures():
02260 Creator.Activated(self,"Dimension")
02261 self.dimtrack = dimTracker()
02262 self.arctrack = arcTracker()
02263 self.constraintrack = lineTracker(dotted=True)
02264 self.createOnMeasures()
02265 self.finish()
02266 else:
02267 Creator.Activated(self,"Dimension")
02268 if self.ui:
02269 self.ui.pointUi()
02270 self.ui.continueCmd.show()
02271 self.altdown = False
02272 self.call = self.view.addEventCallback("SoEvent",self.action)
02273 self.dimtrack = dimTracker()
02274 self.arctrack = arcTracker()
02275 self.link = None
02276 self.edges = []
02277 self.pts = []
02278 self.angledata = None
02279 self.indices = []
02280 self.center = None
02281 self.arcmode = False
02282 self.point2 = None
02283 self.constraintrack = lineTracker(dotted=True)
02284 msg(translate("draft", "Pick first point:\n"))
02285 FreeCADGui.draftToolBar.show()
02286
02287 def hasMeasures(self):
02288 "checks if only measurements objects are selected"
02289 sel = FreeCADGui.Selection.getSelection()
02290 if not sel:
02291 return False
02292 for o in sel:
02293 if not o.isDerivedFrom("App::MeasureDistance"):
02294 return False
02295 return True
02296
02297 def finish(self,closed=False):
02298 "terminates the operation"
02299 self.cont = None
02300 self.dir = None
02301 Creator.finish(self)
02302 if self.ui:
02303 self.dimtrack.finalize()
02304 self.arctrack.finalize()
02305 self.constraintrack.finalize()
02306
02307 def createOnMeasures(self):
02308 for o in FreeCADGui.Selection.getSelection():
02309 p1 = o.P1
02310 p2 = o.P2
02311 pt = o.ViewObject.RootNode.getChildren()[1].getChildren()[0].getChildren()[0].getChildren()[3]
02312 p3 = Vector(pt.point.getValues()[2].getValue())
02313 self.commit(translate("draft","Create Dimension"),
02314 partial(Draft.makeDimension,p1,p2,p3))
02315 self.commit(translate("draft","Delete Measurement"),
02316 partial(FreeCAD.ActiveDocument.removeObject,o.Name))
02317
02318 def createObject(self):
02319 "creates an object in the current doc"
02320 if self.angledata:
02321 self.commit(translate("draft","Create Dimension"),
02322 partial(Draft.makeAngularDimension,self.center,
02323 self.angledata,self.node[-1]))
02324 elif self.link and (not self.arcmode):
02325 self.commit(translate("draft","Create Dimension"),
02326 partial(Draft.makeDimension,self.link[0],self.link[1],
02327 self.link[2],self.node[2]))
02328 elif self.arcmode:
02329 self.commit(translate("draft","Create Dimension"),
02330 partial(Draft.makeDimension,self.link[0],self.link[1],
02331 self.arcmode,self.node[2]))
02332 else:
02333 self.commit(translate("draft","Create Dimension"),
02334 partial(Draft.makeDimension,self.node[0],self.node[1],
02335 self.node[2]))
02336 if self.ui.continueMode:
02337 self.cont = self.node[2]
02338 if not self.dir:
02339 if self.link:
02340 v1 = self.link[0].Shape.Vertexes[self.link[1]].Point
02341 v2 = self.link[0].Shape.Vertexes[self.link[2]].Point
02342 self.dir = v2.sub(v1)
02343 else:
02344 self.dir = self.node[1].sub(self.node[0])
02345 self.node = [self.node[1]]
02346 self.link = None
02347
02348 def action(self,arg):
02349 "scene event handler"
02350 if arg["Type"] == "SoKeyboardEvent":
02351 if arg["Key"] == "ESCAPE":
02352 self.finish()
02353 elif arg["Type"] == "SoLocation2Event":
02354 shift = hasMod(arg,MODCONSTRAIN)
02355 if self.arcmode or self.point2:
02356 setMod(arg,MODCONSTRAIN,False)
02357 point,ctrlPoint = getPoint(self,arg)
02358 self.ui.cross(True)
02359 if hasMod(arg,MODALT) and (len(self.node)<3):
02360 self.ui.cross(False)
02361 self.dimtrack.off()
02362 if not self.altdown:
02363 self.altdown = True
02364 self.ui.switchUi(True)
02365 snapped = self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
02366 if snapped:
02367 ob = self.doc.getObject(snapped['Object'])
02368 if "Edge" in snapped['Component']:
02369 num = int(snapped['Component'].lstrip('Edge'))-1
02370 ed = ob.Shape.Edges[num]
02371 v1 = ed.Vertexes[0].Point
02372 v2 = ed.Vertexes[-1].Point
02373 self.dimtrack.update([v1,v2,self.cont])
02374 else:
02375 self.ui.cross(True)
02376 if self.node and (len(self.edges) < 2):
02377 self.dimtrack.on()
02378 if len(self.edges) == 2:
02379
02380 self.dimtrack.off()
02381 r = point.sub(self.center)
02382 self.arctrack.setRadius(r.Length)
02383 a = self.arctrack.getAngle(point)
02384 pair = fcgeo.getBoundaryAngles(a,self.pts)
02385 if not (pair[0] < a < pair[1]):
02386 self.angledata = [4*math.pi-pair[0],2*math.pi-pair[1]]
02387 else:
02388 self.angledata = [2*math.pi-pair[0],2*math.pi-pair[1]]
02389 self.arctrack.setStartAngle(self.angledata[0])
02390 self.arctrack.setEndAngle(self.angledata[1])
02391 if self.altdown:
02392 self.altdown = False
02393 self.ui.switchUi(False)
02394 if self.dir:
02395 point = self.node[0].add(fcvec.project(point.sub(self.node[0]),self.dir))
02396 if len(self.node) == 2:
02397 if self.arcmode and self.edges:
02398 cen = self.edges[0].Curve.Center
02399 rad = self.edges[0].Curve.Radius
02400 baseray = point.sub(cen)
02401 v2 = fcvec.scaleTo(baseray,rad)
02402 v1 = fcvec.neg(v2)
02403 if shift:
02404 self.node = [cen,cen.add(v2)]
02405 self.arcmode = "radius"
02406 else:
02407 self.node = [cen.add(v1),cen.add(v2)]
02408 self.arcmode = "diameter"
02409 self.dimtrack.update(self.node)
02410
02411 if shift and (not self.arcmode):
02412 if len(self.node) == 2:
02413 if not self.point2:
02414 self.point2 = self.node[1]
02415 else:
02416 self.node[1] = self.point2
02417 a=abs(point.sub(self.node[0]).getAngle(plane.u))
02418 if (a > math.pi/4) and (a <= 0.75*math.pi):
02419 self.node[1] = Vector(self.node[0].x,self.node[1].y,self.node[0].z)
02420 else:
02421 self.node[1] = Vector(self.node[1].x,self.node[0].y,self.node[0].z)
02422 self.constraintrack.p1(point)
02423 self.constraintrack.p2(ctrlPoint)
02424 self.constraintrack.on()
02425 else:
02426 if self.point2:
02427 self.node[1] = self.point2
02428 self.point2 = None
02429 self.constraintrack.off()
02430
02431 if self.node and (not self.arcmode):
02432 self.dimtrack.update(self.node+[point]+[self.cont])
02433 elif arg["Type"] == "SoMouseButtonEvent":
02434 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
02435 point,ctrlPoint = getPoint(self,arg)
02436 if not self.node: self.support = getSupport(arg)
02437 if hasMod(arg,MODALT) and (len(self.node)<3):
02438 snapped = self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
02439 if snapped:
02440 ob = self.doc.getObject(snapped['Object'])
02441 if 'Edge' in snapped['Component']:
02442 num = int(snapped['Component'].lstrip('Edge'))-1
02443 ed = ob.Shape.Edges[num]
02444 v1 = ed.Vertexes[0].Point
02445 v2 = ed.Vertexes[-1].Point
02446 i1 = i2 = None
02447 for i in range(len(ob.Shape.Vertexes)):
02448 if v1 == ob.Shape.Vertexes[i].Point:
02449 i1 = i
02450 if v2 == ob.Shape.Vertexes[i].Point:
02451 i2 = i
02452 if (i1 != None) and (i2 != None):
02453 self.indices.append(num)
02454 if not self.edges:
02455
02456 self.node = [v1,v2]
02457 self.link = [ob,i1,i2]
02458 self.edges.append(ed)
02459 if isinstance(ed.Curve,Part.Circle):
02460
02461 self.arcmode = "diameter"
02462 self.link = [ob,num]
02463 else:
02464
02465 self.edges.append(ed)
02466 self.node.extend([v1,v2])
02467 c = fcgeo.findIntersection(self.node[0],
02468 self.node[1],
02469 self.node[2],
02470 self.node[3],
02471 True,True)
02472 if c:
02473 self.center = c[0]
02474 self.arctrack.setCenter(self.center)
02475 self.arctrack.on()
02476 for e in self.edges:
02477 for v in e.Vertexes:
02478 self.pts.append(self.arctrack.getAngle(v.Point))
02479 self.link = [self.link[0],ob]
02480 else:
02481 msg(translate("draft", "Edges don't intersect!\n"))
02482 self.finish()
02483 self.dimtrack.on()
02484 else:
02485 if self.dir:
02486 point = self.node[0].add(fcvec.project(point.sub(self.node[0]),self.dir))
02487 self.node.append(point)
02488 self.dimtrack.update(self.node)
02489 if (len(self.node) == 2):
02490 self.point2 = self.node[1]
02491 if (len(self.node) == 1):
02492 self.dimtrack.on()
02493 self.planetrack.set(self.node[0])
02494 elif (len(self.node) == 2) and self.cont:
02495 self.node.append(self.cont)
02496 self.createObject()
02497 if not self.cont: self.finish()
02498 elif (len(self.node) == 3):
02499
02500
02501
02502
02503
02504
02505 self.createObject()
02506 if not self.cont: self.finish()
02507 elif self.angledata:
02508 self.node.append(point)
02509 self.createObject()
02510 self.finish()
02511
02512 def numericInput(self,numx,numy,numz):
02513 "this function gets called by the toolbar when valid x, y, and z have been entered there"
02514 point = Vector(numx,numy,numz)
02515 self.node.append(point)
02516 self.dimtrack.update(self.node)
02517 if (len(self.node) == 1):
02518 self.dimtrack.on()
02519 elif (len(self.node) == 3):
02520 self.createObject()
02521 if not self.cont: self.finish()
02522
02523
02524
02525
02526
02527
02528 class Modifier:
02529 "A generic Modifier Tool, used by modification tools such as move"
02530
02531 def __init__(self):
02532 self.commitList = []
02533
02534 def Activated(self,name="None"):
02535 if FreeCAD.activeDraftCommand:
02536 FreeCAD.activeDraftCommand.finish()
02537 self.ui = None
02538 self.call = None
02539 self.commitList = []
02540 self.doc = FreeCAD.ActiveDocument
02541 if not self.doc:
02542 self.finish()
02543 else:
02544 FreeCAD.activeDraftCommand = self
02545 self.view = FreeCADGui.ActiveDocument.ActiveView
02546 self.ui = FreeCADGui.draftToolBar
02547 FreeCADGui.draftToolBar.show()
02548 rot = self.view.getCameraNode().getField("orientation").getValue()
02549 upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue())
02550 plane.setup(fcvec.neg(self.view.getViewDirection()), Vector(0,0,0), upv)
02551 self.node = []
02552 self.ui.sourceCmd = self
02553 self.constrain = None
02554 self.obj = None
02555 self.extendedCopy = False
02556 self.ui.setTitle(name)
02557 self.featureName = name
02558 self.snap = snapTracker()
02559 self.extsnap = lineTracker(dotted=True)
02560 self.planetrack = PlaneTracker()
02561 if Draft.getParam("grid"):
02562 self.grid = gridTracker()
02563 self.grid.set()
02564 else:
02565 self.grid = None
02566
02567 def IsActive(self):
02568 if FreeCADGui.ActiveDocument:
02569 return True
02570 else:
02571 return False
02572
02573 def finish(self):
02574 self.node = []
02575 self.snap.finalize()
02576 self.extsnap.finalize()
02577 FreeCAD.activeDraftCommand = None
02578 if self.ui:
02579 self.ui.offUi()
02580 self.ui.sourceCmd=None
02581 self.ui.cross(False)
02582 msg("")
02583 self.planetrack.finalize()
02584 if self.grid: self.grid.finalize()
02585 if self.call:
02586 self.view.removeEventCallback("SoEvent",self.call)
02587 self.call = None
02588 if self.commitList:
02589 todo.delayCommit(self.commitList)
02590 self.commitList = []
02591
02592 def commit(self,name,func):
02593 "stores partial actions to be committed to the FreeCAD document"
02594
02595 self.commitList.append((name,func))
02596
02597
02598 class Move(Modifier):
02599 "The Draft_Move FreeCAD command definition"
02600
02601 def GetResources(self):
02602 return {'Pixmap' : 'Draft_Move',
02603 'Accel' : "M, V",
02604 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Move", "Move"),
02605 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Move", "Moves the selected objects between 2 points. CTRL to snap, SHIFT to constrain, ALT to copy")}
02606
02607 def Activated(self):
02608 Modifier.Activated(self,"Move")
02609 if self.ui:
02610 if not Draft.getSelection():
02611 self.ghost = None
02612 self.linetrack = None
02613 self.constraintrack = None
02614 self.ui.selectUi()
02615 msg(translate("draft", "Select an object to move\n"))
02616 self.call = self.view.addEventCallback("SoEvent",selectObject)
02617 else:
02618 self.proceed()
02619
02620 def proceed(self):
02621 if self.call: self.view.removeEventCallback("SoEvent",self.call)
02622 self.sel = Draft.getSelection()
02623 self.sel = Draft.getGroupContents(self.sel)
02624 self.ui.pointUi()
02625 self.ui.modUi()
02626 self.ui.xValue.setFocus()
02627 self.ui.xValue.selectAll()
02628 self.linetrack = lineTracker()
02629 self.constraintrack = lineTracker(dotted=True)
02630 self.ghost = ghostTracker(self.sel)
02631 self.call = self.view.addEventCallback("SoEvent",self.action)
02632 msg(translate("draft", "Pick start point:\n"))
02633 self.ui.cross(True)
02634
02635 def finish(self,closed=False,cont=False):
02636 if self.ui:
02637 self.ghost.finalize()
02638 self.linetrack.finalize()
02639 self.constraintrack.finalize()
02640 Modifier.finish(self)
02641 if cont and self.ui:
02642 if self.ui.continueMode:
02643 FreeCADGui.Selection.clearSelection()
02644 self.Activated()
02645
02646 def move(self,delta,copy=False):
02647 "moving the real shapes"
02648 if copy:
02649 self.commit(translate("draft","Copy"),partial(Draft.move,self.sel,delta,copy))
02650 else:
02651 self.commit(translate("draft","Move"),partial(Draft.move,self.sel,delta,copy))
02652 self.doc.recompute()
02653
02654 def action(self,arg):
02655 "scene event handler"
02656 if arg["Type"] == "SoKeyboardEvent":
02657 if arg["Key"] == "ESCAPE":
02658 self.finish()
02659 elif arg["Type"] == "SoLocation2Event":
02660 point,ctrlPoint = getPoint(self,arg)
02661 self.linetrack.p2(point)
02662 self.ui.cross(True)
02663
02664 if hasMod(arg,MODCONSTRAIN):
02665 self.constraintrack.p1(point)
02666 self.constraintrack.p2(ctrlPoint)
02667 self.constraintrack.on()
02668 else: self.constraintrack.off()
02669 if (len(self.node) > 0):
02670 last = self.node[len(self.node)-1]
02671 delta = point.sub(last)
02672 self.ghost.trans.translation.setValue([delta.x,delta.y,delta.z])
02673 if self.extendedCopy:
02674 if not hasMod(arg,MODALT): self.finish()
02675 elif arg["Type"] == "SoMouseButtonEvent":
02676 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
02677 point,ctrlPoint = getPoint(self,arg)
02678 if (self.node == []):
02679 self.node.append(point)
02680 self.ui.isRelative.show()
02681 self.linetrack.on()
02682 self.ghost.on()
02683 self.linetrack.p1(point)
02684 msg(translate("draft", "Pick end point:\n"))
02685 self.planetrack.set(point)
02686 else:
02687 last = self.node[0]
02688 if self.ui.isCopy.isChecked() or hasMod(arg,MODALT):
02689 self.move(point.sub(last),True)
02690 else:
02691 self.move(point.sub(last))
02692 if hasMod(arg,MODALT):
02693 self.extendedCopy = True
02694 else:
02695 self.finish(cont=True)
02696
02697 def numericInput(self,numx,numy,numz):
02698 "this function gets called by the toolbar when valid x, y, and z have been entered there"
02699 point = Vector(numx,numy,numz)
02700 if not self.node:
02701 self.node.append(point)
02702 self.ui.isRelative.show()
02703 self.ui.isCopy.show()
02704 self.linetrack.p1(point)
02705 self.linetrack.on()
02706 self.ghost.on()
02707 msg(translate("draft", "Pick end point:\n"))
02708 else:
02709 last = self.node[-1]
02710 if self.ui.isCopy.isChecked():
02711 self.move(point.sub(last),True)
02712 else:
02713 self.move(point.sub(last))
02714 self.finish()
02715
02716
02717 class ApplyStyle(Modifier):
02718 "The Draft_ApplyStyle FreeCA command definition"
02719
02720 def GetResources(self):
02721 return {'Pixmap' : 'Draft_Apply',
02722 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ApplyStyle", "Apply Current Style"),
02723 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_ApplyStyle", "Applies current line width and color to selected objects")}
02724
02725 def IsActive(self):
02726 if Draft.getSelection():
02727 return True
02728 else:
02729 return False
02730
02731 def Activated(self):
02732 Modifier.Activated(self)
02733 if self.ui:
02734 self.sel = Draft.getSelection()
02735 if (len(self.sel)>0):
02736 for ob in self.sel:
02737 if (ob.Type == "App::DocumentObjectGroup"):
02738 self.formatGroup(ob)
02739 else:
02740 self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob))
02741
02742 def formatGroup(self,grpob):
02743 for ob in grpob.Group:
02744 if (ob.Type == "App::DocumentObjectGroup"):
02745 self.formatGroup(ob)
02746 else:
02747 self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob))
02748
02749
02750 class Rotate(Modifier):
02751 "The Draft_Rotate FreeCAD command definition"
02752
02753 def GetResources(self):
02754 return {'Pixmap' : 'Draft_Rotate',
02755 'Accel' : "R, O",
02756 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Rotate", "Rotate"),
02757 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Rotate", "Rotates the selected objects. CTRL to snap, SHIFT to constrain, ALT creates a copy")}
02758
02759 def Activated(self):
02760 Modifier.Activated(self,"Rotate")
02761 if self.ui:
02762 if not Draft.getSelection():
02763 self.ghost = None
02764 self.linetrack = None
02765 self.arctrack = None
02766 self.constraintrack = None
02767 self.ui.selectUi()
02768 msg(translate("draft", "Select an object to rotate\n"))
02769 self.call = self.view.addEventCallback("SoEvent",selectObject)
02770 else:
02771 self.proceed()
02772
02773 def proceed(self):
02774 if self.call: self.view.removeEventCallback("SoEvent",self.call)
02775 self.sel = Draft.getSelection()
02776 self.sel = Draft.getGroupContents(self.sel)
02777 self.step = 0
02778 self.center = None
02779 self.ui.arcUi()
02780 self.ui.isCopy.show()
02781 self.ui.setTitle("Rotate")
02782 self.linetrack = lineTracker()
02783 self.constraintrack = lineTracker(dotted=True)
02784 self.arctrack = arcTracker()
02785 self.ghost = ghostTracker(self.sel)
02786 self.call = self.view.addEventCallback("SoEvent",self.action)
02787 msg(translate("draft", "Pick rotation center:\n"))
02788 self.ui.cross(True)
02789
02790 def finish(self,closed=False,cont=False):
02791 "finishes the arc"
02792 Modifier.finish(self)
02793 if self.ui:
02794 self.linetrack.finalize()
02795 self.constraintrack.finalize()
02796 self.arctrack.finalize()
02797 self.ghost.finalize()
02798 self.doc.recompute()
02799 if cont and self.ui:
02800 if self.ui.continueMode:
02801 FreeCADGui.Selection.clearSelection()
02802 self.Activated()
02803
02804 def rot (self,angle,copy=False):
02805 "rotating the real shapes"
02806 if copy:
02807 self.commit(translate("draft","Copy"),
02808 partial(Draft.rotate,self.sel,
02809 math.degrees(angle),self.center,plane.axis,copy))
02810 else:
02811 self.commit(translate("draft","Rotate"),
02812 partial(Draft.rotate,self.sel,
02813 math.degrees(angle),self.center,plane.axis,copy))
02814
02815 def action(self,arg):
02816 "scene event handler"
02817 if arg["Type"] == "SoKeyboardEvent":
02818 if arg["Key"] == "ESCAPE":
02819 self.finish()
02820 elif arg["Type"] == "SoLocation2Event":
02821 point,ctrlPoint = getPoint(self,arg)
02822 self.ui.cross(True)
02823
02824 if self.center and fcvec.dist(point,self.center):
02825 viewdelta = fcvec.project(point.sub(self.center), plane.axis)
02826 if not fcvec.isNull(viewdelta):
02827 point = point.add(fcvec.neg(viewdelta))
02828 if self.extendedCopy:
02829 if not hasMod(arg,MODALT):
02830 self.step = 3
02831 self.finish()
02832 if (self.step == 0):
02833 pass
02834 elif (self.step == 1):
02835 currentrad = fcvec.dist(point,self.center)
02836 if (currentrad != 0):
02837 angle = fcvec.angle(plane.u, point.sub(self.center), plane.axis)
02838 else: angle = 0
02839 self.linetrack.p2(point)
02840
02841 if hasMod(arg,MODCONSTRAIN):
02842 self.constraintrack.p1(point)
02843 self.constraintrack.p2(ctrlPoint)
02844 self.constraintrack.on()
02845 else:
02846 self.constraintrack.off()
02847 self.ui.radiusValue.setText("%.2f" % math.degrees(angle))
02848 self.firstangle = angle
02849 self.ui.radiusValue.setFocus()
02850 self.ui.radiusValue.selectAll()
02851 elif (self.step == 2):
02852 currentrad = fcvec.dist(point,self.center)
02853 if (currentrad != 0):
02854 angle = fcvec.angle(plane.u, point.sub(self.center), plane.axis)
02855 else: angle = 0
02856 if (angle < self.firstangle):
02857 sweep = (2*math.pi-self.firstangle)+angle
02858 else:
02859 sweep = angle - self.firstangle
02860 self.arctrack.setApertureAngle(sweep)
02861 self.ghost.trans.rotation.setValue(coin.SbVec3f(fcvec.tup(plane.axis)),sweep)
02862 self.linetrack.p2(point)
02863
02864 if hasMod(arg,MODCONSTRAIN):
02865 self.constraintrack.p1(point)
02866 self.constraintrack.p2(ctrlPoint)
02867 self.constraintrack.on()
02868 else:
02869 self.constraintrack.off()
02870 self.ui.radiusValue.setText("%.2f" % math.degrees(sweep))
02871 self.ui.radiusValue.setFocus()
02872 self.ui.radiusValue.selectAll()
02873
02874 elif arg["Type"] == "SoMouseButtonEvent":
02875 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
02876 point,ctrlPoint = getPoint(self,arg)
02877 if self.center and fcvec.dist(point,self.center):
02878 viewdelta = fcvec.project(point.sub(self.center), plane.axis)
02879 if not fcvec.isNull(viewdelta): point = point.add(fcvec.neg(viewdelta))
02880 if (self.step == 0):
02881 self.center = point
02882 self.node = [point]
02883 self.ui.radiusUi()
02884 self.ui.hasFill.hide()
02885 self.ui.labelRadius.setText("Base angle")
02886 self.linetrack.p1(self.center)
02887 self.arctrack.setCenter(self.center)
02888 self.ghost.trans.center.setValue(self.center.x,self.center.y,self.center.z)
02889 self.linetrack.on()
02890 self.step = 1
02891 msg(translate("draft", "Pick base angle:\n"))
02892 self.planetrack.set(point)
02893 elif (self.step == 1):
02894 self.ui.labelRadius.setText("Rotation")
02895 self.rad = fcvec.dist(point,self.center)
02896 self.arctrack.on()
02897 self.arctrack.setStartPoint(point)
02898 self.ghost.on()
02899 self.step = 2
02900 msg(translate("draft", "Pick rotation angle:\n"))
02901 else:
02902 currentrad = fcvec.dist(point,self.center)
02903 angle = point.sub(self.center).getAngle(plane.u)
02904 if fcvec.project(point.sub(self.center), plane.v).getAngle(plane.v) > 1:
02905 angle = -angle
02906 if (angle < self.firstangle):
02907 sweep = (2*math.pi-self.firstangle)+angle
02908 else:
02909 sweep = angle - self.firstangle
02910 if self.ui.isCopy.isChecked() or hasMod(arg,MODALT):
02911 self.rot(sweep,True)
02912 else:
02913 self.rot(sweep)
02914 if hasMod(arg,MODALT):
02915 self.extendedCopy = True
02916 else:
02917 self.finish(cont=True)
02918
02919 def numericInput(self,numx,numy,numz):
02920 "this function gets called by the toolbar when valid x, y, and z have been entered there"
02921 self.center = Vector(numx,numy,numz)
02922 self.node = [self.center]
02923 self.arctrack.setCenter(self.center)
02924 self.ghost.trans.center.setValue(self.center.x,self.center.y,self.center.z)
02925 self.linetrack.p1(self.center)
02926
02927 self.linetrack.on()
02928 self.ui.radiusUi()
02929 self.ui.hasFill.hide()
02930 self.ui.labelRadius.setText("Base angle")
02931 self.step = 1
02932 msg(translate("draft", "Pick base angle:\n"))
02933
02934 def numericRadius(self,rad):
02935 "this function gets called by the toolbar when valid radius have been entered there"
02936 if (self.step == 1):
02937 self.ui.labelRadius.setText("Rotation")
02938 self.firstangle = math.radians(rad)
02939 self.arctrack.setStartAngle(self.firstangle)
02940 self.arctrack.on()
02941 self.ghost.on()
02942 self.step = 2
02943 msg(translate("draft", "Pick rotation angle:\n"))
02944 else:
02945 self.rot(math.radians(rad),self.ui.isCopy.isChecked())
02946 self.finish(cont=True)
02947
02948
02949 class Offset(Modifier):
02950 "The Draft_Offset FreeCAD command definition"
02951
02952 def GetResources(self):
02953 return {'Pixmap' : 'Draft_Offset',
02954 'Accel' : "O, S",
02955 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Offset", "Offset"),
02956 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Offset", "Offsets the active object. CTRL to snap, SHIFT to constrain, ALT to copy")}
02957
02958 def Activated(self):
02959 self.running = False
02960 Modifier.Activated(self,"Offset")
02961 if self.ui:
02962 if not Draft.getSelection():
02963 self.ghost = None
02964 self.linetrack = None
02965 self.arctrack = None
02966 self.constraintrack = None
02967 self.ui.selectUi()
02968 msg(translate("draft", "Select an object to offset\n"))
02969 self.call = self.view.addEventCallback("SoEvent",selectObject)
02970 elif len(Draft.getSelection()) > 1:
02971 msg(translate("draft", "Offset only works on one object at a time\n"),"warning")
02972 else:
02973 self.proceed()
02974
02975 def proceed(self):
02976 if self.call: self.view.removeEventCallback("SoEvent",self.call)
02977 self.sel = Draft.getSelection()[0]
02978 if not self.sel.isDerivedFrom("Part::Feature"):
02979 msg(translate("draft", "Cannot offset this object type\n"),"warning")
02980 self.finish()
02981 else:
02982 self.step = 0
02983 self.dvec = None
02984 self.constrainSeg = None
02985 self.ui.offsetUi()
02986 self.linetrack = lineTracker()
02987 self.constraintrack = lineTracker(dotted=True)
02988 self.faces = False
02989 self.shape = self.sel.Shape
02990 self.mode = None
02991 if Draft.getType(self.sel) in ["Circle","Arc"]:
02992 self.ghost = arcTracker()
02993 self.mode = "Circle"
02994 self.center = self.shape.Edges[0].Curve.Center
02995 self.ghost.setCenter(self.center)
02996 self.ghost.setStartAngle(math.radians(self.sel.FirstAngle))
02997 self.ghost.setEndAngle(math.radians(self.sel.LastAngle))
02998 else:
02999 self.ghost = wireTracker(self.shape)
03000 self.mode = "Wire"
03001 self.call = self.view.addEventCallback("SoEvent",self.action)
03002 msg(translate("draft", "Pick distance:\n"))
03003 self.ui.cross(True)
03004 self.planetrack.set(self.shape.Vertexes[0].Point)
03005 self.running = True
03006
03007 def action(self,arg):
03008 "scene event handler"
03009 if arg["Type"] == "SoKeyboardEvent":
03010 if arg["Key"] == "ESCAPE":
03011 self.finish()
03012 elif arg["Type"] == "SoLocation2Event":
03013 self.ui.cross(True)
03014 point,ctrlPoint = getPoint(self,arg)
03015 if hasMod(arg,MODCONSTRAIN) and self.constrainSeg:
03016 dist = fcgeo.findPerpendicular(point,self.shape,self.constrainSeg[1])
03017 e = self.shape.Edges[self.constrainSeg[1]]
03018 self.constraintrack.p1(e.Vertexes[0].Point)
03019 self.constraintrack.p2(point.add(dist[0]))
03020 self.constraintrack.on()
03021 else:
03022 dist = fcgeo.findPerpendicular(point,self.shape.Edges)
03023 self.constraintrack.off()
03024 if dist:
03025 self.ghost.on()
03026 if self.mode == "Wire":
03027 d = fcvec.neg(dist[0])
03028 v1 = fcgeo.getTangent(self.shape.Edges[0],point)
03029 v2 = fcgeo.getTangent(self.shape.Edges[dist[1]],point)
03030 a = -fcvec.angle(v1,v2)
03031 self.dvec = fcvec.rotate(d,a,plane.axis)
03032 self.ghost.update(fcgeo.offsetWire(self.shape,self.dvec,occ=self.ui.occOffset.isChecked()))
03033 elif self.mode == "Circle":
03034 self.dvec = point.sub(self.center).Length
03035 self.ghost.setRadius(self.dvec)
03036 self.constrainSeg = dist
03037 self.linetrack.on()
03038 self.linetrack.p1(point)
03039 self.linetrack.p2(point.add(dist[0]))
03040 self.ui.radiusValue.setText("%.2f" % dist[0].Length)
03041 else:
03042 self.dvec = None
03043 self.ghost.off()
03044 self.constrainSeg = None
03045 self.linetrack.off()
03046 self.ui.radiusValue.setText("off")
03047 self.ui.radiusValue.setFocus()
03048 self.ui.radiusValue.selectAll()
03049 if self.extendedCopy:
03050 if not hasMod(arg,MODALT): self.finish()
03051
03052 elif arg["Type"] == "SoMouseButtonEvent":
03053 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
03054 copymode = False
03055 occmode = self.ui.occOffset.isChecked()
03056 if hasMod(arg,MODALT) or self.ui.isCopy.isChecked(): copymode = True
03057 if self.dvec:
03058 self.commit(translate("draft","Offset"),
03059 partial(Draft.offset,self.sel,
03060 self.dvec,copymode,occ=occmode))
03061 if hasMod(arg,MODALT):
03062 self.extendedCopy = True
03063 else:
03064 self.finish()
03065
03066 def finish(self,closed=False):
03067 Modifier.finish(self)
03068 if self.ui and self.running:
03069 self.linetrack.finalize()
03070 self.constraintrack.finalize()
03071 self.ghost.finalize()
03072
03073 def numericRadius(self,rad):
03074 '''this function gets called by the toolbar when
03075 valid radius have been entered there'''
03076 if self.dvec:
03077 self.dvec.normalize()
03078 self.dvec.multiply(rad)
03079 copymode = False
03080 occmode = self.ui.occOffset.isChecked()
03081 if self.ui.isCopy.isChecked(): copymode = True
03082 self.commit(translate("draft","Offset"),
03083 partial(Draft.offset,self.sel,
03084 self.dvec,copymode,occ=occmode))
03085 self.finish()
03086
03087
03088 class Upgrade(Modifier):
03089 '''The Draft_Upgrade FreeCAD command definition.
03090 This class upgrades selected objects in different ways,
03091 following this list (in order):
03092 - if there are more than one faces, the faces are merged (union)
03093 - if there is only one face, nothing is done
03094 - if there are closed wires, they are transformed in a face
03095 - otherwise join all edges into a wire (closed if applicable)
03096 - if nothing of the above is possible, a Compound is created
03097 '''
03098
03099 def GetResources(self):
03100 return {'Pixmap' : 'Draft_Upgrade',
03101 'Accel' : "U, P",
03102 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Upgrade", "Upgrade"),
03103 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Upgrade", "Joins the selected objects into one, or converts closed wires to filled faces, or unite faces")}
03104
03105 def Activated(self):
03106 Modifier.Activated(self,"Upgrade")
03107 if self.ui:
03108 if not Draft.getSelection():
03109 self.ui.selectUi()
03110 msg(translate("draft", "Select an object to upgrade\n"))
03111 self.call = self.view.addEventCallback("SoEvent",selectObject)
03112 else:
03113 self.proceed()
03114
03115 def compound(self):
03116
03117
03118
03119
03120 newob = Draft.makeBlock(self.sel)
03121 self.nodelete = True
03122 return newob
03123
03124 def proceed(self):
03125 if self.call: self.view.removeEventCallback("SoEvent",self.call)
03126 self.sel = Draft.getSelection()
03127 newob = None
03128 self.nodelete = False
03129 edges = []
03130 wires = []
03131 openwires = []
03132 faces = []
03133 groups = []
03134 curves = []
03135 facewires = []
03136
03137
03138 for ob in self.sel:
03139 if ob.Type == "App::DocumentObjectGroup":
03140 groups.append(ob)
03141 else:
03142 if ob.Shape.ShapeType == 'Edge': openwires.append(ob.Shape)
03143 for f in ob.Shape.Faces:
03144 faces.append(f)
03145 facewires.extend(f.Wires)
03146 for w in ob.Shape.Wires:
03147 if w.isClosed():
03148 wires.append(w)
03149 else:
03150 openwires.append(w)
03151 for e in ob.Shape.Edges:
03152 if not isinstance(e.Curve,Part.Line):
03153 curves.append(e)
03154 lastob = ob
03155
03156
03157
03158
03159 self.doc.openTransaction("Upgrade")
03160
03161 if groups:
03162
03163 msg(translate("draft", "Found groups: closing each open object inside\n"))
03164 for grp in groups:
03165 for ob in grp.Group:
03166 if not ob.Shape.Faces:
03167 for w in ob.Shape.Wires:
03168 newob = Draft.makeWire(w,closed=w.isClosed())
03169 self.sel.append(ob)
03170 grp.addObject(newob)
03171
03172 elif faces and (len(wires)+len(openwires)==len(facewires)):
03173
03174
03175 if (len(self.sel) == 1) and (len(faces) > 1):
03176
03177 sol = Part.makeSolid(self.sel[0].Shape)
03178 if sol.isClosed():
03179 msg(translate("draft", "Found 1 solidificable object: solidifying it\n"))
03180 newob = self.doc.addObject("Part::Feature","Solid")
03181 newob.Shape = sol
03182 Draft.formatObject(newob,lastob)
03183
03184 elif (len(self.sel) == 2) and (not curves):
03185
03186 msg(translate("draft", "Found 2 objects: fusing them\n"))
03187 newob = Draft.fuse(self.sel[0],self.sel[1])
03188 self.nodelete = True
03189
03190 elif (len(self.sel) > 2) and (len(faces) > 10):
03191
03192 sh = Part.makeShell(faces)
03193 newob = self.doc.addObject("Part::Feature","Shell")
03194 newob.Shape = sh
03195 Draft.formatObject(newob,lastob)
03196
03197 elif (len(self.sel) > 2) or (len(faces) > 1):
03198
03199 u = faces.pop(0)
03200 for f in faces:
03201 u = u.fuse(f)
03202 if fcgeo.isCoplanar(faces):
03203 if self.sel[0].ViewObject.DisplayMode == "Wireframe":
03204 f = False
03205 else:
03206 f = True
03207 u = fcgeo.concatenate(u)
03208 if not curves:
03209 msg(translate("draft", "Found several objects or faces: making a parametric face\n"))
03210 newob = Draft.makeWire(u.Wires[0],closed=True,face=f)
03211 Draft.formatObject(newob,lastob)
03212 else:
03213
03214 msg(translate("draft", "Found objects containing curves: fusing them\n"))
03215 newob = self.doc.addObject("Part::Feature","Union")
03216 newob.Shape = u
03217 Draft.formatObject(newob,lastob)
03218 else:
03219
03220 msg(translate("draft", "Found several objects: fusing them\n"))
03221
03222 if (len(u.Faces) > 1) and u.isClosed():
03223 u = Part.makeSolid(u)
03224 newob = self.doc.addObject("Part::Feature","Union")
03225 newob.Shape = u
03226 Draft.formatObject(newob,lastob)
03227 elif len(self.sel) == 1:
03228
03229 self.nodelete = True
03230 if (not curves) and (Draft.getType(self.sel[0]) == "Part"):
03231 msg(translate("draft", "Found 1 non-parametric objects: draftifying it\n"))
03232 Draft.draftify(self.sel[0])
03233
03234 elif wires and (not faces) and (not openwires):
03235
03236
03237 if (len(self.sel) == 1) and self.sel[0].isDerivedFrom("Sketcher::SketchObject") and (not curves):
03238
03239 msg(translate("draft", "Found 1 closed sketch object: making a face from it\n"))
03240 newob = Draft.makeWire(self.sel[0].Shape,closed=True)
03241 newob.Base = self.sel[0]
03242 self.sel[0].ViewObject.Visibility = False
03243 self.nodelete = True
03244
03245 else:
03246
03247 for w in wires:
03248 f = Part.Face(w)
03249 faces.append(f)
03250 for f in faces:
03251 if not curves:
03252 newob = Draft.makeWire(f.Wire,closed=True)
03253 else:
03254
03255 msg(translate("draft", "Found closed wires: making faces\n"))
03256 newob = self.doc.addObject("Part::Feature","Face")
03257 newob.Shape = f
03258 Draft.formatObject(newob,lastob)
03259
03260 elif (len(openwires) == 1) and (not faces) and (not wires):
03261
03262 p0 = openwires[0].Vertexes[0].Point
03263 p1 = openwires[0].Vertexes[-1].Point
03264 edges = openwires[0].Edges
03265 edges.append(Part.Line(p1,p0).toShape())
03266 w = Part.Wire(fcgeo.sortEdges(edges))
03267 msg(translate("draft", "Found 1 open wire: closing it\n"))
03268 if not curves:
03269 newob = Draft.makeWire(w,closed=True)
03270 else:
03271
03272 newob = self.doc.addObject("Part::Feature","Wire")
03273 newob.Shape = w
03274 Draft.formatObject(newob,lastob)
03275
03276 elif openwires and (not wires) and (not faces):
03277
03278 for ob in self.sel:
03279 for e in ob.Shape.Edges:
03280 edges.append(e)
03281 newob = None
03282 nedges = fcgeo.sortEdges(edges[:])
03283
03284 w = Part.Wire(nedges)
03285 if len(w.Edges) == len(edges):
03286 msg(translate("draft", "Found several edges: wiring them\n"))
03287 if not curves:
03288 newob = Draft.makeWire(w)
03289 else:
03290 newob = self.doc.addObject("Part::Feature","Wire")
03291 newob.Shape = w
03292 Draft.formatObject(newob,lastob)
03293 if not newob:
03294 print "no new object found"
03295 msg(translate("draft", "Found several non-connected edges: making compound\n"))
03296 newob = self.compound()
03297 Draft.formatObject(newob,lastob)
03298 else:
03299
03300 msg(translate("draft", "Found several non-treatable objects: making compound\n"))
03301 newob = self.compound()
03302 Draft.formatObject(newob,lastob)
03303
03304 if not self.nodelete:
03305
03306 for ob in self.sel:
03307 if not ob.Type == "App::DocumentObjectGroup":
03308 self.doc.removeObject(ob.Name)
03309
03310 self.doc.commitTransaction()
03311 if newob: Draft.select(newob)
03312 Modifier.finish(self)
03313
03314
03315 class Downgrade(Modifier):
03316 '''
03317 The Draft_Downgrade FreeCAD command definition.
03318 This class downgrades selected objects in different ways,
03319 following this list (in order):
03320 - if there are more than one faces, the subsequent
03321 faces are subtracted from the first one
03322 - if there is only one face, it gets converted to a wire
03323 - otherwise wires are exploded into single edges
03324 '''
03325
03326 def GetResources(self):
03327 return {'Pixmap' : 'Draft_Downgrade',
03328 'Accel' : "D, N",
03329 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Downgrade", "Downgrade"),
03330 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Downgrade", "Explodes the selected objects into simpler objects, or subtract faces")}
03331
03332 def Activated(self):
03333 Modifier.Activated(self,"Downgrade")
03334 if self.ui:
03335 if not Draft.getSelection():
03336 self.ui.selectUi()
03337 msg(translate("draft", "Select an object to upgrade\n"))
03338 self.call = self.view.addEventCallback("SoEvent",selectObject)
03339 else:
03340 self.proceed()
03341
03342 def proceed(self):
03343 self.sel = Draft.getSelection()
03344 edges = []
03345 faces = []
03346
03347
03348 for ob in self.sel:
03349 for f in ob.Shape.Faces:
03350 faces.append(f)
03351 for ob in self.sel:
03352 for e in ob.Shape.Edges:
03353 edges.append(e)
03354 lastob = ob
03355
03356
03357 self.doc.openTransaction("Downgrade")
03358
03359 if (len(self.sel) == 1) and (Draft.getType(self.sel[0]) == "Block"):
03360
03361 pl = self.sel[0].Placement
03362 newob = []
03363 for ob in self.sel[0].Components:
03364 ob.ViewObject.Visibility = True
03365 ob.Placement = ob.Placement.multiply(pl)
03366 newob.append(ob)
03367 self.doc.removeObject(self.sel[0].Name)
03368
03369 elif (len(self.sel) == 1) and (self.sel[0].isDerivedFrom("Part::Feature")) and ("Base" in self.sel[0].PropertiesList):
03370
03371 msg(translate("draft", "Found 1 parametric object: breaking its dependencies\n"))
03372 newob = Draft.shapify(self.sel[0])
03373
03374 elif len(self.sel) == 2:
03375
03376 msg(translate("draft", "Found 2 objects: subtracting them\n"))
03377 newob = Draft.cut(self.sel[0],self.sel[1])
03378
03379 elif (len(faces) > 1):
03380
03381 if len(self.sel) == 1:
03382
03383 for f in faces:
03384 msg(translate("draft", "Found several faces: splitting them\n"))
03385 newob = self.doc.addObject("Part::Feature","Face")
03386 newob.Shape = f
03387 Draft.formatObject(newob,self.sel[0])
03388 self.doc.removeObject(ob.Name)
03389
03390 else:
03391
03392 msg(translate("draft", "Found several objects: subtracting them from the first one\n"))
03393 u = faces.pop(0)
03394 for f in faces:
03395 u = u.cut(f)
03396 newob = self.doc.addObject("Part::Feature","Subtraction")
03397 newob.Shape = u
03398 for ob in self.sel:
03399 Draft.formatObject(newob,ob)
03400 self.doc.removeObject(ob.Name)
03401
03402 elif (len(faces) > 0):
03403
03404 msg(translate("draft", "Found 1 face: extracting its wires\n"))
03405 for w in faces[0].Wires:
03406 newob = self.doc.addObject("Part::Feature","Wire")
03407 newob.Shape = w
03408 Draft.formatObject(newob,lastob)
03409 for ob in self.sel:
03410 self.doc.removeObject(ob.Name)
03411
03412 else:
03413
03414 msg(translate("draft", "Found only wires: extracting their edges\n"))
03415 for ob in self.sel:
03416 for e in edges:
03417 newob = self.doc.addObject("Part::Feature","Edge")
03418 newob.Shape = e
03419 Draft.formatObject(newob,ob)
03420 self.doc.removeObject(ob.Name)
03421 self.doc.commitTransaction()
03422 Draft.select(newob)
03423 Modifier.finish(self)
03424
03425
03426 class Trimex(Modifier):
03427 ''' The Draft_Trimex FreeCAD command definition.
03428 This tool trims or extends lines, wires and arcs,
03429 or extrudes single faces. SHIFT constrains to the last point
03430 or extrudes in direction to the face normal.'''
03431
03432 def GetResources(self):
03433 return {'Pixmap' : 'Draft_Trimex',
03434 'Accel' : "T, R",
03435 'MenuText' : QtCore.QT_TRANSLATE_NOOP("Draft_Trimex", "Trimex"),
03436 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Trimex", "Trims or extends the selected object, or extrudes single faces. CTRL snaps, SHIFT constrains to current segment or to normal, ALT inverts")}
03437
03438 def Activated(self):
03439 Modifier.Activated(self,"Trimex")
03440 self.edges = []
03441 self.placement = None
03442 self.ghost = None
03443 self.linetrack = None
03444 self.constraintrack = None
03445 if self.ui:
03446 if not Draft.getSelection():
03447 self.ui.selectUi()
03448 msg(translate("draft", "Select an object to trim/extend\n"))
03449 self.call = self.view.addEventCallback("SoEvent",selectObject)
03450 else:
03451 self.proceed()
03452
03453 def proceed(self):
03454 if self.call: self.view.removeEventCallback("SoEvent",self.call)
03455 self.obj = Draft.getSelection()[0]
03456 self.ui.trimUi()
03457 self.linetrack = lineTracker()
03458 self.constraintrack = lineTracker(dotted=True)
03459 if not "Shape" in self.obj.PropertiesList: return
03460 if "Placement" in self.obj.PropertiesList:
03461 self.placement = self.obj.Placement
03462 if len(self.obj.Shape.Faces) == 1:
03463
03464 self.extrudeMode = True
03465 self.ghost = [ghostTracker([self.obj])]
03466 self.normal = self.obj.Shape.Faces[0].normalAt(.5,.5)
03467 for v in self.obj.Shape.Vertexes:
03468 self.ghost.append(lineTracker())
03469 elif len(self.obj.Shape.Faces) > 1:
03470
03471 ss = FreeCADGui.Selection.getSelectionEx()[0]
03472 if len(ss.SubObjects) == 1:
03473 if ss.SubObjects[0].ShapeType == "Face":
03474 self.obj = self.doc.addObject("Part::Feature","Face")
03475 self.obj.Shape = ss.SubObjects[0]
03476 self.extrudeMode = True
03477 self.ghost = [ghostTracker([self.obj])]
03478 self.normal = self.obj.Shape.Faces[0].normalAt(.5,.5)
03479 for v in self.obj.Shape.Vertexes:
03480 self.ghost.append(lineTracker())
03481 else:
03482
03483 self.obj.ViewObject.Visibility = False
03484 self.extrudeMode = False
03485 if self.obj.Shape.Wires:
03486 self.edges = self.obj.Shape.Wires[0].Edges
03487 self.edges = fcgeo.sortEdges(self.edges)
03488 else:
03489 self.edges = self.obj.Shape.Edges
03490 self.ghost = []
03491 lc = self.obj.ViewObject.LineColor
03492 sc = (lc[0],lc[1],lc[2])
03493 sw = self.obj.ViewObject.LineWidth
03494 for e in self.edges:
03495 if isinstance(e.Curve,Part.Line):
03496 self.ghost.append(lineTracker(scolor=sc,swidth=sw))
03497 else:
03498 self.ghost.append(arcTracker(scolor=sc,swidth=sw))
03499 if not self.ghost: self.finish()
03500 for g in self.ghost: g.on()
03501 self.activePoint = 0
03502 self.nodes = []
03503 self.shift = False
03504 self.alt = False
03505 self.force = None
03506 self.cv = None
03507 self.call = self.view.addEventCallback("SoEvent",self.action)
03508 msg(translate("draft", "Pick distance:\n"))
03509 self.ui.cross(True)
03510
03511 def action(self,arg):
03512 "scene event handler"
03513 if arg["Type"] == "SoKeyboardEvent":
03514 if arg["Key"] == "ESCAPE":
03515 self.finish()
03516 elif arg["Type"] == "SoLocation2Event":
03517 self.ui.cross(True)
03518 self.shift = hasMod(arg,MODCONSTRAIN)
03519 self.alt = hasMod(arg,MODALT)
03520 wp = not(self.extrudeMode and self.shift)
03521 self.point = getPoint(self,arg,workingplane=wp)[0]
03522 if hasMod(arg,MODSNAP): self.snapped = None
03523 else: self.snapped = self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
03524 if self.extrudeMode:
03525 dist = self.extrude(self.shift)
03526 else:
03527 dist = self.redraw(self.point,self.snapped,self.shift,self.alt)
03528 self.constraintrack.p1(self.point)
03529 self.constraintrack.p2(self.newpoint)
03530 self.constraintrack.on()
03531 self.ui.radiusValue.setText("%.2f" % dist)
03532 self.ui.radiusValue.setFocus()
03533 self.ui.radiusValue.selectAll()
03534
03535 elif arg["Type"] == "SoMouseButtonEvent":
03536 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
03537 cursor = arg["Position"]
03538 self.shift = hasMod(arg,MODCONSTRAIN)
03539 self.alt = hasMod(arg,MODALT)
03540 if hasMod(arg,MODSNAP): self.snapped = None
03541 else: self.snapped = self.view.getObjectInfo((cursor[0],cursor[1]))
03542 self.trimObject()
03543 self.finish()
03544
03545 def extrude(self,shift=False,real=False):
03546 "redraws the ghost in extrude mode"
03547 self.newpoint = self.obj.Shape.Faces[0].CenterOfMass
03548 dvec = self.point.sub(self.newpoint)
03549 if shift: delta = fcvec.project(dvec,self.normal)
03550 else: delta = dvec
03551 if self.force:
03552 ratio = self.force/delta.Length
03553 delta.multiply(ratio)
03554 if real: return delta
03555 self.ghost[0].trans.translation.setValue([delta.x,delta.y,delta.z])
03556 for i in range(1,len(self.ghost)):
03557 base = self.obj.Shape.Vertexes[i-1].Point
03558 self.ghost[i].p1(base)
03559 self.ghost[i].p2(base.add(delta))
03560 return delta.Length
03561
03562 def redraw(self,point,snapped=None,shift=False,alt=False,real=None):
03563 "redraws the ghost"
03564
03565
03566 reverse = False
03567 for g in self.ghost: g.off()
03568 if real: newedges = []
03569
03570
03571 vlist = []
03572 for e in self.edges: vlist.append(e.Vertexes[0].Point)
03573 vlist.append(self.edges[-1].Vertexes[-1].Point)
03574 if shift: npoint = self.activePoint
03575 else: npoint = fcgeo.findClosest(point,vlist)
03576 if npoint > len(self.edges)/2: reverse = True
03577 if alt: reverse = not reverse
03578 self.activePoint = npoint
03579
03580
03581 if reverse and (npoint > 0): npoint = npoint-1
03582 if (npoint > len(self.edges)-1):
03583 edge = self.edges[-1]
03584 ghost = self.ghost[-1]
03585 else:
03586 edge = self.edges[npoint]
03587 ghost = self.ghost[npoint]
03588 if reverse:
03589 v1 = edge.Vertexes[-1].Point
03590 v2 = edge.Vertexes[0].Point
03591 else:
03592 v1 = edge.Vertexes[0].Point
03593 v2 = edge.Vertexes[-1].Point
03594
03595
03596 if snapped:
03597 snapped = self.doc.getObject(snapped['Object'])
03598 pts = []
03599 for e in snapped.Shape.Edges:
03600 int = fcgeo.findIntersection(edge,e,True,True)
03601 if int: pts.extend(int)
03602 if pts:
03603 point = pts[fcgeo.findClosest(point,pts)]
03604
03605
03606 if isinstance(edge.Curve,Part.Line):
03607 perp = fcgeo.vec(edge).cross(Vector(0,0,1))
03608 chord = v1.sub(point)
03609 proj = fcvec.project(chord,perp)
03610 self.newpoint = Vector.add(point,proj)
03611 dist = v1.sub(self.newpoint).Length
03612 ghost.p1(self.newpoint)
03613 ghost.p2(v2)
03614 self.ui.labelRadius.setText("Distance")
03615 if real:
03616 if self.force:
03617 ray = self.newpoint.sub(v1)
03618 ray = fcvec.scale(ray,self.force/ray.Length)
03619 self.newpoint = Vector.add(v1,ray)
03620 newedges.append(Part.Line(self.newpoint,v2).toShape())
03621 else:
03622 center = edge.Curve.Center
03623 rad = edge.Curve.Radius
03624 ang1 = fcvec.angle(v2.sub(center))
03625 ang2 = fcvec.angle(point.sub(center))
03626 self.newpoint=Vector.add(center,fcvec.rotate(Vector(rad,0,0),-ang2))
03627 self.ui.labelRadius.setText("Angle")
03628 dist = math.degrees(-ang2)
03629
03630 print "last calculated:",math.degrees(-ang1),math.degrees(-ang2)
03631 ghost.setEndAngle(-ang2)
03632 ghost.setStartAngle(-ang1)
03633 ghost.setCenter(center)
03634 ghost.setRadius(rad)
03635 if real:
03636 if self.force:
03637 angle = math.radians(self.force)
03638 newray = fcvec.rotate(Vector(rad,0,0),-angle)
03639 self.newpoint = Vector.add(center,newray)
03640 chord = self.newpoint.sub(v2)
03641 perp = chord.cross(Vector(0,0,1))
03642 scaledperp = fcvec.scaleTo(perp,rad)
03643 midpoint = Vector.add(center,scaledperp)
03644 newedges.append(Part.Arc(self.newpoint,midpoint,v2).toShape())
03645 ghost.on()
03646
03647
03648 if not reverse: list = range(npoint+1,len(self.edges))
03649 else: list = range(npoint-1,-1,-1)
03650 for i in list:
03651 edge = self.edges[i]
03652 ghost = self.ghost[i]
03653 if isinstance(edge.Curve,Part.Line):
03654 ghost.p1(edge.Vertexes[0].Point)
03655 ghost.p2(edge.Vertexes[-1].Point)
03656 else:
03657 ang1 = fcvec.angle(edge.Vertexes[0].Point.sub(center))
03658 ang2 = fcvec.angle(edge.Vertexes[-1].Point.sub(center))
03659
03660 ghost.setEndAngle(-ang2)
03661 ghost.setStartAngle(-ang1)
03662 ghost.setCenter(edge.Curve.Center)
03663 ghost.setRadius(edge.Curve.Radius)
03664 if real: newedges.append(edge)
03665 ghost.on()
03666
03667
03668 if real: return newedges
03669 else: return dist
03670
03671 def trimObject(self):
03672 "trims the actual object"
03673 if self.extrudeMode:
03674 delta = self.extrude(self.shift,real=True)
03675 print "delta",delta
03676 self.doc.openTransaction("Extrude")
03677 obj = Draft.extrude(self.obj,delta)
03678 self.doc.commitTransaction()
03679 self.obj = obj
03680 else:
03681 edges = self.redraw(self.point,self.snapped,self.shift,self.alt,real=True)
03682 newshape = Part.Wire(edges)
03683 self.doc.openTransaction("Trim/extend")
03684 if Draft.getType(self.obj) in ["Wire","BSpline"]:
03685 p = []
03686 if self.placement: invpl = self.placement.inverse()
03687 for v in newshape.Vertexes:
03688 np = v.Point
03689 if self.placement: np = invpl.multVec(np)
03690 p.append(np)
03691 self.obj.Points = p
03692 elif Draft.getType(self.obj) == "Circle":
03693 angles = self.ghost[0].getAngles()
03694 print "original",self.obj.FirstAngle," ",self.obj.LastAngle
03695 print "new",angles
03696 if angles[0] > angles[1]: angles = (angles[1],angles[0])
03697 self.obj.FirstAngle = angles[0]
03698 self.obj.LastAngle = angles[1]
03699 else:
03700 self.obj.Shape = newshape
03701 self.doc.commitTransaction()
03702 for g in self.ghost: g.off()
03703
03704 def finish(self,closed=False):
03705 Modifier.finish(self)
03706 self.force = None
03707 if self.ui:
03708 self.linetrack.finalize()
03709 self.constraintrack.finalize()
03710 if self.ghost:
03711 for g in self.ghost:
03712 g.finalize()
03713 self.obj.ViewObject.Visibility = True
03714 Draft.select(self.obj)
03715
03716 def numericRadius(self,dist):
03717 "this function gets called by the toolbar when valid distance have been entered there"
03718 self.force = dist
03719 self.trimObject()
03720 self.finish()
03721
03722
03723 class Scale(Modifier):
03724 '''The Draft_Scale FreeCAD command definition.
03725 This tool scales the selected objects from a base point.'''
03726
03727 def GetResources(self):
03728 return {'Pixmap' : 'Draft_Scale',
03729 'Accel' : "S, C",
03730 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Scale", "Scale"),
03731 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Scale", "Scales the selected objects from a base point. CTRL to snap, SHIFT to constrain, ALT to copy")}
03732
03733 def Activated(self):
03734 Modifier.Activated(self,"Scale")
03735 if self.ui:
03736 if not Draft.getSelection():
03737 self.ghost = None
03738 self.linetrack = None
03739 self.constraintrack = None
03740 self.ui.selectUi()
03741 msg(translate("draft", "Select an object to scale\n"))
03742 self.call = self.view.addEventCallback("SoEvent",selectObject)
03743 else:
03744 self.proceed()
03745
03746 def proceed(self):
03747 if self.call: self.view.removeEventCallback("SoEvent",self.call)
03748 self.sel = Draft.getSelection()
03749 self.sel = Draft.getGroupContents(self.sel)
03750 self.ui.pointUi()
03751 self.ui.modUi()
03752 self.ui.xValue.setFocus()
03753 self.ui.xValue.selectAll()
03754 self.linetrack = lineTracker()
03755 self.constraintrack = lineTracker(dotted=True)
03756 self.ghost = ghostTracker(self.sel)
03757 self.call = self.view.addEventCallback("SoEvent",self.action)
03758 msg(translate("draft", "Pick base point:\n"))
03759 self.ui.cross(True)
03760
03761 def finish(self,closed=False,cont=False):
03762 Modifier.finish(self)
03763 if self.ui:
03764 self.ghost.finalize()
03765 self.linetrack.finalize()
03766 self.constraintrack.finalize()
03767 if cont and self.ui:
03768 if self.ui.continueMode:
03769 FreeCADGui.Selection.clearSelection()
03770 self.Activated()
03771
03772 def scale(self,delta,copy=False):
03773 "moving the real shapes"
03774 if copy:
03775 self.commit(translate("draft","Copy"),
03776 partial(Draft.scale,self.sel,delta,self.node[0],copy))
03777 else:
03778 self.commit(translate("draft","Scale"),
03779 partial(Draft.scale,self.sel,delta,self.node[0],copy))
03780
03781 def action(self,arg):
03782 "scene event handler"
03783 if arg["Type"] == "SoKeyboardEvent":
03784 if arg["Key"] == "ESCAPE":
03785 self.finish()
03786 elif arg["Type"] == "SoLocation2Event":
03787 point,ctrlPoint = getPoint(self,arg,sym=True)
03788 self.linetrack.p2(point)
03789 self.ui.cross(True)
03790
03791 if hasMod(arg,MODCONSTRAIN):
03792 self.constraintrack.p1(point)
03793 self.constraintrack.p2(ctrlPoint)
03794 self.constraintrack.on()
03795 else: self.constraintrack.off()
03796 if (len(self.node) > 0):
03797 last = self.node[len(self.node)-1]
03798 delta = point.sub(last)
03799 self.ghost.trans.scaleFactor.setValue([delta.x,delta.y,delta.z])
03800 corr = Vector(self.node[0].x,self.node[0].y,self.node[0].z)
03801 corr.scale(delta.x,delta.y,delta.z)
03802 corr = fcvec.neg(corr.sub(self.node[0]))
03803 self.ghost.trans.translation.setValue([corr.x,corr.y,corr.z])
03804 if self.extendedCopy:
03805 if not hasMod(arg,MODALT): self.finish()
03806 elif arg["Type"] == "SoMouseButtonEvent":
03807 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
03808 point,ctrlPoint = getPoint(self,arg,sym=True)
03809 if (self.node == []):
03810 self.node.append(point)
03811 self.ui.isRelative.show()
03812 self.ui.isCopy.show()
03813 self.linetrack.on()
03814 self.ghost.on()
03815 self.linetrack.p1(point)
03816 msg(translate("draft", "Pick scale factor:\n"))
03817 else:
03818 last = self.node[0]
03819 if self.ui.isCopy.isChecked() or hasMod(arg,MODALT):
03820 self.scale(point.sub(last),True)
03821 else:
03822 self.scale(point.sub(last))
03823 if hasMod(arg,MODALT):
03824 self.extendedCopy = True
03825 else:
03826 self.finish(cont=True)
03827
03828 def numericInput(self,numx,numy,numz):
03829 "this function gets called by the toolbar when valid x, y, and z have been entered there"
03830 point = Vector(numx,numy,numz)
03831 if not self.node:
03832 self.node.append(point)
03833 self.ui.isRelative.show()
03834 self.ui.isCopy.show()
03835 self.linetrack.p1(point)
03836 self.linetrack.on()
03837 self.ghost.on()
03838 msg(translate("draft", "Pick scale factor:\n"))
03839 else:
03840 last = self.node[-1]
03841 if self.ui.isCopy.isChecked():
03842 self.scale(point.sub(last),True)
03843 else:
03844 self.scale(point.sub(last))
03845 self.finish(cont=True)
03846
03847
03848 class ToggleConstructionMode():
03849 "The Draft_ToggleConstructionMode FreeCAD command definition"
03850
03851 def GetResources(self):
03852 return {'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleConstructionMode", "Toggle construcion Mode"),
03853 'Accel' : "C, M",
03854 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleConstructionMode", "Toggles the Construction Mode for next objects.")}
03855
03856 def Activated(self):
03857 FreeCADGui.draftToolBar.constrButton.toggle()
03858
03859
03860 class ToggleContinueMode():
03861 "The Draft_ToggleContinueMode FreeCAD command definition"
03862
03863 def GetResources(self):
03864 return {'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleContinueMode", "Toggle continue Mode"),
03865 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleContinueMode", "Toggles the Continue Mode for next commands.")}
03866
03867 def Activated(self):
03868 FreeCADGui.draftToolBar.continueCmd.toggle()
03869
03870
03871 class Drawing(Modifier):
03872 "The Draft Drawing command definition"
03873
03874 def GetResources(self):
03875 return {'Pixmap' : 'Draft_Drawing',
03876 'Accel' : "D, D",
03877 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Drawing", "Drawing"),
03878 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Drawing", "Puts the selected objects on a Drawing sheet.")}
03879
03880 def IsActive(self):
03881 if Draft.getSelection():
03882 return True
03883 else:
03884 return False
03885
03886 def Activated(self):
03887 Modifier.Activated(self,"Drawing")
03888 sel = Draft.getSelection()
03889 if not sel:
03890 self.page = self.createDefaultPage()
03891 else:
03892 self.page = None
03893 for obj in sel:
03894 if obj.isDerivedFrom("Drawing::FeaturePage"):
03895 self.page = obj
03896 sel.pop(sel.index(obj))
03897 if not self.page:
03898 for obj in self.doc.Objects:
03899 if obj.isDerivedFrom("Drawing::FeaturePage"):
03900 self.page = obj
03901 if not self.page:
03902 self.page = self.createDefaultPage()
03903 sel.reverse()
03904 for obj in sel:
03905 self.insertPattern(obj)
03906 if obj.ViewObject.isVisible():
03907 name = 'View'+obj.Name
03908 oldobj = self.page.getObject(name)
03909 if oldobj: self.doc.removeObject(oldobj.Name)
03910 Draft.makeDrawingView(obj,self.page)
03911 self.doc.recompute()
03912
03913 def insertPattern(self,obj):
03914 "inserts a pattern object on the page"
03915 if 'FillStyle' in obj.ViewObject.PropertiesList:
03916 if obj.ViewObject.FillStyle != 'shape color':
03917 hatch = obj.ViewObject.FillStyle
03918 vobj = self.page.getObject('Pattern'+hatch)
03919 if not vobj:
03920 if hatch in FreeCAD.svgpatterns:
03921 view = self.doc.addObject('Drawing::FeatureView','Pattern'+hatch)
03922 svg = FreeCAD.svgpatterns[hatch]
03923 view.ViewResult = svg
03924 view.X = 0
03925 view.Y = 0
03926 view.Scale = 1
03927 self.page.addObject(view)
03928
03929 def createDefaultPage(self):
03930 "created a default page"
03931 template = Draft.getParam("template")
03932 if not template:
03933 template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg'
03934 page = self.doc.addObject('Drawing::FeaturePage','Page')
03935 page.ViewObject.HintOffsetX = 200
03936 page.ViewObject.HintOffsetY = 100
03937 page.ViewObject.HintScale = 20
03938 page.Template = template
03939 self.doc.recompute()
03940 return page
03941
03942
03943 class ToggleDisplayMode():
03944 "The ToggleDisplayMode FreeCAD command definition"
03945
03946 def GetResources(self):
03947 return {'Pixmap' : 'Draft_SwitchMode',
03948 'Accel' : "Shift+Space",
03949 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleDisplayMode", "Toggle display mode"),
03950 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_ToggleDisplayMode", "Swaps display mode of selected objects between wireframe and flatlines")}
03951
03952 def IsActive(self):
03953 if Draft.getSelection():
03954 return True
03955 else:
03956 return False
03957
03958 def Activated(self):
03959 for obj in Draft.getSelection():
03960 if obj.ViewObject.DisplayMode == "Flat Lines":
03961 if "Wireframe" in obj.ViewObject.listDisplayModes():
03962 obj.ViewObject.DisplayMode = "Wireframe"
03963 elif obj.ViewObject.DisplayMode == "Wireframe":
03964 if "Flat Lines" in obj.ViewObject.listDisplayModes():
03965 obj.ViewObject.DisplayMode = "Flat Lines"
03966
03967
03968 class Edit(Modifier):
03969 "The Draft_Edit FreeCAD command definition"
03970
03971 def __init__(self):
03972 self.running = False
03973
03974 def GetResources(self):
03975 return {'Pixmap' : 'Draft_Edit',
03976 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Edit", "Edit"),
03977 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Edit", "Edits the active object")}
03978
03979 def IsActive(self):
03980 if Draft.getSelection():
03981 self.selection = Draft.getSelection()
03982 if "Proxy" in self.selection[0].PropertiesList:
03983 if hasattr(self.selection[0].Proxy,"Type"):
03984 return True
03985 return False
03986
03987 def Activated(self):
03988 if self.running:
03989 self.finish()
03990 else:
03991 Modifier.Activated(self,"Edit")
03992 self.ui.editUi()
03993 if self.doc:
03994 self.obj = Draft.getSelection()
03995 if self.obj:
03996 self.obj = self.obj[0]
03997
03998 self.selectstate = self.obj.ViewObject.Selectable
03999 self.obj.ViewObject.Selectable = False
04000 if not Draft.getType(self.obj) in ["Wire","BSpline"]:
04001 self.ui.setEditButtons(False)
04002 else:
04003 self.ui.setEditButtons(True)
04004 self.editing = None
04005 self.editpoints = []
04006 self.pl = None
04007 if "Placement" in self.obj.PropertiesList:
04008 self.pl = self.obj.Placement
04009 self.invpl = self.pl.inverse()
04010 if Draft.getType(self.obj) in ["Wire","BSpline"]:
04011 for p in self.obj.Points:
04012 if self.pl: p = self.pl.multVec(p)
04013 self.editpoints.append(p)
04014 elif Draft.getType(self.obj) == "Circle":
04015 self.editpoints.append(self.obj.Placement.Base)
04016 if self.obj.FirstAngle == self.obj.LastAngle:
04017 self.editpoints.append(self.obj.Shape.Vertexes[0].Point)
04018 elif Draft.getType(self.obj) == "Rectangle":
04019 self.editpoints.append(self.obj.Placement.Base)
04020 self.editpoints.append(self.obj.Shape.Vertexes[2].Point)
04021 v = self.obj.Shape.Vertexes
04022 self.bx = v[1].Point.sub(v[0].Point)
04023 if self.obj.Length < 0: self.bx = fcvec.neg(self.bx)
04024 self.by = v[2].Point.sub(v[1].Point)
04025 if self.obj.Height < 0: self.by = fcvec.neg(self.by)
04026 elif Draft.getType(self.obj) == "Polygon":
04027 self.editpoints.append(self.obj.Placement.Base)
04028 self.editpoints.append(self.obj.Shape.Vertexes[0].Point)
04029 elif Draft.getType(self.obj) == "Dimension":
04030 p = self.obj.ViewObject.Proxy.textpos.translation.getValue()
04031 self.editpoints.append(self.obj.Start)
04032 self.editpoints.append(self.obj.End)
04033 self.editpoints.append(self.obj.Dimline)
04034 self.editpoints.append(Vector(p[0],p[1],p[2]))
04035 self.trackers = []
04036 self.constraintrack = None
04037 if self.editpoints:
04038 for ep in range(len(self.editpoints)):
04039 self.trackers.append(editTracker(self.editpoints[ep],self.obj.Name,
04040 ep,self.obj.ViewObject.LineColor))
04041 self.constraintrack = lineTracker(dotted=True)
04042 self.call = self.view.addEventCallback("SoEvent",self.action)
04043 self.running = True
04044 plane.save()
04045 if "Shape" in self.obj.PropertiesList:
04046 plane.alignToFace(self.obj.Shape)
04047 self.planetrack.set(self.editpoints[0])
04048 else:
04049 msg(translate("draft", "This object type is not editable\n"),'warning')
04050 self.finish()
04051 else:
04052 self.finish()
04053
04054 def finish(self,closed=False):
04055 "terminates the operation"
04056 if closed:
04057 if "Closed" in self.obj.PropertiesList:
04058 if not self.obj.Closed:
04059 self.obj.Closed = True
04060 if self.ui:
04061 if self.trackers:
04062 for t in self.trackers:
04063 t.finalize()
04064 if self.constraintrack:
04065 self.constraintrack.finalize()
04066 self.obj.ViewObject.Selectable = self.selectstate
04067 Modifier.finish(self)
04068 plane.restore()
04069 self.running = False
04070
04071 def action(self,arg):
04072 "scene event handler"
04073 if arg["Type"] == "SoKeyboardEvent":
04074 if arg["Key"] == "ESCAPE":
04075 self.finish()
04076 elif arg["Type"] == "SoLocation2Event":
04077 if self.editing != None:
04078 point,ctrlPoint = getPoint(self,arg)
04079
04080 if hasMod(arg,MODCONSTRAIN):
04081 self.constraintrack.p1(point)
04082 self.constraintrack.p2(ctrlPoint)
04083 self.constraintrack.on()
04084 else:
04085 self.constraintrack.off()
04086 self.trackers[self.editing].set(point)
04087 self.update(self.trackers[self.editing].get())
04088 elif arg["Type"] == "SoMouseButtonEvent":
04089 if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
04090 if self.editing == None:
04091 snapped = self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
04092 if snapped:
04093 if snapped['Object'] == self.obj.Name:
04094 if self.ui.addButton.isChecked():
04095 point,ctrlPoint = getPoint(self,arg)
04096 self.pos = arg["Position"]
04097 self.addPoint(point)
04098 elif self.ui.delButton.isChecked():
04099 if 'EditNode' in snapped['Component']:
04100 self.delPoint(int(snapped['Component'][8:]))
04101 elif 'EditNode' in snapped['Component']:
04102 self.ui.pointUi()
04103 self.ui.isRelative.show()
04104 self.editing = int(snapped['Component'][8:])
04105 self.trackers[self.editing].off()
04106 self.obj.ViewObject.Selectable = False
04107 if "Points" in self.obj.PropertiesList:
04108 self.node.append(self.obj.Points[self.editing])
04109 else:
04110 print "finishing edit"
04111 self.trackers[self.editing].on()
04112 self.obj.ViewObject.Selectable = True
04113 self.numericInput(self.trackers[self.editing].get())
04114
04115 def update(self,v):
04116 if Draft.getType(self.obj) in ["Wire","BSpline"]:
04117 pts = self.obj.Points
04118 editPnt = self.invpl.multVec(v)
04119
04120 tol = 0.001
04121 if ( ( self.editing == 0 ) and ( (editPnt - pts[-1]).Length < tol) ) or ( self.editing == len(pts) - 1 ) and ( (editPnt - pts[0]).Length < tol):
04122 self.obj.Closed = True
04123
04124 if ( editPnt in pts ) == False:
04125 pts[self.editing] = editPnt
04126 self.obj.Points = pts
04127 self.trackers[self.editing].set(v)
04128 elif Draft.getType(self.obj) == "Circle":
04129 delta = v.sub(self.obj.Placement.Base)
04130 if self.editing == 0:
04131 p = self.obj.Placement
04132 p.move(delta)
04133 self.obj.Placement = p
04134 self.trackers[0].set(self.obj.Placement.Base)
04135 elif self.editing == 1:
04136 self.obj.Radius = delta.Length
04137 self.trackers[1].set(self.obj.Shape.Vertexes[0].Point)
04138 elif Draft.getType(self.obj) == "Rectangle":
04139 delta = v.sub(self.obj.Placement.Base)
04140 if self.editing == 0:
04141 p = self.obj.Placement
04142 p.move(delta)
04143 self.obj.Placement = p
04144 elif self.editing == 1:
04145 diag = v.sub(self.obj.Placement.Base)
04146 nx = fcvec.project(diag,self.bx)
04147 ny = fcvec.project(diag,self.by)
04148 ax = nx.Length
04149 ay = ny.Length
04150 if ax and ay:
04151 if abs(nx.getAngle(self.bx)) > 0.1:
04152 ax = -ax
04153 if abs(ny.getAngle(self.by)) > 0.1:
04154 ay = -ay
04155 self.obj.Length = ax
04156 self.obj.Height = ay
04157 self.trackers[0].set(self.obj.Placement.Base)
04158 self.trackers[1].set(self.obj.Shape.Vertexes[2].Point)
04159 elif Draft.getType(self.obj) == "Polygon":
04160 delta = v.sub(self.obj.Placement.Base)
04161 if self.editing == 0:
04162 p = self.obj.Placement
04163 p.move(delta)
04164 self.obj.Placement = p
04165 self.trackers[0].set(self.obj.Placement.Base)
04166 elif self.editing == 1:
04167 if self.obj.DrawMode == 'inscribed':
04168 self.obj.Radius = delta.Length
04169 else:
04170 halfangle = ((math.pi*2)/self.obj.FacesNumber)/2
04171 rad = math.cos(halfangle)*delta.Length
04172 self.obj.Radius = rad
04173 self.trackers[1].set(self.obj.Shape.Vertexes[0].Point)
04174 elif Draft.getType(self.obj) == "Dimension":
04175 if self.editing == 0:
04176 self.obj.Start = v
04177 elif self.editing == 1:
04178 self.obj.End = v
04179 elif self.editing == 2:
04180 self.obj.Dimline = v
04181 elif self.editing == 3:
04182 self.obj.ViewObject.TextPosition = v
04183
04184 def numericInput(self,v,numy=None,numz=None):
04185 '''this function gets called by the toolbar
04186 when valid x, y, and z have been entered there'''
04187 if (numy != None):
04188 v = Vector(v,numy,numz)
04189 self.doc.openTransaction("Edit "+self.obj.Name)
04190 self.update(v)
04191 self.doc.commitTransaction()
04192 self.editing = None
04193 self.ui.editUi()
04194 self.node = []
04195
04196 def addPoint(self,point):
04197 if not (Draft.getType(self.obj) in ["Wire","BSpline"]): return
04198 pts = self.obj.Points
04199 if ( Draft.getType(self.obj) == "Wire" ):
04200 if (self.obj.Closed == True):
04201
04202
04203 edges = self.obj.Shape.Wires[0].Edges
04204 e1 = edges[-1]
04205 v1 = e1.Vertexes[0].Point
04206 v2 = e1.Vertexes[1].Point
04207 v2.multiply(0.9999)
04208 edges[-1] = Part.makeLine(v1,v2)
04209 edges.reverse()
04210 wire = Part.Wire(edges)
04211 curve = wire.approximate(0.0001,0.0001,100,25)
04212 else:
04213
04214 curve = self.obj.Shape.Wires[0].approximate(0.0001,0.0001,100,25)
04215 elif ( Draft.getType(self.obj) == "BSpline" ):
04216 if (self.obj.Closed == True):
04217 curve = self.obj.Shape.Edges[0].Curve
04218 else:
04219 curve = self.obj.Shape.Curve
04220 uNewPoint = curve.parameter(point)
04221 uPoints = []
04222 for p in self.obj.Points:
04223 uPoints.append(curve.parameter(p))
04224 for i in range(len(uPoints)-1):
04225 if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ):
04226 pts.insert(i+1, self.invpl.multVec(point))
04227 break
04228
04229 if ( self.obj.Closed ) and ( uNewPoint > uPoints[-1] ) :
04230 pts.append(self.invpl.multVec(point))
04231 self.doc.openTransaction("Edit "+self.obj.Name)
04232 self.obj.Points = pts
04233 self.doc.commitTransaction()
04234 self.resetTrackers()
04235
04236 def delPoint(self,point):
04237 if not (Draft.getType(self.obj) in ["Wire","BSpline"]): return
04238 if len(self.obj.Points) <= 2:
04239 msg(translate("draft", "Active object must have more than two points/nodes\n"),'warning')
04240 else:
04241 pts = self.obj.Points
04242 pts.pop(point)
04243 self.doc.openTransaction("Edit "+self.obj.Name)
04244 self.obj.Points = pts
04245 self.doc.commitTransaction()
04246 self.resetTrackers()
04247
04248 def resetTrackers(self):
04249 for t in self.trackers:
04250 t.finalize()
04251 self.trackers = []
04252 for ep in range(len(self.obj.Points)):
04253 objPoints = self.obj.Points[ep]
04254 if self.pl: objPoints = self.pl.multVec(objPoints)
04255 self.trackers.append(editTracker(objPoints,self.obj.Name,ep,self.obj.ViewObject.LineColor))
04256
04257
04258 class AddToGroup():
04259 "The AddToGroup FreeCAD command definition"
04260
04261 def GetResources(self):
04262 return {'Pixmap' : 'Draft_AddToGroup',
04263 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_AddToGroup", "Add to group..."),
04264 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_AddToGroup", "Adds the selected object(s) to an existing group")}
04265
04266 def IsActive(self):
04267 if Draft.getSelection():
04268 return True
04269 else:
04270 return False
04271
04272 def Activated(self):
04273 self.groups = ["Ungroup"]
04274 self.groups.extend(Draft.getGroupNames())
04275 self.labels = ["Ungroup"]
04276 for g in self.groups:
04277 o = FreeCAD.ActiveDocument.getObject(g)
04278 if o: self.labels.append(o.Label)
04279 self.ui = FreeCADGui.draftToolBar
04280 self.ui.sourceCmd = self
04281 self.ui.popupMenu(self.labels)
04282
04283 def proceed(self,labelname):
04284 self.ui.sourceCmd = None
04285 if labelname == "Ungroup":
04286 for obj in Draft.getSelection():
04287 try:
04288 Draft.ungroup(obj)
04289 except:
04290 pass
04291 else:
04292 if labelname in self.labels:
04293 i = self.labels.index(labelname)
04294 g = FreeCAD.ActiveDocument.getObject(self.groups[i])
04295 for obj in Draft.getSelection():
04296 try:
04297 g.addObject(obj)
04298 except:
04299 pass
04300
04301
04302 class AddPoint(Modifier):
04303 "The Draft_AddPoint FreeCAD command definition"
04304
04305 def __init__(self):
04306 self.running = False
04307
04308 def GetResources(self):
04309 return {'Pixmap' : 'Draft_AddPoint',
04310 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_AddPoint", "Add Point"),
04311 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_AddPoint", "Adds a point to an existing wire/bspline")}
04312
04313 def IsActive(self):
04314 self.selection = Draft.getSelection()
04315 if (Draft.getType(self.selection[0]) in ['Wire','BSpline']):
04316 return True
04317 else:
04318 return False
04319
04320 def Activated(self):
04321 FreeCADGui.draftToolBar.vertUi(True)
04322 FreeCADGui.runCommand("Draft_Edit")
04323
04324
04325 class DelPoint(Modifier):
04326 "The Draft_DelPoint FreeCAD command definition"
04327
04328 def __init__(self):
04329 self.running = False
04330
04331 def GetResources(self):
04332 return {'Pixmap' : 'Draft_DelPoint',
04333 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_DelPoint", "Remove Point"),
04334 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_DelPoint", "Removes a point from an existing wire or bspline")}
04335
04336 def IsActive(self):
04337 self.selection = Draft.getSelection()
04338 if (Draft.getType(self.selection[0]) in ['Wire','BSpline']):
04339 return True
04340 else:
04341 return False
04342
04343 def Activated(self):
04344 FreeCADGui.draftToolBar.vertUi(False)
04345 FreeCADGui.runCommand("Draft_Edit")
04346
04347
04348 class WireToBSpline(Modifier):
04349 "The Draft_Wire2BSpline FreeCAD command definition"
04350
04351 def __init__(self):
04352 self.running = False
04353
04354 def GetResources(self):
04355 return {'Pixmap' : 'Draft_WireToBSpline',
04356 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_WireToBSpline", "Wire to BSpline"),
04357 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_WireToBSpline", "Converts between Wire and BSpline")}
04358
04359 def IsActive(self):
04360 self.selection = Draft.getSelection()
04361 if (Draft.getType(self.selection[0]) in ['Wire','BSpline']):
04362 return True
04363 else:
04364 return False
04365
04366 def Activated(self):
04367 if self.running:
04368 self.finish()
04369 else:
04370 Modifier.Activated(self,"Convert Curve Type")
04371 if self.doc:
04372 self.obj = Draft.getSelection()
04373 if self.obj:
04374 self.obj = self.obj[0]
04375 self.pl = None
04376 if "Placement" in self.obj.PropertiesList:
04377 self.pl = self.obj.Placement
04378 self.Points = self.obj.Points
04379 self.closed = self.obj.Closed
04380 n = None
04381 if (Draft.getType(self.selection[0]) == 'Wire'):
04382 n = Draft.makeBSpline(self.Points, self.closed, self.pl)
04383 elif (Draft.getType(self.selection[0]) == 'BSpline'):
04384 n = Draft.makeWire(self.Points, self.closed, self.pl)
04385 if n:
04386 Draft.formatObject(n,self.selection[0])
04387 else:
04388 self.finish()
04389 def finish(self):
04390 Modifier.finish(self)
04391
04392
04393 class SelectGroup():
04394 "The SelectGroup FreeCAD command definition"
04395
04396 def GetResources(self):
04397 return {'Pixmap' : 'Draft_SelectGroup',
04398 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_SelectGroup", "Select group"),
04399 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_SelectGroup", "Selects all objects with the same parents as this group")}
04400
04401 def IsActive(self):
04402 if Draft.getSelection():
04403 return True
04404 else:
04405 return False
04406
04407 def Activated(self):
04408 sellist = []
04409 for ob in Draft.getSelection():
04410 for child in ob.OutList:
04411 FreeCADGui.Selection.addSelection(child)
04412 for parent in ob.InList:
04413 FreeCADGui.Selection.addSelection(parent)
04414 for child in parent.OutList:
04415 FreeCADGui.Selection.addSelection(child)
04416
04417
04418 class Shape2DView():
04419 "The Shape2DView FreeCAD command definition"
04420 def GetResources(self):
04421 return {'Pixmap' : 'Draft_2DShapeView',
04422 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Shape2DView", "Shape 2D view"),
04423 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Shape2DView", "Creates Shape 2D views of selected objects")}
04424
04425 def IsActive(self):
04426 if Draft.getSelection():
04427 return True
04428 else:
04429 return False
04430
04431 def Activated(self):
04432 sellist = []
04433 for ob in Draft.getSelection():
04434 Draft.makeShape2DView(ob)
04435
04436
04437
04438
04439
04440
04441 FreeCADGui.addCommand('Draft_SelectPlane',SelectPlane())
04442 FreeCADGui.addCommand('Draft_Line',Line())
04443 FreeCADGui.addCommand('Draft_Wire',Wire())
04444 FreeCADGui.addCommand('Draft_Circle',Circle())
04445 FreeCADGui.addCommand('Draft_Arc',Arc())
04446 FreeCADGui.addCommand('Draft_Text',Text())
04447 FreeCADGui.addCommand('Draft_Rectangle',Rectangle())
04448 FreeCADGui.addCommand('Draft_Dimension',Dimension())
04449 FreeCADGui.addCommand('Draft_Polygon',Polygon())
04450 FreeCADGui.addCommand('Draft_BSpline',BSpline())
04451
04452
04453 FreeCADGui.addCommand('Draft_Move',Move())
04454 FreeCADGui.addCommand('Draft_Rotate',Rotate())
04455 FreeCADGui.addCommand('Draft_Offset',Offset())
04456 FreeCADGui.addCommand('Draft_Upgrade',Upgrade())
04457 FreeCADGui.addCommand('Draft_Downgrade',Downgrade())
04458 FreeCADGui.addCommand('Draft_Trimex',Trimex())
04459 FreeCADGui.addCommand('Draft_Scale',Scale())
04460 FreeCADGui.addCommand('Draft_Drawing',Drawing())
04461 FreeCADGui.addCommand('Draft_Edit',Edit())
04462 FreeCADGui.addCommand('Draft_AddPoint',AddPoint())
04463 FreeCADGui.addCommand('Draft_DelPoint',DelPoint())
04464 FreeCADGui.addCommand('Draft_WireToBSpline',WireToBSpline())
04465
04466
04467 FreeCADGui.addCommand('Draft_FinishLine',FinishLine())
04468 FreeCADGui.addCommand('Draft_CloseLine',CloseLine())
04469 FreeCADGui.addCommand('Draft_UndoLine',UndoLine())
04470 FreeCADGui.addCommand('Draft_ToggleConstructionMode',ToggleConstructionMode())
04471 FreeCADGui.addCommand('Draft_ToggleContinueMode',ToggleContinueMode())
04472 FreeCADGui.addCommand('Draft_ApplyStyle',ApplyStyle())
04473 FreeCADGui.addCommand('Draft_ToggleDisplayMode',ToggleDisplayMode())
04474 FreeCADGui.addCommand('Draft_AddToGroup',AddToGroup())
04475 FreeCADGui.addCommand('Draft_SelectGroup',SelectGroup())
04476 FreeCADGui.addCommand('Draft_Shape2DView',Shape2DView())
04477
04478
04479 FreeCAD.activeDraftCommand = None
04480
04481