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
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
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
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
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
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
00286 unsortedFaces = obj.Shape.Faces[:]
00287 for face in unsortedFaces:
00288
00289
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
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
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
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:
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:
00348 ni = getLastIndex(tempFaces[:-1],sortedFaces)
00349 if ni == None:
00350 sortedFaces.append(face)
00351 else:
00352 sortedFaces.insert(ni+1,face)
00353 else:
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
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())