SectionPlane.py

Go to the documentation of this file.
00001 import FreeCAD,FreeCADGui,Part,Component,WorkingPlane,Drawing,math
00002 from FreeCAD import Vector
00003 from PyQt4 import QtCore
00004 from pivy import coin
00005 from draftlibs import fcvec,fcgeo
00006 
00007 
00008 class CommandSectionPlane:
00009     "the Arch SectionPlane command definition"
00010     def GetResources(self):
00011         return {'Pixmap'  : 'Arch_SectionPlane',
00012                 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_SectionPlane","Section Plane"),
00013                 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_SectionPlane","Adds a section plane object to the document")}
00014 
00015     def Activated(self):
00016         sel = FreeCADGui.Selection.getSelection()
00017         FreeCAD.ActiveDocument.openTransaction("Section Plane")
00018         obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Section")
00019         SectionPlane(obj)
00020         ViewProviderSectionPlane(obj.ViewObject)
00021         FreeCAD.ActiveDocument.commitTransaction()
00022         g = []
00023         for o in sel:
00024             if o.isDerivedFrom("Part::Feature"):
00025                 g.append(o)
00026         obj.Objects = g
00027         page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage","Page")
00028         template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg'
00029         page.ViewObject.HintOffsetX = 200
00030         page.ViewObject.HintOffsetY = 100
00031         page.ViewObject.HintScale = 20
00032         page.Template = template
00033         view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View")
00034         page.addObject(view)
00035         ArchDrawingView(view)
00036         view.Source = obj
00037         FreeCAD.ActiveDocument.recompute()
00038 
00039 class SectionPlane:
00040     "A section plane object"
00041     def __init__(self,obj):
00042         obj.Proxy = self
00043         obj.addProperty("App::PropertyLinkList","Objects","Base",
00044                         "The objects that must be considered by this section plane. Empty means all document")
00045         self.Type = "SectionPlane"
00046         self.Object = obj
00047         
00048     def execute(self,obj):
00049         pl = obj.Placement
00050         l = obj.ViewObject.DisplaySize
00051         p = Part.makePlane(l,l,Vector(l/2,-l/2,0),Vector(0,0,-1))
00052         obj.Shape = p
00053         obj.Placement = pl
00054         self.Object = obj
00055 
00056     def onChanged(self,obj,prop):
00057         pass
00058 
00059     def getNormal(self):
00060         return self.Object.Shape.Faces[0].normalAt(0,0)
00061 
00062 class ViewProviderSectionPlane(Component.ViewProviderComponent):
00063     "A View Provider for Section Planes"
00064     def __init__(self,vobj):
00065         vobj.addProperty("App::PropertyLength","DisplaySize","Base",
00066                         "The display size of the section plane")
00067         vobj.DisplaySize = 1
00068         vobj.Transparency = 85
00069         vobj.LineWidth = 1
00070         vobj.LineColor = (0.0,0.0,0.4,1.0)
00071         vobj.Proxy = self
00072         self.Object = vobj.Object
00073 
00074     def getIcon(self):
00075         return """
00076             /* XPM */
00077             static char * Arch_SectionPlane_xpm[] = {
00078             "16 16 3 1",
00079             "   c None",
00080             ".  c #000000",
00081             "+  c #FFFFFF",
00082             "     ......     ",
00083             "   ..+++.++..   ",
00084             "  .+++++..+++.  ",
00085             " .++++++...+++. ",
00086             " .++.+++....++. ",
00087             ".++.+.++.....++.",
00088             ".+.+++.+......+.",
00089             ".+.+++.+........",
00090             ".+.+++.+........",
00091             ".+.....+......+.",
00092             ".+.+++.+.....++.",
00093             " .++++++....++. ",
00094             " .++++++...+++. ",
00095             "  .+++++..+++.  ",
00096             "   ..+++.++..   ",
00097             "     ......     "};
00098             """
00099 
00100     def claimChildren(self):
00101         return []
00102 
00103     def attach(self,vobj):
00104         self.Object = vobj.Object
00105         # adding arrows
00106         rn = vobj.RootNode
00107         self.col = coin.SoBaseColor()
00108         self.setColor()
00109         ds = coin.SoDrawStyle()
00110         ds.style = coin.SoDrawStyle.LINES
00111         self.lcoords = coin.SoCoordinate3()
00112         ls = coin.SoLineSet()
00113         ls.numVertices.setValues([2,4,4,2,4,4,2,4,4,2,4,4])
00114         pt = coin.SoAnnotation()
00115         pt.addChild(self.col)
00116         pt.addChild(ds)
00117         pt.addChild(self.lcoords)
00118         pt.addChild(ls)
00119         rn.addChild(pt)
00120         self.setVerts()
00121 
00122     def setColor(self):
00123         print self.Object.ViewObject.LineColor
00124         self.col.rgb.setValue(self.Object.ViewObject.LineColor[0],
00125                               self.Object.ViewObject.LineColor[1],
00126                               self.Object.ViewObject.LineColor[2])
00127 
00128     def setVerts(self):
00129         def extendVerts(x,y):
00130             l1 = hd/3
00131             l2 = l1/3
00132             verts.extend([[x,y,0],[x,y,-l1]])
00133             verts.extend([[x,y,-l1],[x-l2,y,-l1+l2],[x+l2,y,-l1+l2],[x,y,-l1]])
00134             verts.extend([[x,y,-l1],[x,y-l2,-l1+l2],[x,y+l2,-l1+l2],[x,y,-l1]])
00135         hd = self.Object.ViewObject.DisplaySize/2
00136         verts = []
00137         extendVerts(-hd,-hd)
00138         extendVerts(hd,-hd)
00139         extendVerts(hd,hd)
00140         extendVerts(-hd,hd)
00141         self.lcoords.point.setValues(verts)
00142 
00143     def updateData(self,obj,prop):
00144         if prop in ["Shape","Placement"]:
00145             self.setVerts()
00146         return
00147 
00148     def onChanged(self,vobj,prop):
00149         if prop == "LineColor":
00150             self.setColor()
00151         elif prop == "DisplaySize":
00152             vobj.Object.Proxy.execute(vobj.Object)
00153         return
00154 
00155 class ArchDrawingView:
00156     def __init__(self, obj):
00157         obj.addProperty("App::PropertyLink","Source","Base","The linked object")
00158         obj.Proxy = self
00159         self.Type = "DrawingView"
00160 
00161     def execute(self, obj):
00162         print "executing"
00163         if obj.Source:
00164             obj.ViewResult = self.updateSVG(obj)
00165 
00166     def onChanged(self, obj, prop):
00167         print "prop:",prop
00168         if prop == "Source":
00169             obj.ViewResult = self.updateSVG(obj)
00170 
00171     def updateSVG(self, obj):
00172         "encapsulates a svg fragment into a transformation node"
00173         if obj.Source:
00174             if obj.Source.Objects:
00175                 svg = ''
00176                 for o in obj.Source.Objects:
00177                     svg += self.getSVG(o,obj.Source.Proxy.getNormal())
00178                 result = ''
00179                 result += '<g id="' + obj.Name + '"'
00180                 result += ' transform="'
00181                 result += 'rotate('+str(obj.Rotation)+','+str(obj.X)+','+str(obj.Y)+') '
00182                 result += 'translate('+str(obj.X)+','+str(obj.Y)+') '
00183                 result += 'scale('+str(obj.Scale)+','+str(-obj.Scale)+')'
00184                 result += '">\n'
00185                 result += svg
00186                 result += '</g>\n'
00187                 print "complete node:",result
00188                 return result
00189         return ''
00190 
00191     def getSVG(self,obj,direction,base=None):
00192         """returns an svg fragment from a SectionPlane object,
00193         a direction vector and optionally a base point"""
00194 
00195         print "getting representation of ",obj.Name," at ",direction
00196 
00197         # using Draft WorkingPlane
00198         plane = None
00199         if direction != Vector(0,0,0):
00200             plane = WorkingPlane.plane()
00201             plane.alignToPointAndAxis(Vector(0,0,0),fcvec.neg(direction),0)
00202         print "plane:",plane
00203 
00204         def intersection(p1,p2,p3,p4):
00205             "returns the intersection of line (p1,p2) with plane (p3,p4)"
00206             # http://paulbourke.net/geometry/planeline/
00207             dn = p4.dot(p2.sub(p1))
00208             if dn != 0:
00209                 u = (p4.dot(p3.sub(p1))) / dn 
00210                 p = p1.add((p2.sub(p1)).scale(u,u,u))
00211                 return p
00212             else:
00213                 # line is parallel to normal
00214                 vp = fcvec.project(p3.sub(p1),p2.sub(p1))
00215                 l = vp.Length
00216                 if vp.getAngle(p2.sub(p1)) > 1:
00217                     l = -l
00218                 return fcvec.scaleTo(p2.sub(p1),l)
00219 
00220         def getFirstIndex(list1,list2):
00221             "returns the first index from list2 where there is an item of list1"
00222             for i1 in range(len(list1)):
00223                 for i2 in range(len(list2)):
00224                             if list1[i1].hashCode() == list2[i2].hashCode():
00225                                 return i2
00226             return None
00227 
00228         def getLastIndex(list1,list2):
00229             "returns the last index from list2 where there is an item of list1"
00230             i = None
00231             for i1 in range(len(list1)):
00232                 for i2 in range(len(list2)):
00233                             if list1[i1].hashCode() == list2[i2].hashCode():
00234                                 i = i2
00235             return i
00236 
00237         def getProj(vec):
00238             if not plane:
00239                 return vec
00240             nx = fcvec.project(vec,plane.u)
00241             lx = nx.Length
00242             if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx
00243             ny = fcvec.project(vec,plane.v)
00244             ly = ny.Length
00245             if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly
00246             return Vector(lx,ly,0)
00247 
00248         def getPath(face):
00249             svg ='<path '
00250             edges = fcgeo.sortEdges(face.Edges)
00251             v = getProj(edges[0].Vertexes[0].Point)
00252             svg += 'd="M '+ str(v.x) +' '+ str(v.y) + ' '
00253             for e in edges:
00254                 if isinstance(e.Curve,Part.Line) or  isinstance(e.Curve,Part.BSplineCurve):
00255                     v = getProj(e.Vertexes[-1].Point)
00256                     svg += 'L '+ str(v.x) +' '+ str(v.y) + ' '
00257                 elif isinstance(e.Curve,Part.Circle):
00258                     r = e.Curve.Radius
00259                     v = getProj(e.Vertexes[-1].Point)
00260                     svg += 'A '+ str(r) + ' '+ str(r) +' 0 0 1 '+ str(v.x) +' '
00261                     svg += str(v.y) + ' '
00262             svg += '" '
00263             svg += 'stroke="#000000" '
00264             svg += 'stroke-width="0.01 px" '
00265             svg += 'style="stroke-width:0.01;'
00266             svg += 'stroke-miterlimit:1;'
00267             svg += 'stroke-dasharray:none;'
00268             svg += 'fill:#aaaaaa"'
00269             svg += '/>\n'
00270             return svg
00271 
00272         sortedFaces = []
00273 
00274         if not base:
00275             # getting the base point = first point from the bounding box
00276             bb = obj.Shape.BoundBox
00277             rad = bb.DiagonalLength/2
00278             rv = bb.Center.add(direction)
00279             rv = fcvec.scaleTo(rv,rad)
00280             rv = fcvec.neg(rv)
00281             base = bb.Center.add(rv)
00282 
00283         print "base:",base
00284 
00285         # getting faces rays
00286         unsortedFaces = obj.Shape.Faces[:]
00287         for face in unsortedFaces:
00288 
00289             # testing if normal
00290             normal = face.normalAt(0,0)
00291             if normal.getAngle(direction) <= math.pi/2:
00292                 print "normal pointing outwards"
00293                 continue
00294             
00295             bhash = face.hashCode()
00296             center = face.CenterOfMass
00297             ray = center.sub(base)
00298             ray = fcvec.project(ray,direction)
00299             z = ray.Length
00300             if ray.getAngle(direction) > 1:
00301                 z = -z
00302 
00303             print "face center:",center," ray:",ray
00304 
00305             tempFaces = [face]
00306 
00307             # comparing with other faces crossed by the same ray
00308             for of in sortedFaces:
00309                 obb = of.BoundBox
00310                 op = intersection(base,ray,of.CenterOfMass,of.normalAt(0,0))
00311                 if obb.isInside(op):
00312                     oray = op.sub(base)
00313                     oray = fcvec.project(oray,direction)
00314                     oz = oray.Length
00315                     if oray.getAngle(direction) > 1:
00316                         oz = -oz
00317                     if oz > 0:
00318                         if oz < z:
00319                             tempFaces.insert(0,of)
00320                         elif oz > z:
00321                             tempFaces.append(of)
00322 
00323             print "tempFaces:",tempFaces
00324 
00325             # finding the position of the base face among others
00326             findex = 0
00327             if len(tempFaces) > 1:
00328                 for i in range(len(tempFaces)):
00329                     if tempFaces[i].hashCode() == bhash:
00330                         findex = i
00331 
00332             print "face index in tempfaces:",findex
00333 
00334             # finding the right place to insert in ordered list
00335             oindex = 0 
00336             if not sortedFaces:
00337                 sortedFaces.append(face)
00338             elif len(tempFaces) == 1:
00339                 sortedFaces.append(face)
00340             else:
00341                 if findex == 0: # our face is the first item
00342                     ni = getFirstIndex(tempFaces[1:],sortedFaces)
00343                     if ni == None:
00344                         sortedFaces.append(face)
00345                     else:
00346                         sortedFaces.insert(ni,face)
00347                 elif findex == len(tempFaces)-1: # our face is the last item
00348                     ni = getLastIndex(tempFaces[:-1],sortedFaces)
00349                     if ni == None:
00350                         sortedFaces.append(face)
00351                     else:
00352                         sortedFaces.insert(ni+1,face)
00353                 else: # there are faces before and after
00354                     i1 = getLastIndex(tempFaces[:findex],sortedFaces)
00355                     i2 = getFirstIndex(tempFaces[findex+1:],sortedFaces)
00356                     if i1 == None:
00357                         if i2 == None:
00358                             sortedFaces.append(face)
00359                         else:
00360                             sortedFaces.insert(i2,face)
00361                     else:
00362                             sortedFaces.insert(i1+1,face)
00363 
00364         print "sorted faces:",len(sortedFaces)
00365 
00366         # building SVG representation
00367         svg = ''
00368         for f in sortedFaces:
00369             svg += getPath(f)
00370 
00371         print "result:",svg
00372             
00373         return svg
00374 
00375 
00376 FreeCADGui.addCommand('Arch_SectionPlane',CommandSectionPlane())

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