Draft.py

Go to the documentation of this file.
00001 #***************************************************************************
00002 #*                                                                         *
00003 #*   Copyright (c) 2009, 2010                                              *  
00004 #*   Yorik van Havre <yorik@uncreated.net>, Ken Cline <cline@frii.com>     *  
00005 #*                                                                         *
00006 #*   This program is free software; you can redistribute it and/or modify  *
00007 #*   it under the terms of the GNU General Public License (GPL)            *
00008 #*   as published by the Free Software Foundation; either version 2 of     *
00009 #*   the License, or (at your option) any later version.                   *
00010 #*   for detail see the LICENCE text file.                                 *
00011 #*                                                                         *
00012 #*   This program is distributed in the hope that it will be useful,       *
00013 #*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014 #*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015 #*   GNU Library General Public License for more details.                  *
00016 #*                                                                         *
00017 #*   You should have received a copy of the GNU Library General Public     *
00018 #*   License along with this program; if not, write to the Free Software   *
00019 #*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
00020 #*   USA                                                                   *
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 # import FreeCAD modules
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 # General functions
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" # TODO
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" # TODO
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         # setting fill
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         # setting linetype
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 # Python Features definitions
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                     # arc linked dimension
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                     # linear linked dimension
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         # In case reading from buffer failed
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                 # DNC: bug fix: convert to face if closed
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

Generated on Wed Nov 23 19:00:08 2011 for FreeCAD by  doxygen 1.6.1