00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 from __future__ import division
00025
00026 __title__="FreeCAD Draft Workbench"
00027 __author__ = "Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline, Dmitry Chigrin"
00028 __url__ = "http://free-cad.sourceforge.net"
00029
00030 '''
00031 General description:
00032
00033 The Draft module is a FreeCAD module for drawing/editing 2D entities.
00034 The aim is to give FreeCAD basic 2D-CAD capabilities (similar
00035 to Autocad and other similar software). This modules is made to be run
00036 inside FreeCAD and needs the PyQt4 and pivy modules available.
00037
00038 User manual:
00039
00040 http://sourceforge.net/apps/mediawiki/free-cad/index.php?title=2d_Drafting_Module
00041
00042 How it works / how to extend:
00043
00044 This module is written entirely in python. If you know a bit of python
00045 language, you are welcome to modify this module or to help us to improve it.
00046 Suggestions are also welcome on the FreeCAD discussion forum.
00047
00048 If you want to have a look at the code, here is a general explanation. The
00049 Draft module is divided in several files:
00050
00051 - Draft.py: Hosts the functions that are useful for scripting outside of
00052 the Draft module, it is the "Draft API"
00053 - draftGui.py: Creates and manages the special Draft toolbar
00054 - draftTools.py: Contains the user tools of the Draft module (the commands
00055 from the Draft menu), and a couple of helpers such as the "Trackers"
00056 (temporary geometry used while drawing)
00057 - draftlibs/fcvec.py: a vector math library, contains functions that are not
00058 implemented in the standard FreeCAD vector
00059 - draftlibs/fcgeo.py: a library of misc functions to manipulate shapes.
00060
00061 The Draft.py contains everything to create geometry in the scene. You
00062 should start there if you intend to modify something. Then, the draftTools
00063 are where the FreeCAD commands are defined, while in draftGui.py
00064 you have the ui part, ie. the draft command bar. Both draftTools and
00065 draftGui are loaded at module init by InitGui.py, which is called directly by FreeCAD.
00066 The tools all have an Activated() function, which is called by FreeCAD when the
00067 corresponding FreeCAD command is invoked. Most tools then create the trackers they
00068 will need during operation, then place a callback mechanism, which will detect
00069 user input and do the necessary cad operations. They also send commands to the
00070 command bar, which will display the appropriate controls. While the scene event
00071 callback watches mouse events, the keyboard is being watched by the command bar.
00072 '''
00073
00074
00075
00076 import FreeCAD, FreeCADGui, Part, math, sys, os, Image, Drawing, WorkingPlane
00077 from FreeCAD import Vector
00078 from draftlibs import fcvec, fcgeo
00079 from pivy import coin
00080
00081
00082
00083
00084
00085 def typecheck (args_and_types, name="?"):
00086 "typecheck([arg1,type),(arg2,type),...]): checks arguments types"
00087 for v,t in args_and_types:
00088 if not isinstance (v,t):
00089 w = "typecheck[" + str(name) + "]: "
00090 w += str(v) + " is not " + str(t) + "\n"
00091 FreeCAD.Console.PrintWarning(w)
00092 raise TypeError("Draft." + str(name))
00093
00094 def getParamType(param):
00095 if param in ["dimsymbol","dimPrecision","dimorientation","precision","defaultWP",
00096 "snapRange","gridEvery","linewidth","UiMode","modconstrain","modsnap",
00097 "modalt"]:
00098 return "int"
00099 elif param in ["constructiongroupname","textfont","patternFile","template"]:
00100 return "string"
00101 elif param in ["textheight","tolerance","gridSpacing"]:
00102 return "float"
00103 elif param in ["selectBaseObjects","alwaysSnap","grid","fillmode","saveonexit"]:
00104 return "bool"
00105 elif param in ["color","constructioncolor","snapcolor"]:
00106 return "unsigned"
00107 else:
00108 return None
00109
00110 def getParam(param):
00111 "getParam(parameterName): returns a Draft parameter value from the current config"
00112 p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
00113 t = getParamType(param)
00114 if t == "int": return p.GetInt(param)
00115 elif t == "string": return p.GetString(param)
00116 elif t == "float": return p.GetFloat(param)
00117 elif t == "bool": return p.GetBool(param)
00118 elif t == "unsigned": return p.GetUnsigned(param)
00119 else: return None
00120
00121 def setParam(param,value):
00122 "setParam(parameterName,value): sets a Draft parameter with the given value"
00123 p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
00124 t = getParamType(param)
00125 if t == "int": p.SetInt(param,value)
00126 elif t == "string": p.SetString(param,value)
00127 elif t == "float": p.SetFloat(param,value)
00128 elif t == "bool": p.SetBool(param,value)
00129 elif t == "unsigned": p.SetUnsigned(param,value)
00130
00131 def precision():
00132 "precision(): returns the precision value from Draft user settings"
00133 return getParam("precision")
00134
00135 def tolerance():
00136 "tolerance(): returns the tolerance value from Draft user settings"
00137 return getParam("tolerance")
00138
00139 def getRealName(name):
00140 "getRealName(string): strips the trailing numbers from a string name"
00141 for i in range(1,len(name)):
00142 if not name[-i] in '1234567890':
00143 return name[:len(name)-(i-1)]
00144 return name
00145
00146 def getType(obj):
00147 "getType(object): returns the Draft type of the given object"
00148 if "Proxy" in obj.PropertiesList:
00149 if hasattr(obj.Proxy,"Type"):
00150 return obj.Proxy.Type
00151 if obj.isDerivedFrom("Part::Feature"):
00152 return "Part"
00153 if (obj.Type == "App::Annotation"):
00154 return "Annotation"
00155 if obj.isDerivedFrom("Mesh::Feature"):
00156 return "Mesh"
00157 if (obj.Type == "App::DocumentObjectGroup"):
00158 return "Group"
00159 return "Unknown"
00160
00161 def getGroupNames():
00162 "returns a list of existing groups in the document"
00163 glist = []
00164 doc = FreeCAD.ActiveDocument
00165 for obj in doc.Objects:
00166 if obj.Type == "App::DocumentObjectGroup":
00167 glist.append(obj.Name)
00168 return glist
00169
00170 def ungroup(obj):
00171 "removes the current object from any group it belongs to"
00172 for g in getGroupNames():
00173 grp = FreeCAD.ActiveDocument.getObject(g)
00174 if grp.hasObject(obj):
00175 grp.removeObject(obj)
00176
00177 def dimSymbol():
00178 "returns the current dim symbol from the preferences as a pivy SoMarkerSet"
00179 s = getParam("dimsymbol")
00180 marker = coin.SoMarkerSet()
00181 if s == 0: marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_5_5
00182 elif s == 1: marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_7_7
00183 elif s == 2: marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
00184 elif s == 3: marker.markerIndex = coin.SoMarkerSet.CIRCLE_LINE_5_5
00185 elif s == 4: marker.markerIndex = coin.SoMarkerSet.CIRCLE_LINE_7_7
00186 elif s == 5: marker.markerIndex = coin.SoMarkerSet.CIRCLE_LINE_9_9
00187 elif s == 6: marker.markerIndex = coin.SoMarkerSet.SLASH_5_5
00188 elif s == 7: marker.markerIndex = coin.SoMarkerSet.SLASH_7_7
00189 elif s == 8: marker.markerIndex = coin.SoMarkerSet.SLASH_9_9
00190 elif s == 9: marker.markerIndex = coin.SoMarkerSet.BACKSLASH_5_5
00191 elif s == 10: marker.markerIndex = coin.SoMarkerSet.BACKSLASH_7_7
00192 elif s == 11: marker.markerIndex = coin.SoMarkerSet.BACKSLASH_9_9
00193 return marker
00194
00195 def shapify(obj):
00196 '''shapify(object): transforms a parametric shape object into
00197 non-parametric and returns the new object'''
00198 if not (obj.isDerivedFrom("Part::Feature")): return None
00199 if not "Shape" in obj.PropertiesList: return None
00200 if obj.Type == "Part::Feature": return obj
00201 shape = obj.Shape
00202 name = getRealName(obj.Name)
00203 FreeCAD.ActiveDocument.removeObject(obj.Name)
00204 newobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
00205 newobj.Shape = shape
00206 FreeCAD.ActiveDocument.recompute()
00207 return newobj
00208
00209 def getGroupContents(objectslist):
00210 '''getGroupContents(objectlist): if any object of the given list
00211 is a group, its content is appened to the list, which is returned'''
00212 newlist = []
00213 for obj in objectslist:
00214 if obj.Type == "App::DocumentObjectGroup":
00215 newlist.extend(getGroupContents(obj.Group))
00216 else:
00217 newlist.append(obj)
00218 return newlist
00219
00220 def formatObject(target,origin=None):
00221 '''
00222 formatObject(targetObject,[originObject]): This function applies
00223 to the given target object the current properties
00224 set on the toolbar (line color and line width),
00225 or copies the properties of another object if given as origin.
00226 It also places the object in construction group if needed.
00227 '''
00228 obrep = target.ViewObject
00229 ui = FreeCADGui.draftToolBar
00230 doc = FreeCAD.ActiveDocument
00231 if ui.isConstructionMode():
00232 col = fcol = ui.getDefaultColor("constr")
00233 gname = getParam("constructiongroupname")
00234 if gname:
00235 grp = doc.getObject(gname)
00236 if not grp: grp = doc.addObject("App::DocumentObjectGroup",gname)
00237 grp.addObject(target)
00238 obrep.Transparency = 80
00239 else:
00240 col = ui.getDefaultColor("ui")
00241 fcol = ui.getDefaultColor("face")
00242 col = (float(col[0]),float(col[1]),float(col[2]),0.0)
00243 fcol = (float(fcol[0]),float(fcol[1]),float(fcol[2]),0.0)
00244 lw = ui.linewidth
00245 fs = ui.fontsize
00246 if not origin:
00247 if "FontSize" in obrep.PropertiesList: obrep.FontSize = fs
00248 if "TextColor" in obrep.PropertiesList: obrep.TextColor = col
00249 if "LineWidth" in obrep.PropertiesList: obrep.LineWidth = lw
00250 if "PointColor" in obrep.PropertiesList: obrep.PointColor = col
00251 if "LineColor" in obrep.PropertiesList: obrep.LineColor = col
00252 if "ShapeColor" in obrep.PropertiesList: obrep.ShapeColor = fcol
00253 else:
00254 matchrep = origin.ViewObject
00255 if ("LineWidth" in obrep.PropertiesList) and \
00256 ("LineWidth" in matchrep.PropertiesList):
00257 obrep.LineWidth = matchrep.LineWidth
00258 if ("PointColor" in obrep.PropertiesList) and \
00259 ("PointColor" in matchrep.PropertiesList):
00260 obrep.PointColor = matchrep.PointColor
00261 if ("LineColor" in obrep.PropertiesList) and \
00262 ("LineColor" in matchrep.PropertiesList):
00263 obrep.LineColor = matchrep.LineColor
00264 if ("ShapeColor" in obrep.PropertiesList) and \
00265 ("ShapeColor" in matchrep.PropertiesList):
00266 obrep.ShapeColor = matchrep.ShapeColor
00267 if matchrep.DisplayMode in obrep.listDisplayModes():
00268 obrep.DisplayMode = matchrep.DisplayMode
00269
00270 def getSelection():
00271 "getSelection(): returns the current FreeCAD selection"
00272 return FreeCADGui.Selection.getSelection()
00273
00274 def select(objs):
00275 "select(object): deselects everything and selects only the passed object or list"
00276 FreeCADGui.Selection.clearSelection()
00277 if not isinstance(objs,list):
00278 objs = [objs]
00279 for obj in objs:
00280 FreeCADGui.Selection.addSelection(obj)
00281
00282 def makeCircle(radius, placement=None, face=True, startangle=None, endangle=None, support=None):
00283 '''makeCircle(radius,[placement,face,startangle,endangle]): Creates a circle
00284 object with given radius. If placement is given, it is
00285 used. If face is False, the circle is shown as a
00286 wireframe, otherwise as a face. If startangle AND endangle are given
00287 (in degrees), they are used and the object appears as an arc.'''
00288 if placement: typecheck([(placement,FreeCAD.Placement)], "makeCircle")
00289 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Circle")
00290 Circle(obj)
00291 ViewProviderDraft(obj.ViewObject)
00292 obj.Radius = radius
00293 if not face: obj.ViewObject.DisplayMode = "Wireframe"
00294 if (startangle != None) and (endangle != None):
00295 if startangle == -0: startangle = 0
00296 obj.FirstAngle = startangle
00297 obj.LastAngle = endangle
00298 obj.Support = support
00299 if placement: obj.Placement = placement
00300 formatObject(obj)
00301 select(obj)
00302 FreeCAD.ActiveDocument.recompute()
00303 return obj
00304
00305 def makeRectangle(length, height, placement=None, face=True, support=None):
00306 '''makeRectangle(length,width,[placement],[face]): Creates a Rectangle
00307 object with length in X direction and height in Y direction.
00308 If a placement is given, it is used. If face is False, the
00309 rectangle is shown as a wireframe, otherwise as a face.'''
00310 if placement: typecheck([(placement,FreeCAD.Placement)], "makeRectangle")
00311 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Rectangle")
00312 Rectangle(obj)
00313 ViewProviderRectangle(obj.ViewObject)
00314 obj.Length = length
00315 obj.Height = height
00316 obj.Support = support
00317 if not face: obj.ViewObject.DisplayMode = "Wireframe"
00318 if placement: obj.Placement = placement
00319 formatObject(obj)
00320 select(obj)
00321 FreeCAD.ActiveDocument.recompute()
00322 return obj
00323
00324 def makeDimension(p1,p2,p3=None,p4=None):
00325 '''makeDimension(p1,p2,[p3]) or makeDimension(object,i1,i2,p3)
00326 or makeDimension(objlist,indices,p3): Creates a Dimension object with
00327 the dimension line passign through p3.The current line width and color
00328 will be used. There are multiple ways to create a dimension, depending on
00329 the arguments you pass to it:
00330 - (p1,p2,p3): creates a standard dimension from p1 to p2
00331 - (object,i1,i2,p3): creates a linked dimension to the given object,
00332 measuring the distance between its vertices indexed i1 and i2
00333 - (object,i1,mode,p3): creates a linked dimension
00334 to the given object, i1 is the index of the (curved) edge to measure,
00335 and mode is either "radius" or "diameter".
00336 '''
00337 obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Dimension")
00338 Dimension(obj)
00339 ViewProviderDimension(obj.ViewObject)
00340 if isinstance(p1,Vector) and isinstance(p2,Vector):
00341 obj.Start = p1
00342 obj.End = p2
00343 elif isinstance(p2,int) and isinstance(p3,int):
00344 obj.Base = p1
00345 obj.LinkedVertices = [p2,p3]
00346 p3 = p4
00347 elif isinstance(p3,str):
00348 obj.Base = p1
00349 if p3 == "radius":
00350 obj.LinkedVertices = [p2,1,1]
00351 obj.ViewObject.Override = "rdim"
00352 elif p3 == "diameter":
00353 obj.LinkedVertices = [p2,2,1]
00354 obj.ViewObject.Override = "ddim"
00355 p3 = p4
00356 if not p3:
00357 p3 = p2.sub(p1)
00358 p3.multiply(0.5)
00359 p3 = p1.add(p3)
00360 obj.Dimline = p3
00361 formatObject(obj)
00362 select(obj)
00363 FreeCAD.ActiveDocument.recompute()
00364 return obj
00365
00366 def makeAngularDimension(center,angles,p3):
00367 '''makeAngularDimension(center,[angle1,angle2],p3): creates an angular Dimension
00368 from the given center, with the given list of angles, passing through p3.
00369 '''
00370 obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Dimension")
00371 AngularDimension(obj)
00372 ViewProviderAngularDimension(obj.ViewObject)
00373 obj.Center = center
00374 for a in range(len(angles)):
00375 if angles[a] > 2*math.pi:
00376 angles[a] = angles[a]-(2*math.pi)
00377 obj.FirstAngle = math.degrees(angles[1])
00378 obj.LastAngle = math.degrees(angles[0])
00379 obj.Dimline = p3
00380 formatObject(obj)
00381 select(obj)
00382 FreeCAD.ActiveDocument.recompute()
00383 return obj
00384
00385 def makeWire(pointslist,closed=False,placement=None,face=True,support=None):
00386 '''makeWire(pointslist,[closed],[placement]): Creates a Wire object
00387 from the given list of vectors. If closed is True or first
00388 and last points are identical, the wire is closed. If face is
00389 true (and wire is closed), the wire will appear filled. Instead of
00390 a pointslist, you can also pass a Part Wire.'''
00391 if not isinstance(pointslist,list):
00392 nlist = []
00393 for v in pointslist.Vertexes:
00394 nlist.append(v.Point)
00395 if fcgeo.isReallyClosed(pointslist):
00396 nlist.append(pointslist.Vertexes[0].Point)
00397 pointslist = nlist
00398 if placement: typecheck([(placement,FreeCAD.Placement)], "makeWire")
00399 if len(pointslist) == 2: fname = "Line"
00400 else: fname = "Wire"
00401 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
00402 Wire(obj)
00403 ViewProviderWire(obj.ViewObject)
00404 obj.Points = pointslist
00405 obj.Closed = closed
00406 obj.Support = support
00407 if not face: obj.ViewObject.DisplayMode = "Wireframe"
00408 if placement: obj.Placement = placement
00409 formatObject(obj)
00410 select(obj)
00411 FreeCAD.ActiveDocument.recompute()
00412 return obj
00413
00414 def makePolygon(nfaces,radius=1,inscribed=True,placement=None,face=True,support=None):
00415 '''makePolgon(nfaces,[radius],[inscribed],[placement],[face]): Creates a
00416 polygon object with the given number of faces and the radius.
00417 if inscribed is False, the polygon is circumscribed around a circle
00418 with the given radius, otherwise it is inscribed. If face is True,
00419 the resulting shape is displayed as a face, otherwise as a wireframe.
00420 '''
00421 if nfaces < 3: return None
00422 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Polygon")
00423 Polygon(obj)
00424 ViewProviderDraft(obj.ViewObject)
00425 obj.FacesNumber = nfaces
00426 obj.Radius = radius
00427 if inscribed:
00428 obj.DrawMode = "inscribed"
00429 else:
00430 obj.DrawMode = "circumscribed"
00431 if not face: obj.ViewObject.DisplayMode = "Wireframe"
00432 obj.Support = support
00433 if placement: obj.Placement = placement
00434 formatObject(obj)
00435 select(obj)
00436 FreeCAD.ActiveDocument.recompute()
00437 return obj
00438
00439 def makeLine(p1,p2):
00440 '''makeLine(p1,p2): Creates a line between p1 and p2.'''
00441 obj = makeWire([p1,p2])
00442 return obj
00443
00444 def makeBSpline(pointslist,closed=False,placement=None,face=True,support=None):
00445 '''makeBSpline(pointslist,[closed],[placement]): Creates a B-Spline object
00446 from the given list of vectors. If closed is True or first
00447 and last points are identical, the wire is closed. If face is
00448 true (and wire is closed), the wire will appear filled. Instead of
00449 a pointslist, you can also pass a Part Wire.'''
00450 if not isinstance(pointslist,list):
00451 nlist = []
00452 for v in pointslist.Vertexes:
00453 nlist.append(v.Point)
00454 pointslist = nlist
00455 if placement: typecheck([(placement,FreeCAD.Placement)], "makeBSpline")
00456 if len(pointslist) == 2: fname = "Line"
00457 else: fname = "BSpline"
00458 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
00459 BSpline(obj)
00460 ViewProviderBSpline(obj.ViewObject)
00461 obj.Points = pointslist
00462 obj.Closed = closed
00463 obj.Support = support
00464 if not face: obj.ViewObject.DisplayMode = "Wireframe"
00465 if placement: obj.Placement = placement
00466 formatObject(obj)
00467 select(obj)
00468 FreeCAD.ActiveDocument.recompute()
00469 return obj
00470
00471 def makeText(stringslist,point=Vector(0,0,0),screen=False):
00472 '''makeText(strings,[point],[screen]): Creates a Text object at the given point,
00473 containing the strings given in the strings list, one string by line (strings
00474 can also be one single string). The current color and text height and font
00475 specified in preferences are used.
00476 If screen is True, the text always faces the view direction.'''
00477 typecheck([(point,Vector)], "makeText")
00478 if not isinstance(stringslist,list): stringslist = [stringslist]
00479 textbuffer = []
00480 for l in stringslist: textbuffer.append(unicode(l).encode('utf-8'))
00481 obj=FreeCAD.ActiveDocument.addObject("App::Annotation","Text")
00482 obj.LabelText=textbuffer
00483 obj.Position=point
00484 if not screen: obj.ViewObject.DisplayMode="World"
00485 h = getParam("textheight")
00486 if screen: h = h*10
00487 obj.ViewObject.FontSize = h
00488 obj.ViewObject.FontName = getParam("textfont")
00489 obj.ViewObject.LineSpacing = 0.6
00490 formatObject(obj)
00491 select(obj)
00492 return obj
00493
00494 def makeCopy(obj):
00495 '''makeCopy(object): returns an exact copy of an object'''
00496 newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
00497 if getType(obj) == "Rectangle":
00498 Rectangle(newobj)
00499 ViewProviderRectangle(newobj.ViewObject)
00500 elif getType(obj) == "Wire":
00501 Wire(newobj)
00502 ViewProviderWire(newobj.ViewObject)
00503 elif getType(obj) == "Circle":
00504 Circle(newobj)
00505 ViewProviderCircle(newobj.ViewObject)
00506 elif getType(obj) == "Polygon":
00507 Polygon(newobj)
00508 ViewProviderPolygon(newobj.ViewObject)
00509 elif getType(obj) == "BSpline":
00510 BSpline(newobj)
00511 ViewProviderBSpline(newobj.ViewObject)
00512 elif getType(obj) == "Block":
00513 Block(newobj)
00514 ViewProviderBlock(newobj.ViewObject)
00515 elif getType(obj) == "Structure":
00516 import Structure
00517 Structure.Structure(newobj)
00518 Structure.ViewProviderStructure(newobj.ViewObject)
00519 elif getType(obj) == "Wall":
00520 import Wall
00521 Wall.Wall(newobj)
00522 Wall.ViewProviderWall(newobj.ViewObject)
00523 elif obj.isDerivedFrom("Part::Feature"):
00524 newobj.Shape = obj.Shape
00525 else:
00526 print "Error: Object type cannot be copied"
00527 return None
00528 for p in obj.PropertiesList:
00529 if p in newobj.PropertiesList:
00530 setattr(newobj,p,obj.getPropertyByName(p))
00531 formatObject(newobj,obj)
00532 return newobj
00533
00534 def makeBlock(objectslist):
00535 '''makeBlock(objectslist): Creates a Draft Block from the given objects'''
00536 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Block")
00537 Block(obj)
00538 ViewProviderBlock(obj.ViewObject)
00539 obj.Components = objectslist
00540 for o in objectslist:
00541 o.ViewObject.Visibility = False
00542 select(obj)
00543 return obj
00544
00545 def extrude(obj,vector):
00546 '''makeExtrusion(object,vector): extrudes the given object
00547 in the direction given by the vector. The original object
00548 gets hidden.'''
00549 newobj = FreeCAD.ActiveDocument.addObject("Part::Extrusion","Extrusion")
00550 newobj.Base = obj
00551 newobj.Dir = vector
00552 obj.ViewObject.Visibility = False
00553 formatObject(newobj,obj)
00554 FreeCAD.ActiveDocument.recompute()
00555 return newobj
00556
00557 def fuse(object1,object2):
00558 '''fuse(oject1,object2): returns an object made from
00559 the union of the 2 given objects. If the objects are
00560 coplanar, a special Draft Wire is used, otherwise we use
00561 a standard Part fuse.'''
00562 if fcgeo.isCoplanar(object1.Shape.fuse(object2.Shape).Faces):
00563 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Fusion")
00564 Wire(obj)
00565 ViewProviderWire(obj.ViewObject)
00566 else:
00567 obj = FreeCAD.ActiveDocument.addObject("Part::Fuse","Fusion")
00568 obj.Base = object1
00569 obj.Tool = object2
00570 object1.ViewObject.Visibility = False
00571 object2.ViewObject.Visibility = False
00572 formatObject(obj,object1)
00573 FreeCAD.ActiveDocument.recompute()
00574 return obj
00575
00576 def cut(object1,object2):
00577 '''cut(oject1,object2): returns a cut object made from
00578 the difference of the 2 given objects.'''
00579 obj = FreeCAD.ActiveDocument.addObject("Part::Cut","Cut")
00580 obj.Base = object1
00581 obj.Tool = object2
00582 object1.ViewObject.Visibility = False
00583 object2.ViewObject.Visibility = False
00584 formatObject(obj,object1)
00585 FreeCAD.ActiveDocument.recompute()
00586 return obj
00587
00588 def move(objectslist,vector,copy=False):
00589 '''move(objects,vector,[copy]): Moves the objects contained
00590 in objects (that can be an object or a list of objects)
00591 in the direction and distance indicated by the given
00592 vector. If copy is True, the actual objects are not moved, but copies
00593 are created instead.he objects (or their copies) are returned.'''
00594 typecheck([(vector,Vector), (copy,bool)], "move")
00595 if not isinstance(objectslist,list): objectslist = [objectslist]
00596 newobjlist = []
00597 for obj in objectslist:
00598 if (obj.isDerivedFrom("Part::Feature")):
00599 if copy:
00600 newobj = makeCopy(obj)
00601 else:
00602 newobj = obj
00603 pla = newobj.Placement
00604 pla.move(vector)
00605 elif getType(obj) == "Annotation":
00606 if copy:
00607 newobj = FreeCAD.ActiveDocument.addObject("App::Annotation",getRealName(obj.Name))
00608 newobj.LabelText = obj.LabelText
00609 else:
00610 newobj = obj
00611 newobj.Position = obj.Position.add(vector)
00612 elif getType(obj) == "Dimension":
00613 if copy:
00614 newobj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",getRealName(obj.Name))
00615 Dimension(newobj)
00616 DimensionViewProvider(newobj.ViewObject)
00617 else:
00618 newobj = obj
00619 newobj.Start = obj.Start.add(vector)
00620 newobj.End = obj.End.add(vector)
00621 newobj.Dimline = obj.Dimline.add(vector)
00622 else:
00623 if copy: print "Mesh copy not supported at the moment"
00624 newobj = obj
00625 if "Placement" in obj.PropertiesList:
00626 pla = obj.Placement
00627 pla.move(vector)
00628 newobjlist.append(newobj)
00629 if copy and getParam("selectBaseObjects"):
00630 select(objectslist)
00631 else:
00632 select(newobjlist)
00633 if len(newobjlist) == 1: return newobjlist[0]
00634 return newobjlist
00635
00636 def array(objectslist,arg1,arg2,arg3,arg4=None):
00637 '''array(objectslist,xvector,yvector,xnum,ynum) for rectangular array, or
00638 array(objectslist,center,totalangle,totalnum) for polar array: Creates an array
00639 of the objects contained in list (that can be an object or a list of objects)
00640 with, in case of rectangular array, xnum of iterations in the x direction
00641 at xvector distance between iterations, and same for y direction with yvector
00642 and ynum. In case of polar array, center is a vector, totalangle is the angle
00643 to cover (in degrees) and totalnum is the number of objects, including the original.'''
00644
00645 def rectArray(objectslist,xvector,yvector,xnum,ynum):
00646 typecheck([(xvector,Vector), (yvector,Vector), (xnum,int), (ynum,int)], "rectArray")
00647 if not isinstance(objectslist,list): objectslist = [objectslist]
00648 for xcount in range(xnum):
00649 currentxvector=fcvec.scale(xvector,xcount)
00650 if not xcount==0:
00651 move(objectslist,currentxvector,True)
00652 for ycount in range(ynum):
00653 currentxvector=FreeCAD.Base.Vector(currentxvector)
00654 currentyvector=currentxvector.add(fcvec.scale(yvector,ycount))
00655 if not ycount==0:
00656 move(objectslist,currentyvector,True)
00657 def polarArray(objectslist,center,angle,num):
00658 typecheck([(center,Vector), (num,int)], "polarArray")
00659 if not isinstance(objectslist,list): objectslist = [objectslist]
00660 fraction = angle/num
00661 for i in range(num):
00662 currangle = fraction + (i*fraction)
00663 rotate(objectslist,currangle,center,copy=True)
00664
00665 if arg4:
00666 rectArray(objectslist,arg1,arg2,arg3,arg4)
00667 else:
00668 polarArray(objectslist,arg1,arg2,arg3)
00669
00670 def rotate(objectslist,angle,center=Vector(0,0,0),axis=Vector(0,0,1),copy=False):
00671 '''rotate(objects,angle,[center,axis,copy]): Rotates the objects contained
00672 in objects (that can be a list of objects or an object) of the given angle
00673 (in degrees) around the center, using axis as a rotation axis. If axis is
00674 omitted, the rotation will be around the vertical Z axis.
00675 If copy is True, the actual objects are not moved, but copies
00676 are created instead. The objects (or their copies) are returned.'''
00677 typecheck([(copy,bool)], "rotate")
00678 if not isinstance(objectslist,list): objectslist = [objectslist]
00679 newobjlist = []
00680 for obj in objectslist:
00681 if copy:
00682 newobj = makeCopy(obj)
00683 else:
00684 newobj = obj
00685 if (obj.isDerivedFrom("Part::Feature")):
00686 shape = obj.Shape.copy()
00687 shape.rotate(fcvec.tup(center), fcvec.tup(axis), angle)
00688 newobj.Shape = shape
00689 elif hasattr(obj,"Placement"):
00690 shape = Part.Shape()
00691 shape.Placement = obj.Placement
00692 shape.rotate(fcvec.tup(center), fcvec.tup(axis), angle)
00693 newobj.Placement = shape.Placement
00694 if copy:
00695 formatObject(newobj,obj)
00696 newobjlist.append(newobj)
00697 if copy and getParam("selectBaseObjects"):
00698 select(objectslist)
00699 else:
00700 select(newobjlist)
00701 if len(newobjlist) == 1: return newobjlist[0]
00702 return newobjlist
00703
00704
00705 def scale(objectslist,delta,center=Vector(0,0,0),copy=False):
00706 '''scale(objects,vector,[center,copy]): Scales the objects contained
00707 in objects (that can be a list of objects or an object) of the given scale
00708 factors defined by the given vector (in X, Y and Z directions) around
00709 given center. If copy is True, the actual objects are not moved, but copies
00710 are created instead. The objects (or their copies) are returned.'''
00711 if not isinstance(objectslist,list): objectslist = [objectslist]
00712 newobjlist = []
00713 for obj in objectslist:
00714 if copy:
00715 newobj = makeCopy(obj)
00716 else:
00717 newobj = obj
00718 sh = obj.Shape.copy()
00719 m = FreeCAD.Matrix()
00720 m.scale(delta)
00721 sh = sh.transformGeometry(m)
00722 corr = Vector(center.x,center.y,center.z)
00723 corr.scale(delta.x,delta.y,delta.z)
00724 corr = fcvec.neg(corr.sub(center))
00725 sh.translate(corr)
00726 if getType(obj) == "Rectangle":
00727 p = []
00728 for v in sh.Vertexes: p.append(v.Point)
00729 pl = obj.Placement.copy()
00730 pl.Base = p[0]
00731 diag = p[2].sub(p[0])
00732 bb = p[1].sub(p[0])
00733 bh = p[3].sub(p[0])
00734 nb = fcvec.project(diag,bb)
00735 nh = fcvec.project(diag,bh)
00736 if obj.Length < 0: l = -nb.Length
00737 else: l = nb.Length
00738 if obj.Height < 0: h = -nh.Length
00739 else: h = nh.Length
00740 newobj.Length = l
00741 newobj.Height = h
00742 tr = p[0].sub(obj.Shape.Vertexes[0].Point)
00743 newobj.Placement = pl
00744 elif getType(obj) == "Wire":
00745 p = []
00746 for v in sh.Vertexes: p.append(v.Point)
00747 newobj.Points = p
00748 elif (obj.isDerivedFrom("Part::Feature")):
00749 newobj.Shape = sh
00750 elif (obj.Type == "App::Annotation"):
00751 factor = delta.x * delta.y * delta.z * obj.ViewObject.FontSize
00752 obj.ViewObject.Fontsize = factor
00753 if copy: formatObject(newobj,obj)
00754 newobjlist.append(newobj)
00755 if copy and getParam("selectBaseObjects"):
00756 select(objectslist)
00757 else:
00758 select(newobjlist)
00759 if len(newobjlist) == 1: return newobjlist[0]
00760 return newobjlist
00761
00762 def offset(obj,delta,copy=False,bind=False,sym=False,occ=False):
00763 '''offset(object,Vector,[copymode],[bind]): offsets the given wire by
00764 applying the given Vector to its first vertex. If copymode is
00765 True, another object is created, otherwise the same object gets
00766 offsetted. If bind is True, and provided the wire is open, the original
00767 and the offsetted wires will be bound by their endpoints, forming a face
00768 if sym is True, bind must be true too, and the offset is made on both
00769 sides, the total width being the given delta length.'''
00770
00771 def getRect(p,obj):
00772 "returns length,heigh,placement"
00773 pl = obj.Placement.copy()
00774 pl.Base = p[0]
00775 diag = p[2].sub(p[0])
00776 bb = p[1].sub(p[0])
00777 bh = p[3].sub(p[0])
00778 nb = fcvec.project(diag,bb)
00779 nh = fcvec.project(diag,bh)
00780 if obj.Length < 0: l = -nb.Length
00781 else: l = nb.Length
00782 if obj.Height < 0: h = -nh.Length
00783 else: h = nh.Length
00784 return l,h,pl
00785
00786 def getRadius(obj,delta):
00787 "returns a new radius for a regular polygon"
00788 an = math.pi/obj.FacesNumber
00789 nr = fcvec.rotate(delta,-an)
00790 nr.multiply(1/math.cos(an))
00791 nr = obj.Shape.Vertexes[0].Point.add(nr)
00792 nr = nr.sub(obj.Placement.Base)
00793 nr = nr.Length
00794 if obj.DrawMode == "inscribed":
00795 return nr
00796 else:
00797 return nr * math.cos(math.pi/obj.FacesNumber)
00798
00799 if getType(obj) == "Circle":
00800 pass
00801 else:
00802 if sym:
00803 d1 = delta.multiply(0.5)
00804 d2 = fcvec.neg(d1)
00805 n1 = fcgeo.offsetWire(obj.Shape,d1)
00806 n2 = fcgeo.offsetWire(obj.Shape,d2)
00807 else:
00808 newwire = fcgeo.offsetWire(obj.Shape,delta)
00809 p = fcgeo.getVerts(newwire)
00810 if occ:
00811 newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset")
00812 newobj.Shape = fcgeo.offsetWire(obj.Shape,delta,occ=True)
00813 formatObject(newobj,obj)
00814 elif bind:
00815 if not fcgeo.isReallyClosed(obj.Shape):
00816 if sym:
00817 s1 = n1
00818 s2 = n2
00819 else:
00820 s1 = obj.Shape
00821 s2 = newwire
00822 w1 = s1.Edges
00823 w2 = s2.Edges
00824 w3 = Part.Line(s1.Vertexes[0].Point,s2.Vertexes[0].Point).toShape()
00825 w4 = Part.Line(s1.Vertexes[-1].Point,s2.Vertexes[-1].Point).toShape()
00826 newobj = Part.Face(Part.Wire(w1+[w3]+w2+[w4]))
00827 else:
00828 newobj = Part.Face(obj.Shape.Wires[0])
00829 elif copy:
00830 if sym: return None
00831 if getType(obj) == "Wire":
00832 newobj = makeWire(p)
00833 newobj.Closed = obj.Closed
00834 elif getType(obj) == "Rectangle":
00835 length,height,plac = getRect(p,obj)
00836 newobj = makeRectangle(length,height,plac)
00837 elif getType(obj) == "Circle":
00838 pl = obj.Placement
00839 newobj = makeCircle(delta)
00840 newobj.FirstAngle = obj.FirstAngle
00841 newobj.LastAngle = obj.LastAngle
00842 newobj.Placement = pl
00843 elif getType(obj) == "Polygon":
00844 pl = obj.Placement
00845 newobj = makePolygon(obj.FacesNumber)
00846 newobj.Radius = getRadius(obj,delta)
00847 newobj.DrawMode = obj.DrawMode
00848 newobj.Placement = pl
00849 elif getType(obj) == "Part":
00850 newobj = makeWire(p)
00851 newobj.Closed = obj.Shape.isClosed()
00852 formatObject(newobj,obj)
00853 else:
00854 if sym: return None
00855 if getType(obj) == "Wire":
00856 if obj.Base or obj.Tool:
00857 FreeCAD.Console.PrintWarning("Warning: object history removed\n")
00858 obj.Base = None
00859 obj.Tool = None
00860 obj.Points = p
00861 elif getType(obj) == "Rectangle":
00862 length,height,plac = getRect(p,obj)
00863 obj.Placement = plac
00864 obj.Length = length
00865 obj.Height = height
00866 elif getType(obj) == "Circle":
00867 obj.Radius = delta
00868 elif getType(obj) == "Polygon":
00869 obj.Radius = getRadius(obj,delta)
00870 elif getType(obj) == 'Part':
00871 print "unsupported object"
00872 newobj = obj
00873 if copy and getParam("selectBaseObjects"):
00874 select(newobj)
00875 else:
00876 select(obj)
00877 return newobj
00878
00879 def draftify(objectslist):
00880 '''draftify(objectslist): turns each object of the given list
00881 (objectslist can also be a single object) into a Draft parametric
00882 wire'''
00883 if not isinstance(objectslist,list): objectslist = [objectslist]
00884 newobjlist = []
00885 for obj in objectslist:
00886 if obj.isDerivedFrom('Part::Feature'):
00887 for w in obj.Shape.Wires:
00888 verts = []
00889 edges = fcgeo.sortEdges(w.Edges)
00890 for e in edges:
00891 verts.append(e.Vertexes[0].Point)
00892 if w.isClosed():
00893 verts.append(edges[-1].Vertexes[-1].Point)
00894 newobj = makeWire(verts)
00895 if obj.Shape.Faces:
00896 newobj.Closed = True
00897 newobj.ViewObject.DisplayMode = "Flat Lines"
00898 else:
00899 newobj.ViewObject.DisplayMode = "Wireframe"
00900 if obj.Shape.Wires[0].isClosed:
00901 newobj.Closed = True
00902 newobjlist.append(newobj)
00903 FreeCAD.ActiveDocument.removeObject(obj.Name)
00904 if len(newobjlist) == 1: return newobjlist[0]
00905 return newobjlist
00906
00907 def getrgb(color):
00908 "getRGB(color): returns a rgb value #000000 from a freecad color"
00909 r = str(hex(int(color[0]*255)))[2:].zfill(2)
00910 g = str(hex(int(color[1]*255)))[2:].zfill(2)
00911 b = str(hex(int(color[2]*255)))[2:].zfill(2)
00912 return "#"+r+g+b
00913
00914 def getSVG(obj,modifier=100,textmodifier=100,linestyle="continuous",fillstyle="shape color",direction=None):
00915 '''getSVG(object,[modifier],[textmodifier],[linestyle],[fillstyle],[direction]):
00916 returns a string containing a SVG representation of the given object. the modifier attribute
00917 specifies a scale factor for linewidths in %, and textmodifier specifies
00918 a scale factor for texts, in % (both default = 100). You can also supply
00919 an arbitrary projection vector.'''
00920 svg = ""
00921 tmod = ((textmodifier-100)/2)+100
00922 if tmod == 0: tmod = 0.01
00923 modifier = 200-modifier
00924 if modifier == 0: modifier = 0.01
00925 pmod = (200-textmodifier)/20
00926 if pmod == 0: pmod = 0.01
00927 plane = None
00928 if direction:
00929 if direction != Vector(0,0,0):
00930 plane = WorkingPlane.plane()
00931 plane.alignToPointAndAxis(Vector(0,0,0),fcvec.neg(direction),0)
00932
00933 def getProj(vec):
00934 if not plane: return vec
00935 nx = fcvec.project(vec,plane.u)
00936 lx = nx.Length
00937 if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx
00938 ny = fcvec.project(vec,plane.v)
00939 ly = ny.Length
00940 if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly
00941 return Vector(lx,ly,0)
00942
00943 def getPath(edges):
00944 svg ='<path id="' + name + '" '
00945 edges = fcgeo.sortEdges(edges)
00946 v = getProj(edges[0].Vertexes[0].Point)
00947 svg += 'd="M '+ str(v.x) +' '+ str(v.y) + ' '
00948 for e in edges:
00949 if isinstance(e.Curve,Part.Line) or isinstance(e.Curve,Part.BSplineCurve):
00950 v = getProj(e.Vertexes[-1].Point)
00951 svg += 'L '+ str(v.x) +' '+ str(v.y) + ' '
00952 elif isinstance(e.Curve,Part.Circle):
00953 r = e.Curve.Radius
00954 v = getProj(e.Vertexes[-1].Point)
00955 svg += 'A '+ str(r) + ' '+ str(r) +' 0 0 1 '+ str(v.x) +' '
00956 svg += str(v.y) + ' '
00957 if fill != 'none': svg += 'Z'
00958 svg += '" '
00959 svg += 'stroke="' + stroke + '" '
00960 svg += 'stroke-width="' + str(width) + ' px" '
00961 svg += 'style="stroke-width:'+ str(width)
00962 svg += ';stroke-miterlimit:4'
00963 svg += ';stroke-dasharray:' + lstyle
00964 svg += ';fill:' + fill + '"'
00965 svg += '/>\n'
00966 return svg
00967
00968 if getType(obj) == "Dimension":
00969 p1,p2,p3,p4,tbase,norm,rot = obj.ViewObject.Proxy.calcGeom(obj)
00970 dimText = getParam("dimPrecision")
00971 dimText = "%."+str(dimText)+"f"
00972 p1 = getProj(p1)
00973 p2 = getProj(p2)
00974 p3 = getProj(p3)
00975 p4 = getProj(p4)
00976 tbase = getProj(tbase)
00977 svg = '<g id="'+obj.Name+'"><path '
00978 if obj.ViewObject.DisplayMode == "2D":
00979 m = FreeCAD.Placement()
00980 m.Rotation.Q = rot
00981 m = m.toMatrix()
00982 if plane:
00983 vtext = m.multiply(plane.u)
00984 else:
00985 vtext = m.multiply(Vector(1,0,0))
00986 angle = -fcvec.angle(vtext)
00987 svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' '
00988 svg += 'L '+str(p2.x)+' '+str(p2.y)+' '
00989 svg += 'L '+str(p3.x)+' '+str(p3.y)+' '
00990 svg += 'L '+str(p4.x)+' '+str(p4.y)+'" '
00991 else:
00992 ts = (len(dimText)*obj.ViewObject.FontSize)/4
00993 rm = ((p3.sub(p2)).Length/2)-ts
00994 p2a = getProj(p2.add(fcvec.scaleTo(p3.sub(p2),rm)))
00995 p2b = getProj(p3.add(fcvec.scaleTo(p2.sub(p3),rm)))
00996 angle = 0
00997 svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' '
00998 svg += 'L '+str(p2.x)+' '+str(p2.y)+' '
00999 svg += 'L '+str(p2a.x)+' '+str(p2a.y)+' '
01000 svg += 'M '+str(p2b.x)+' '+str(p2b.y)+' '
01001 svg += 'L '+str(p3.x)+' '+str(p3.y)+' '
01002 svg += 'L '+str(p4.x)+' '+str(p4.y)+'" '
01003 svg += 'fill="none" stroke="'
01004 svg += getrgb(obj.ViewObject.LineColor) + '" '
01005 svg += 'stroke-width="' + str(obj.ViewObject.LineWidth/modifier) + ' px" '
01006 svg += 'style="stroke-width:'+ str(obj.ViewObject.LineWidth/modifier)
01007 svg += ';stroke-miterlimit:4;stroke-dasharray:none" '
01008 svg += 'freecad:basepoint1="'+str(p1.x)+' '+str(p1.y)+'" '
01009 svg += 'freecad:basepoint2="'+str(p4.x)+' '+str(p4.y)+'" '
01010 svg += 'freecad:dimpoint="'+str(p2.x)+' '+str(p2.y)+'"'
01011 svg += '/>\n'
01012 svg += '<circle cx="'+str(p2.x)+'" cy="'+str(p2.y)
01013 svg += '" r="'+str(obj.ViewObject.FontSize/(pmod))+'" '
01014 svg += 'fill="'+ getrgb(obj.ViewObject.LineColor) +'" stroke="none" '
01015 svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
01016 svg += 'freecad:skip="1"'
01017 svg += '/>\n'
01018 svg += '<circle cx="'+str(p3.x)+'" cy="'+str(p3.y)
01019 svg += '" r="'+str(obj.ViewObject.FontSize/(pmod))+'" '
01020 svg += 'fill="#000000" stroke="none" '
01021 svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
01022 svg += 'freecad:skip="1"'
01023 svg += '/>\n'
01024 svg += '<text id="' + obj.Name + '" fill="'
01025 svg += getrgb(obj.ViewObject.LineColor) +'" font-size="'
01026 svg += str(obj.ViewObject.FontSize*(tmod/5))+'" '
01027 svg += 'style="text-anchor:middle;text-align:center;'
01028 svg += 'font-family:'+obj.ViewObject.FontName+'" '
01029 svg += 'transform="rotate('+str(math.degrees(angle))
01030 svg += ','+ str(tbase.x) + ',' + str(tbase.y) + ') '
01031 svg += 'translate(' + str(tbase.x) + ',' + str(tbase.y) + ') '
01032 svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+')" '
01033 svg += 'freecad:skip="1"'
01034 svg += '>\n'
01035 svg += dimText % p3.sub(p2).Length
01036 svg += '</text>\n</g>\n'
01037
01038 elif getType(obj) == "Annotation":
01039 "returns an svg representation of a document annotation"
01040 p = getProj(obj.Position)
01041 svg = '<text id="' + obj.Name + '" fill="'
01042 svg += getrgb(obj.ViewObject.TextColor)
01043 svg += '" font-size="'
01044 svg += str(obj.ViewObject.FontSize*(tmod/5))+'" '
01045 svg += 'style="text-anchor:middle;text-align:center;'
01046 svg += 'font-family:'+obj.ViewObject.FontName+'" '
01047 svg += 'transform="'
01048 if obj.ViewObject.RotationAxis == 'Z':
01049 if obj.ViewObject.Rotation != 0:
01050 svg += 'rotate('+str(obj.ViewObject.Rotation)
01051 svg += ','+ str(p.x) + ',' + str(p.y) + ') '
01052 svg += 'translate(' + str(p.x) + ',' + str(p.y) + ') '
01053 svg +='scale('+str(tmod/2000)+','+str(-tmod/2000)+')">\n'
01054 for l in obj.LabelText:
01055 svg += '<tspan>'+l+'</tspan>\n'
01056 svg += '</text>\n'
01057
01058 elif obj.isDerivedFrom('Part::Feature'):
01059 if obj.Shape.isNull(): return ''
01060 color = getrgb(obj.ViewObject.LineColor)
01061
01062 if obj.Shape.Faces and (obj.ViewObject.DisplayMode != "Wireframe"):
01063 if fillstyle == "shape color":
01064 fill = getrgb(obj.ViewObject.ShapeColor)
01065 else:
01066 fill = 'url(#'+hatch+')'
01067 else:
01068 fill = 'none'
01069
01070 if linestyle == "dashed":
01071 lstyle = "0.09,0.05"
01072 elif linestyle == "dashdotted":
01073 lstyle = "0.09,0.05,0.02,0.05"
01074 elif linestyle == "dotted":
01075 lstyle = "0.02,0.02"
01076 else:
01077 lstyle = "none"
01078 name = obj.Name
01079 if obj.ViewObject.DisplayMode == "Shaded":
01080 stroke = "none"
01081 else:
01082 stroke = getrgb(obj.ViewObject.LineColor)
01083 width = obj.ViewObject.LineWidth/modifier
01084 if len(obj.Shape.Vertexes) > 1:
01085 wiredEdges = []
01086 if obj.Shape.Faces:
01087 for f in obj.Shape.Faces:
01088 svg += getPath(f.Edges)
01089 wiredEdges.extend(f.Edges)
01090 else:
01091 for w in obj.Shape.Wires:
01092 svg += getPath(w.Edges)
01093 wiredEdges.extend(w.Edges)
01094 if len(wiredEdges) != len(obj.Shape.Edges):
01095 for e in obj.Shape.Edges:
01096 if (fcgeo.findEdge(e,wiredEdges) == None):
01097 svg += getPath([e])
01098 else:
01099 cen = getProj(obj.Shape.Edges[0].Curve.Center)
01100 rad = obj.Shape.Edges[0].Curve.Radius
01101 svg = '<circle cx="' + str(cen.x)
01102 svg += '" cy="' + str(cen.y)
01103 svg += '" r="' + str(rad)+'" '
01104 svg += 'stroke="' + stroke + '" '
01105 svg += 'stroke-width="' + str(width) + ' px" '
01106 svg += 'style="stroke-width:'+ str(width)
01107 svg += ';stroke-miterlimit:4'
01108 svg += ';stroke-dasharray:' + lstyle
01109 svg += ';fill:' + fill + '"'
01110 svg += '/>\n'
01111 return svg
01112
01113 def makeDrawingView(obj,page,lwmod=None,tmod=None):
01114 '''
01115 makeDrawingView(object,page,[lwmod,tmod]) - adds a View of the given object to the
01116 given page. lwmod modifies lineweights (in percent), tmod modifies text heights
01117 (in percent). The Hint scale, X and Y of the page are used.
01118 '''
01119 viewobj = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View"+obj.Name)
01120 DrawingView(viewobj)
01121 page.addObject(viewobj)
01122 viewobj.Scale = page.ViewObject.HintScale
01123 viewobj.X = page.ViewObject.HintOffsetX
01124 viewobj.Y = page.ViewObject.HintOffsetY
01125 viewobj.Source = obj
01126 if lwmod: viewobj.LineweightModifier = lwmod
01127 if tmod: viewobj.TextModifier = tmod
01128 return viewobj
01129
01130 def makeShape2DView(baseobj,projectionVector=None):
01131 '''
01132 makeShape2DView(object,[projectionVector]) - adds a 2D shape to the document, which is a
01133 2D projection of the given object. A specific projection vector can also be given.
01134 '''
01135 obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Shape2DView")
01136 Shape2DView(obj)
01137 ViewProviderDraft(obj.ViewObject)
01138 obj.Base = baseobj
01139 if projectionVector:
01140 obj.Projection = projectionVector
01141 select(obj)
01142 return obj
01143
01144
01145
01146
01147
01148 class ViewProviderDraft:
01149 "A generic View Provider for Draft objects"
01150
01151 def __init__(self, obj):
01152 obj.Proxy = self
01153
01154 def attach(self, obj):
01155 self.Object = obj.Object
01156 return
01157
01158 def updateData(self, fp, prop):
01159 return
01160
01161 def getDisplayModes(self,obj):
01162 modes=[]
01163 return modes
01164
01165 def setDisplayMode(self,mode):
01166 return mode
01167
01168 def onChanged(self, vp, prop):
01169 return
01170
01171 def __getstate__(self):
01172 return None
01173
01174 def __setstate__(self,state):
01175 return None
01176
01177 def setEdit(self,vp,mode):
01178 FreeCADGui.runCommand("Draft_Edit")
01179 return True
01180
01181 def unsetEdit(self,vp,mode):
01182 if FreeCAD.activeDraftCommand:
01183 FreeCAD.activeDraftCommand.finish()
01184 return
01185
01186 def getIcon(self):
01187 return(":/icons/Draft_Draft.svg")
01188
01189 class Dimension:
01190 "The Dimension object"
01191 def __init__(self, obj):
01192 obj.addProperty("App::PropertyVector","Start","Base",
01193 "Startpoint of dimension")
01194 obj.addProperty("App::PropertyVector","End","Base",
01195 "Endpoint of dimension")
01196 obj.addProperty("App::PropertyVector","Dimline","Base",
01197 "Point through which the dimension line passes")
01198 obj.addProperty("App::PropertyLink","Base","Base",
01199 "The base object this dimension is linked to")
01200 obj.addProperty("App::PropertyIntegerList","LinkedVertices","Base",
01201 "The indices of the vertices from the base object to measure")
01202 obj.Start = FreeCAD.Vector(0,0,0)
01203 obj.End = FreeCAD.Vector(1,0,0)
01204 obj.Dimline = FreeCAD.Vector(0,1,0)
01205 obj.Proxy = self
01206 self.Type = "Dimension"
01207
01208 def onChanged(self, obj, prop):
01209 pass
01210
01211 def execute(self, obj):
01212 if obj.ViewObject:
01213 obj.ViewObject.update()
01214
01215 class ViewProviderDimension:
01216 "A View Provider for the Dimension object"
01217 def __init__(self, obj):
01218 obj.addProperty("App::PropertyLength","FontSize","Base","Font size")
01219 obj.addProperty("App::PropertyString","FontName","Base","Font name")
01220 obj.addProperty("App::PropertyLength","LineWidth","Base","Line width")
01221 obj.addProperty("App::PropertyColor","LineColor","Base","Line color")
01222 obj.addProperty("App::PropertyLength","ExtLines","Base","Ext lines")
01223 obj.addProperty("App::PropertyVector","Position","Base","The position of the text. Leave (0,0,0) for automatic position")
01224 obj.addProperty("App::PropertyString","Override","Base","Text override. Use 'dim' to insert the dimension length")
01225 obj.Proxy = self
01226 obj.FontSize=getParam("textheight")
01227 obj.FontName=getParam("textfont")
01228 obj.ExtLines=0.3
01229 obj.Override = ''
01230
01231 def calcGeom(self,obj):
01232 p1 = obj.Start
01233 p4 = obj.End
01234 base = Part.Line(p1,p4).toShape()
01235 proj = fcgeo.findDistance(obj.Dimline,base)
01236 if not proj:
01237 p2 = p1
01238 p3 = p4
01239 else:
01240 p2 = p1.add(fcvec.neg(proj))
01241 p3 = p4.add(fcvec.neg(proj))
01242 dmax = obj.ViewObject.ExtLines
01243 if dmax and (proj.Length > dmax):
01244 p1 = p2.add(fcvec.scaleTo(proj,dmax))
01245 p4 = p3.add(fcvec.scaleTo(proj,dmax))
01246 midpoint = p2.add(fcvec.scale(p3.sub(p2),0.5))
01247 if not proj:
01248 ed = fcgeo.vec(base)
01249 proj = ed.cross(Vector(0,0,1))
01250 if not proj: norm = Vector(0,0,1)
01251 else: norm = fcvec.neg(p3.sub(p2).cross(proj))
01252 norm.normalize()
01253 va = FreeCADGui.ActiveDocument.ActiveView.getViewDirection()
01254 if va.getAngle(norm) < math.pi/2:
01255 norm = fcvec.neg(norm)
01256 u = p3.sub(p2)
01257 u.normalize()
01258 c = FreeCADGui.ActiveDocument.ActiveView.getCameraNode()
01259 r = c.orientation.getValue()
01260 ru = Vector(r.multVec(coin.SbVec3f(1,0,0)).getValue())
01261 if ru.getAngle(u) > math.pi/2: u = fcvec.neg(u)
01262 v = norm.cross(u)
01263 offset = fcvec.scaleTo(v,obj.ViewObject.FontSize*.2)
01264 if obj.ViewObject:
01265 if hasattr(obj.ViewObject,"DisplayMode"):
01266 if obj.ViewObject.DisplayMode == "3D":
01267 offset = fcvec.neg(offset)
01268 if obj.ViewObject.Position == Vector(0,0,0):
01269 tbase = midpoint.add(offset)
01270 else:
01271 tbase = obj.ViewObject.Position
01272 rot = FreeCAD.Placement(fcvec.getPlaneRotation(u,v,norm)).Rotation.Q
01273 return p1,p2,p3,p4,tbase,norm,rot
01274
01275 def attach(self, obj):
01276 self.Object = obj.Object
01277 p1,p2,p3,p4,tbase,norm,rot = self.calcGeom(obj.Object)
01278 self.color = coin.SoBaseColor()
01279 self.color.rgb.setValue(obj.LineColor[0],
01280 obj.LineColor[1],
01281 obj.LineColor[2])
01282 self.font = coin.SoFont()
01283 self.font3d = coin.SoFont()
01284 self.text = coin.SoAsciiText()
01285 self.text3d = coin.SoText2()
01286 self.text.justification = self.text3d.justification = coin.SoAsciiText.CENTER
01287 self.text.string = self.text3d.string = ''
01288 self.textpos = coin.SoTransform()
01289 self.textpos.translation.setValue([tbase.x,tbase.y,tbase.z])
01290 tm = fcvec.getPlaneRotation(p3.sub(p2),norm)
01291 rm = coin.SbRotation()
01292 self.textpos.rotation = rm
01293 label = coin.SoSeparator()
01294 label.addChild(self.textpos)
01295 label.addChild(self.color)
01296 label.addChild(self.font)
01297 label.addChild(self.text)
01298 label3d = coin.SoSeparator()
01299 label3d.addChild(self.textpos)
01300 label3d.addChild(self.color)
01301 label3d.addChild(self.font3d)
01302 label3d.addChild(self.text3d)
01303 self.coord1 = coin.SoCoordinate3()
01304 self.coord1.point.setValue((p2.x,p2.y,p2.z))
01305 self.coord2 = coin.SoCoordinate3()
01306 self.coord2.point.setValue((p3.x,p3.y,p3.z))
01307 marks = coin.SoAnnotation()
01308 marks.addChild(self.color)
01309 marks.addChild(self.coord1)
01310 marks.addChild(dimSymbol())
01311 marks.addChild(self.coord2)
01312 marks.addChild(dimSymbol())
01313 self.drawstyle = coin.SoDrawStyle()
01314 self.drawstyle.lineWidth = 1
01315 self.line = coin.SoLineSet()
01316 self.coords = coin.SoCoordinate3()
01317 selnode=coin.SoType.fromName("SoFCSelection").createInstance()
01318 selnode.documentName.setValue(FreeCAD.ActiveDocument.Name)
01319 selnode.objectName.setValue(obj.Object.Name)
01320 selnode.subElementName.setValue("Line")
01321 selnode.addChild(self.line)
01322 self.node = coin.SoGroup()
01323 self.node.addChild(self.color)
01324 self.node.addChild(self.drawstyle)
01325 self.node.addChild(self.coords)
01326 self.node.addChild(selnode)
01327 self.node.addChild(marks)
01328 self.node.addChild(label)
01329 self.node3d = coin.SoGroup()
01330 self.node3d.addChild(self.color)
01331 self.node3d.addChild(self.drawstyle)
01332 self.node3d.addChild(self.coords)
01333 self.node3d.addChild(selnode)
01334 self.node3d.addChild(marks)
01335 self.node3d.addChild(label3d)
01336 obj.addDisplayMode(self.node,"2D")
01337 obj.addDisplayMode(self.node3d,"3D")
01338 self.onChanged(obj,"FontSize")
01339 self.onChanged(obj,"FontName")
01340
01341 def updateData(self, obj, prop):
01342 text = None
01343 if obj.Base and obj.LinkedVertices:
01344 if "Shape" in obj.Base.PropertiesList:
01345 if len(obj.LinkedVertices) == 3:
01346
01347 e = obj.Base.Shape.Edges[obj.LinkedVertices[0]]
01348 c = e.Curve.Center
01349 bray = fcvec.scaleTo(obj.Dimline.sub(c),e.Curve.Radius)
01350 if obj.LinkedVertices[1] == 1:
01351 v1 = c
01352 else:
01353 v1 = c.add(fcvec.neg(bray))
01354 v2 = c.add(bray)
01355 else:
01356
01357 v1 = obj.Base.Shape.Vertexes[obj.LinkedVertices[0]].Point
01358 v2 = obj.Base.Shape.Vertexes[obj.LinkedVertices[1]].Point
01359 if v1 != obj.Start: obj.Start = v1
01360 if v2 != obj.End: obj.End = v2
01361 p1,p2,p3,p4,tbase,norm,rot = self.calcGeom(obj)
01362 if 'Override' in obj.ViewObject.PropertiesList:
01363 text = str(obj.ViewObject.Override)
01364 dtext = getParam("dimPrecision")
01365 dtext = "%."+str(dtext)+"f"
01366 dtext = (dtext % p3.sub(p2).Length)
01367 if text:
01368 text = text.replace("dim",dtext)
01369 else:
01370 text = dtext
01371 self.text.string = self.text3d.string = text
01372 self.textpos.rotation = coin.SbRotation(rot[0],rot[1],rot[2],rot[3])
01373 self.textpos.translation.setValue([tbase.x,tbase.y,tbase.z])
01374 if obj.ViewObject.DisplayMode == "2D":
01375 self.coords.point.setValues([[p1.x,p1.y,p1.z],
01376 [p2.x,p2.y,p2.z],
01377 [p3.x,p3.y,p3.z],
01378 [p4.x,p4.y,p4.z]])
01379 self.line.numVertices.setValues([4])
01380 else:
01381 ts = (len(text)*obj.ViewObject.FontSize)/4
01382 rm = ((p3.sub(p2)).Length/2)-ts
01383 p2a = p2.add(fcvec.scaleTo(p3.sub(p2),rm))
01384 p2b = p3.add(fcvec.scaleTo(p2.sub(p3),rm))
01385 self.coords.point.setValues([[p1.x,p1.y,p1.z],
01386 [p2.x,p2.y,p2.z],
01387 [p2a.x,p2a.y,p2a.z],
01388 [p2b.x,p2b.y,p2b.z],
01389 [p3.x,p3.y,p3.z],
01390 [p4.x,p4.y,p4.z]])
01391 self.line.numVertices.setValues([3,3])
01392 self.coord1.point.setValue((p2.x,p2.y,p2.z))
01393 self.coord2.point.setValue((p3.x,p3.y,p3.z))
01394
01395 def onChanged(self, vp, prop):
01396 if prop == "FontSize":
01397 self.font.size = vp.FontSize
01398 self.font3d.size = vp.FontSize*100
01399 elif prop == "FontName":
01400 self.font.name = self.font3d.name = str(vp.FontName)
01401 elif prop == "LineColor":
01402 c = vp.LineColor
01403 self.color.rgb.setValue(c[0],c[1],c[2])
01404 elif prop == "LineWidth":
01405 self.drawstyle.lineWidth = vp.LineWidth
01406 else:
01407 self.updateData(vp.Object, None)
01408
01409 def getDisplayModes(self,obj):
01410 modes=[]
01411 modes.extend(["2D","3D"])
01412 return modes
01413
01414 def getDefaultDisplayMode(self):
01415 return "2D"
01416
01417 def getIcon(self):
01418 if self.Object.Base:
01419 return """
01420 /* XPM */
01421 static char * dim_xpm[] = {
01422 "16 16 6 1",
01423 " c None",
01424 ". c #000000",
01425 "+ c #FFFF00",
01426 "@ c #FFFFFF",
01427 "$ c #141010",
01428 "# c #615BD2",
01429 " $$$$$$$$",
01430 " $##$$#$$",
01431 " . $##$$##$",
01432 " .. $##$$##$",
01433 " .+. $######$",
01434 " .++. $##$$##$",
01435 " .+++. .$##$$##$",
01436 ".++++. .$######$",
01437 " .+++. .$$$$$$$$"
01438 " .++. .++. ",
01439 " .+. .+. ",
01440 " .. .. ",
01441 " . . ",
01442 " ",
01443 " ",
01444 " "};
01445 """
01446 else:
01447 return """
01448 /* XPM */
01449 static char * dim_xpm[] = {
01450 "16 16 4 1",
01451 " c None",
01452 ". c #000000",
01453 "+ c #FFFF00",
01454 "@ c #FFFFFF",
01455 " ",
01456 " ",
01457 " . . ",
01458 " .. .. ",
01459 " .+. .+. ",
01460 " .++. .++. ",
01461 " .+++. .. .+++. ",
01462 ".++++. .. .++++.",
01463 " .+++. .. .+++. ",
01464 " .++. .++. ",
01465 " .+. .+. ",
01466 " .. .. ",
01467 " . . ",
01468 " ",
01469 " ",
01470 " "};
01471 """
01472
01473 def __getstate__(self):
01474 return None
01475
01476 def __setstate__(self,state):
01477 return None
01478
01479 class AngularDimension:
01480 "The AngularDimension object"
01481 def __init__(self, obj):
01482 obj.addProperty("App::PropertyAngle","FirstAngle","Base",
01483 "Start angle of the dimension")
01484 obj.addProperty("App::PropertyAngle","LastAngle","Base",
01485 "End angle of the dimension")
01486 obj.addProperty("App::PropertyVector","Dimline","Base",
01487 "Point through which the dimension line passes")
01488 obj.addProperty("App::PropertyVector","Center","Base",
01489 "The center point of this dimension")
01490 obj.FirstAngle = 0
01491 obj.LastAngle = 90
01492 obj.Dimline = FreeCAD.Vector(0,1,0)
01493 obj.Center = FreeCAD.Vector(0,0,0)
01494 obj.Proxy = self
01495 self.Type = "AngularDimension"
01496
01497 def onChanged(self, fp, prop):
01498 pass
01499
01500 def execute(self, fp):
01501 if fp.ViewObject:
01502 fp.ViewObject.update()
01503
01504 class ViewProviderAngularDimension:
01505 "A View Provider for the Angular Dimension object"
01506 def __init__(self, obj):
01507 obj.addProperty("App::PropertyLength","FontSize","Base","Font size")
01508 obj.addProperty("App::PropertyString","FontName","Base","Font name")
01509 obj.addProperty("App::PropertyLength","LineWidth","Base","Line width")
01510 obj.addProperty("App::PropertyColor","LineColor","Base","Line color")
01511 obj.addProperty("App::PropertyVector","Position","Base","The position of the text. Leave (0,0,0) for automatic position")
01512 obj.addProperty("App::PropertyString","Override","Base","Text override. Use 'dim' to insert the dimension length")
01513 obj.Proxy = self
01514 obj.FontSize=getParam("textheight")
01515 obj.FontName=getParam("textfont")
01516 obj.Override = ''
01517
01518 def attach(self, vobj):
01519 self.Object = vobj.Object
01520 self.arc = None
01521 c,tbase,trot,p2,p3 = self.calcGeom(vobj.Object)
01522 self.color = coin.SoBaseColor()
01523 self.color.rgb.setValue(vobj.LineColor[0],
01524 vobj.LineColor[1],
01525 vobj.LineColor[2])
01526 self.font = coin.SoFont()
01527 self.font3d = coin.SoFont()
01528 self.text = coin.SoAsciiText()
01529 self.text3d = coin.SoText2()
01530 self.text.justification = self.text3d.justification = coin.SoAsciiText.CENTER
01531 self.text.string = self.text3d.string = ''
01532 self.textpos = coin.SoTransform()
01533 self.textpos.translation.setValue([tbase.x,tbase.y,tbase.z])
01534 self.textpos.rotation = coin.SbRotation()
01535 label = coin.SoSeparator()
01536 label.addChild(self.textpos)
01537 label.addChild(self.color)
01538 label.addChild(self.font)
01539 label.addChild(self.text)
01540 label3d = coin.SoSeparator()
01541 label3d.addChild(self.textpos)
01542 label3d.addChild(self.color)
01543 label3d.addChild(self.font3d)
01544 label3d.addChild(self.text3d)
01545 self.coord1 = coin.SoCoordinate3()
01546 self.coord1.point.setValue((p2.x,p2.y,p2.z))
01547 self.coord2 = coin.SoCoordinate3()
01548 self.coord2.point.setValue((p3.x,p3.y,p3.z))
01549 marks = coin.SoAnnotation()
01550 marks.addChild(self.color)
01551 marks.addChild(self.coord1)
01552 marks.addChild(dimSymbol())
01553 marks.addChild(self.coord2)
01554 marks.addChild(dimSymbol())
01555 self.drawstyle = coin.SoDrawStyle()
01556 self.drawstyle.lineWidth = 1
01557 self.coords = coin.SoCoordinate3()
01558 self.selnode=coin.SoType.fromName("SoFCSelection").createInstance()
01559 self.selnode.documentName.setValue(FreeCAD.ActiveDocument.Name)
01560 self.selnode.objectName.setValue(vobj.Object.Name)
01561 self.selnode.subElementName.setValue("Arc")
01562 self.node = coin.SoGroup()
01563 self.node.addChild(self.color)
01564 self.node.addChild(self.drawstyle)
01565 self.node.addChild(self.coords)
01566 self.node.addChild(self.selnode)
01567 self.node.addChild(marks)
01568 self.node.addChild(label)
01569 self.node3d = coin.SoGroup()
01570 self.node3d.addChild(self.color)
01571 self.node3d.addChild(self.drawstyle)
01572 self.node3d.addChild(self.coords)
01573 self.node3d.addChild(self.selnode)
01574 self.node3d.addChild(marks)
01575 self.node3d.addChild(label3d)
01576 vobj.addDisplayMode(self.node,"2D")
01577 vobj.addDisplayMode(self.node3d,"3D")
01578 self.onChanged(vobj,"FontSize")
01579 self.onChanged(vobj,"FontName")
01580
01581 def calcGeom(self,obj):
01582 rad = (obj.Dimline.sub(obj.Center)).Length
01583 cir = Part.makeCircle(rad,obj.Center,Vector(0,0,1),obj.FirstAngle,obj.LastAngle)
01584 cp = fcgeo.findMidpoint(cir.Edges[0])
01585 rv = cp.sub(obj.Center)
01586 rv = fcvec.scaleTo(rv,rv.Length + obj.ViewObject.FontSize*.2)
01587 tbase = obj.Center.add(rv)
01588 trot = fcvec.angle(rv)-math.pi/2
01589 if (trot > math.pi/2) or (trot < -math.pi/2):
01590 trot = trot + math.pi
01591 s = getParam("dimorientation")
01592 if s == 0:
01593 if round(trot,precision()) == round(-math.pi/2,precision()):
01594 trot = math.pi/2
01595 return cir, tbase, trot, cir.Vertexes[0].Point, cir.Vertexes[-1].Point
01596
01597 def updateData(self, obj, prop):
01598 text = None
01599 ivob = None
01600 c,tbase,trot,p2,p3 = self.calcGeom(obj)
01601 buf=c.writeInventor(2,0.01)
01602 ivin = coin.SoInput()
01603 ivin.setBuffer(buf)
01604 ivob = coin.SoDB.readAll(ivin)
01605 arc = ivob.getChildren()[1]
01606
01607 if ivob and ivob.getNumChildren() > 1:
01608 arc = ivob.getChild(1).getChild(0)
01609 arc.removeChild(arc.getChild(0))
01610 arc.removeChild(arc.getChild(0))
01611 if self.arc:
01612 self.selnode.removeChild(self.arc)
01613 self.arc = arc
01614 self.selnode.addChild(self.arc)
01615 if 'Override' in obj.ViewObject.PropertiesList:
01616 text = str(obj.ViewObject.Override)
01617 dtext = getParam("dimPrecision")
01618 dtext = "%."+str(dtext)+"f"
01619 if obj.LastAngle > obj.FirstAngle:
01620 dtext = (dtext % (obj.LastAngle-obj.FirstAngle))+'\xb0'
01621 else:
01622 dtext = (dtext % ((360-obj.FirstAngle)+obj.LastAngle))+'\xb0'
01623 if text:
01624 text = text.replace("dim",dtext)
01625 else:
01626 text = dtext
01627 self.text.string = self.text3d.string = text
01628 self.textpos.translation.setValue([tbase.x,tbase.y,tbase.z])
01629 m = FreeCAD.Matrix()
01630 m.rotateZ(trot)
01631 tm = FreeCAD.Placement(m).Rotation.Q
01632 self.textpos.rotation = coin.SbRotation(tm[0],tm[1],tm[2],tm[3])
01633 self.coord1.point.setValue((p2.x,p2.y,p2.z))
01634 self.coord2.point.setValue((p3.x,p3.y,p3.z))
01635
01636 def onChanged(self, vobj, prop):
01637 if prop == "FontSize":
01638 self.font.size = vobj.FontSize
01639 self.font3d.size = vobj.FontSize*100
01640 elif prop == "FontName":
01641 self.font.name = self.font3d.name = str(vobj.FontName)
01642 elif prop == "LineColor":
01643 c = vobj.LineColor
01644 self.color.rgb.setValue(c[0],c[1],c[2])
01645 elif prop == "LineWidth":
01646 self.drawstyle.lineWidth = vobj.LineWidth
01647 elif prop == "DisplayMode":
01648 pass
01649 else:
01650 self.updateData(vobj.Object, None)
01651
01652 def getDisplayModes(self,obj):
01653 modes=[]
01654 modes.extend(["2D","3D"])
01655 return modes
01656
01657 def getDefaultDisplayMode(self):
01658 return "2D"
01659
01660 def getIcon(self):
01661 return """
01662 /* XPM */
01663 static char * dim_xpm[] = {
01664 "16 16 4 1",
01665 " c None",
01666 ". c #000000",
01667 "+ c #FFFF00",
01668 "@ c #FFFFFF",
01669 " ",
01670 " ",
01671 " . . ",
01672 " .. .. ",
01673 " .+. .+. ",
01674 " .++. .++. ",
01675 " .+++. .. .+++. ",
01676 ".++++. .. .++++.",
01677 " .+++. .. .+++. ",
01678 " .++. .++. ",
01679 " .+. .+. ",
01680 " .. .. ",
01681 " . . ",
01682 " ",
01683 " ",
01684 " "};
01685 """
01686
01687 class Rectangle:
01688 "The Rectangle object"
01689
01690 def __init__(self, obj):
01691 obj.addProperty("App::PropertyDistance","Length","Base","Length of the rectangle")
01692 obj.addProperty("App::PropertyDistance","Height","Base","Height of the rectange")
01693 obj.Proxy = self
01694 obj.Length=1
01695 obj.Height=1
01696 self.Type = "Rectangle"
01697
01698 def execute(self, fp):
01699 self.createGeometry(fp)
01700
01701 def onChanged(self, fp, prop):
01702 if prop in ["Length","Height"]:
01703 self.createGeometry(fp)
01704
01705 def createGeometry(self,fp):
01706 plm = fp.Placement
01707 p1 = Vector(0,0,0)
01708 p2 = Vector(p1.x+fp.Length,p1.y,p1.z)
01709 p3 = Vector(p1.x+fp.Length,p1.y+fp.Height,p1.z)
01710 p4 = Vector(p1.x,p1.y+fp.Height,p1.z)
01711 shape = Part.makePolygon([p1,p2,p3,p4,p1])
01712 shape = Part.Face(shape)
01713 fp.Shape = shape
01714 fp.Placement = plm
01715
01716 class ViewProviderRectangle(ViewProviderDraft):
01717 "A View Provider for the Rectangle object"
01718 def __init__(self, obj):
01719 ViewProviderDraft.__init__(self,obj)
01720 obj.addProperty("App::PropertyFile","TextureImage",
01721 "Base","Uses an image as a texture map")
01722
01723 def attach(self,obj):
01724 self.texture = None
01725
01726 def onChanged(self, vp, prop):
01727 if prop == "TextureImage":
01728 r = vp.RootNode
01729 if os.path.exists(vp.TextureImage):
01730 self.texture = coin.SoTexture2()
01731 self.texture.filename = str(vp.TextureImage)
01732 r.insertChild(self.texture,1)
01733 else:
01734 if self.texture:
01735 r.removeChild(self.texture)
01736 self.texture = None
01737 return
01738
01739 class Circle:
01740 "The Circle object"
01741
01742 def __init__(self, obj):
01743 obj.addProperty("App::PropertyAngle","FirstAngle","Arc",
01744 "Start angle of the arc")
01745 obj.addProperty("App::PropertyAngle","LastAngle","Arc",
01746 "End angle of the arc (for a full circle, give it same value as First Angle)")
01747 obj.addProperty("App::PropertyDistance","Radius","Base",
01748 "Radius of the circle")
01749 obj.Proxy = self
01750 self.Type = "Circle"
01751
01752 def execute(self, fp):
01753 self.createGeometry(fp)
01754
01755 def onChanged(self, fp, prop):
01756 if prop in ["Radius","FirstAngle","LastAngle"]:
01757 self.createGeometry(fp)
01758
01759 def createGeometry(self,fp):
01760 plm = fp.Placement
01761 shape = Part.makeCircle(fp.Radius,Vector(0,0,0),
01762 Vector(0,0,1),fp.FirstAngle,fp.LastAngle)
01763 if fp.FirstAngle == fp.LastAngle:
01764 shape = Part.Wire(shape)
01765 shape = Part.Face(shape)
01766 fp.Shape = shape
01767 fp.Placement = plm
01768
01769 class Wire:
01770 "The Wire object"
01771
01772 def __init__(self, obj):
01773 obj.addProperty("App::PropertyVectorList","Points","Base",
01774 "The vertices of the wire")
01775 obj.addProperty("App::PropertyBool","Closed","Base",
01776 "If the wire is closed or not")
01777 obj.addProperty("App::PropertyLink","Base","Base",
01778 "The base object is the wire is formed from 2 objects")
01779 obj.addProperty("App::PropertyLink","Tool","Base",
01780 "The tool object is the wire is formed from 2 objects")
01781 obj.Proxy = self
01782 obj.Closed = False
01783 self.Type = "Wire"
01784
01785 def execute(self, fp):
01786 self.createGeometry(fp)
01787
01788 def onChanged(self, fp, prop):
01789 if prop in ["Points","Closed","Base","Tool"]:
01790 self.createGeometry(fp)
01791
01792 def createGeometry(self,fp):
01793 plm = fp.Placement
01794 if fp.Base and (not fp.Tool):
01795 if fp.Base.isDerivedFrom("Sketcher::SketchObject"):
01796 shape = fp.Base.Shape.copy()
01797 if fp.Base.Shape.isClosed():
01798 shape = Part.Face(shape)
01799 fp.Shape = shape
01800 p = []
01801 for v in shape.Vertexes: p.append(v.Point)
01802 if fp.Points != p: fp.Points = p
01803 elif fp.Base and fp.Tool:
01804 if ('Shape' in fp.Base.PropertiesList) and ('Shape' in fp.Tool.PropertiesList):
01805 sh1 = fp.Base.Shape.copy()
01806 sh2 = fp.Tool.Shape.copy()
01807 shape = sh1.fuse(sh2)
01808 if fcgeo.isCoplanar(shape.Faces):
01809 shape = fcgeo.concatenate(shape)
01810 fp.Shape = shape
01811 p = []
01812 for v in shape.Vertexes: p.append(v.Point)
01813 if fp.Points != p: fp.Points = p
01814 elif fp.Points:
01815 if fp.Points[0] == fp.Points[-1]:
01816 if not fp.Closed: fp.Closed = True
01817 fp.Points.pop()
01818 if fp.Closed and (len(fp.Points) > 2):
01819 shape = Part.makePolygon(fp.Points+[fp.Points[0]])
01820 shape = Part.Face(shape)
01821 else:
01822 edges = []
01823 pts = fp.Points[1:]
01824 lp = fp.Points[0]
01825 for p in pts:
01826 edges.append(Part.Line(lp,p).toShape())
01827 lp = p
01828 shape = Part.Wire(edges)
01829 fp.Shape = shape
01830 fp.Placement = plm
01831
01832 class ViewProviderWire(ViewProviderDraft):
01833 "A View Provider for the Wire object"
01834 def __init__(self, obj):
01835 ViewProviderDraft.__init__(self,obj)
01836 obj.addProperty("App::PropertyBool","EndArrow","Base",
01837 "Displays a dim symbol at the end of the wire")
01838
01839 def attach(self, obj):
01840 self.Object = obj.Object
01841 col = coin.SoBaseColor()
01842 col.rgb.setValue(obj.LineColor[0],
01843 obj.LineColor[1],
01844 obj.LineColor[2])
01845 self.coords = coin.SoCoordinate3()
01846 self.pt = coin.SoAnnotation()
01847 self.pt.addChild(col)
01848 self.pt.addChild(self.coords)
01849 self.pt.addChild(dimSymbol())
01850
01851 def updateData(self, obj, prop):
01852 if prop == "Points":
01853 if obj.Points:
01854 p = obj.Points[-1]
01855 self.coords.point.setValue((p.x,p.y,p.z))
01856 return
01857
01858 def onChanged(self, vp, prop):
01859 if prop == "EndArrow":
01860 rn = vp.RootNode
01861 if vp.EndArrow:
01862 rn.addChild(self.pt)
01863 else:
01864 rn.removeChild(self.pt)
01865 return
01866
01867 def claimChildren(self):
01868 return [self.Object.Base,self.Object.Tool]
01869
01870 class Polygon:
01871 "The Polygon object"
01872
01873 def __init__(self, obj):
01874 obj.addProperty("App::PropertyInteger","FacesNumber","Base","Number of faces")
01875 obj.addProperty("App::PropertyDistance","Radius","Base","Radius of the control circle")
01876 obj.addProperty("App::PropertyEnumeration","DrawMode","Base","How the polygon must be drawn from the control circle")
01877 obj.DrawMode = ['inscribed','circumscribed']
01878 obj.FacesNumber = 3
01879 obj.Radius = 1
01880 obj.Proxy = self
01881 self.Type = "Polygon"
01882
01883 def execute(self, fp):
01884 self.createGeometry(fp)
01885
01886 def onChanged(self, fp, prop):
01887 if prop in ["FacesNumber","Radius","DrawMode"]:
01888 self.createGeometry(fp)
01889
01890 def createGeometry(self,fp):
01891 plm = fp.Placement
01892 angle = (math.pi*2)/fp.FacesNumber
01893 if fp.DrawMode == 'inscribed':
01894 delta = fp.Radius
01895 else:
01896 delta = fp.Radius/math.cos(angle/2)
01897 pts = [Vector(delta,0,0)]
01898 for i in range(fp.FacesNumber-1):
01899 ang = (i+1)*angle
01900 pts.append(Vector(delta*math.cos(ang),delta*math.sin(ang),0))
01901 pts.append(pts[0])
01902 shape = Part.makePolygon(pts)
01903 shape = Part.Face(shape)
01904 fp.Shape = shape
01905 fp.Placement = plm
01906
01907 class DrawingView:
01908 def __init__(self, obj):
01909 obj.addProperty("App::PropertyVector","Direction","Shape view","Projection direction")
01910 obj.addProperty("App::PropertyFloat","LinewidthModifier","Drawing view","Modifies the linewidth of the lines inside this object")
01911 obj.addProperty("App::PropertyFloat","TextModifier","Drawing view","Modifies the size of the texts inside this object")
01912 obj.addProperty("App::PropertyLink","Source","Base","The linked object")
01913 obj.addProperty("App::PropertyEnumeration","LineStyle","Drawing view","Line Style")
01914 obj.addProperty("App::PropertyEnumeration","FillStyle","Drawing view","Shape Fill Style")
01915 obj.LineStyle = ['continuous','dashed','dashdotted','dotted']
01916 fills = ['shape color']
01917 for f in FreeCAD.svgpatterns.keys():
01918 fills.append(f)
01919 obj.FillStyle = fills
01920
01921 obj.Proxy = self
01922 obj.LinewidthModifier = 100
01923 obj.TextModifier = 100
01924 self.Type = "DrawingView"
01925
01926 def execute(self, obj):
01927 if obj.Source:
01928 obj.ViewResult = self.updateSVG(obj)
01929
01930 def onChanged(self, obj, prop):
01931 if prop in ["X","Y","Scale","LinewidthModifier","TextModifier","LineStyle","FillStyle","Direction"]:
01932 obj.ViewResult = self.updateSVG(obj)
01933
01934 def updateSVG(self, obj):
01935 "encapsulates a svg fragment into a transformation node"
01936 svg = getSVG(obj.Source,obj.LinewidthModifier,obj.TextModifier,obj.LineStyle,obj.FillStyle,obj.Direction)
01937 result = ''
01938 result += '<g id="' + obj.Name + '"'
01939 result += ' transform="'
01940 result += 'rotate('+str(obj.Rotation)+','+str(obj.X)+','+str(obj.Y)+') '
01941 result += 'translate('+str(obj.X)+','+str(obj.Y)+') '
01942 result += 'scale('+str(obj.Scale)+','+str(-obj.Scale)+')'
01943 result += '">'
01944 result += svg
01945 result += '</g>'
01946 return result
01947
01948 class BSpline:
01949 "The BSpline object"
01950
01951 def __init__(self, obj):
01952 obj.addProperty("App::PropertyVectorList","Points","Base",
01953 "The points of the b-spline")
01954 obj.addProperty("App::PropertyBool","Closed","Base",
01955 "If the b-spline is closed or not")
01956 obj.Proxy = self
01957 obj.Closed = False
01958 self.Type = "BSpline"
01959
01960 def execute(self, fp):
01961 self.createGeometry(fp)
01962
01963 def onChanged(self, fp, prop):
01964 if prop in ["Points","Closed"]:
01965 self.createGeometry(fp)
01966
01967 def createGeometry(self,fp):
01968 plm = fp.Placement
01969 if fp.Points:
01970 if fp.Points[0] == fp.Points[-1]:
01971 if not fp.Closed: fp.Closed = True
01972 fp.Points.pop()
01973 if fp.Closed and (len(fp.Points) > 2):
01974 spline = Part.BSplineCurve()
01975 spline.interpolate(fp.Points, True)
01976
01977 shape = Part.Wire(spline.toShape())
01978 shape = Part.Face(shape)
01979 fp.Shape = shape
01980 else:
01981 spline = Part.BSplineCurve()
01982 spline.interpolate(fp.Points, False)
01983 fp.Shape = spline.toShape()
01984 fp.Placement = plm
01985
01986 class ViewProviderBSpline(ViewProviderDraft):
01987 "A View Provider for the BSPline object"
01988 def __init__(self, obj):
01989 ViewProviderDraft.__init__(self,obj)
01990 obj.addProperty("App::PropertyBool","EndArrow",
01991 "Base","Displays a dim symbol at the end of the wire")
01992 col = coin.SoBaseColor()
01993 col.rgb.setValue(obj.LineColor[0],
01994 obj.LineColor[1],
01995 obj.LineColor[2])
01996 self.coords = coin.SoCoordinate3()
01997 self.pt = coin.SoAnnotation()
01998 self.pt.addChild(col)
01999 self.pt.addChild(self.coords)
02000 self.pt.addChild(dimSymbol())
02001
02002 def updateData(self, obj, prop):
02003 if prop == "Points":
02004 if obj.Points:
02005 p = obj.Points[-1]
02006 self.coords.point.setValue((p.x,p.y,p.z))
02007 return
02008
02009 def onChanged(self, vp, prop):
02010 if prop == "EndArrow":
02011 rn = vp.RootNode
02012 if vp.EndArrow:
02013 rn.addChild(self.pt)
02014 else:
02015 rn.removeChild(self.pt)
02016 return
02017
02018 class Block:
02019 "The Block object"
02020
02021 def __init__(self, obj):
02022 obj.addProperty("App::PropertyLinkList","Components","Base",
02023 "The components of this block")
02024 obj.Proxy = self
02025 self.Type = "Block"
02026
02027 def execute(self, fp):
02028 self.createGeometry(fp)
02029
02030 def onChanged(self, fp, prop):
02031 if prop in ["Components"]:
02032 self.createGeometry(fp)
02033
02034 def createGeometry(self,fp):
02035 plm = fp.Placement
02036 shps = []
02037 for c in fp.Components:
02038 shps.append(c.Shape)
02039 if shps:
02040 shape = Part.makeCompound(shps)
02041 fp.Shape = shape
02042 fp.Placement = plm
02043
02044 class ViewProviderBlock(ViewProviderDraft):
02045 "A View Provider for the Block object"
02046
02047 def claimChildren(self):
02048 return self.Object.Components
02049
02050 class Shape2DView:
02051 "The Shape2DView object"
02052
02053 def __init__(self,obj):
02054 obj.addProperty("App::PropertyLink","Base","Base",
02055 "The base object this 2D view must represent")
02056 obj.addProperty("App::PropertyVector","Projection","Base",
02057 "The projection vector of this object")
02058 obj.Projection = Vector(0,0,-1)
02059 obj.Proxy = self
02060 self.Type = "2DShapeView"
02061
02062 def execute(self,obj):
02063 self.createGeometry(obj)
02064
02065 def onChanged(self,obj,prop):
02066 if prop in ["Projection","Base"]:
02067 print "changing",prop
02068 self.createGeometry(obj)
02069
02070 def createGeometry(self,obj):
02071 pl = obj.Placement
02072 if obj.Base:
02073 if obj.Base.isDerivedFrom("Part::Feature"):
02074 [visibleG0,visibleG1,hiddenG0,hiddenG1] = Drawing.project(obj.Base.Shape,obj.Projection)
02075 print visibleG0.Edges
02076 if visibleG0:
02077 obj.Shape = visibleG0
02078 if not fcgeo.isNull(pl):
02079 obj.Placement = pl