def __init__(self, placement): import WorkingPlane self.reset() self.wp = WorkingPlane.plane() self.wp.setFromPlacement(placement, rebase=True) if DEBUG: print("Renderer initialized on %s. %s, %s" % (self.wp, self.wp.getPlacement().Base, self.wp.getNormal()))
def getRotation(entity): """returns a FreeCAD rotation from an IfcProduct with a IfcMappedItem representation""" try: u = FreeCAD.Vector(entity.Axis1.DirectionRatios) v = FreeCAD.Vector(entity.Axis2.DirectionRatios) w = FreeCAD.Vector(entity.Axis3.DirectionRatios) except AttributeError: return FreeCAD.Rotation() import WorkingPlane p = WorkingPlane.plane(u=u,v=v,w=w) return p.getRotation().Rotation
def buildSvgParts(self, obj, render, groups): faceHighlightDistance = obj.FaceHighlightDistance parts = render.getSvgParts(faceHighlightDistance.Value) wp = WorkingPlane.plane() wp.setFromPlacement(obj.Placement, rebase=True) self.sectionSVG = parts["sections"] self.secondaryFacesSVG = parts["secondaryFaces"] self.windowSVG = parts["windows"] self.patternSVG = parts["patterns"] self.draftSvg = getDraftSvg(groups["drafts"], wp) self.sectionCutSvg = parts["sectionCuts"] self.markerSvg = parts["markers"] self.boundBox = parts["boundBox"] self.boundBox.adaptFromDrafts(groups["drafts"])
def flattenWire(wire): """Force a wire to get completely flat along its normal.""" n = getNormal(wire) if not n: return o = wire.Vertexes[0].Point plane = WorkingPlane.plane() plane.alignToPointAndAxis(o, n, 0) verts = [o] for v in wire.Vertexes[1:]: verts.append(plane.projectPoint(v.Point)) if wire.isClosed(): verts.append(o) w = Part.makePolygon(verts) return w
def flattenWire(wire): """Force a wire to get completely flat along its normal.""" n = get_normal(wire) # for backward compatibility with previous getNormal implementation if n is None: n = App.Vector(0, 0, 1) o = wire.Vertexes[0].Point plane = WorkingPlane.plane() plane.alignToPointAndAxis(o, n, 0) verts = [o] for v in wire.Vertexes[1:]: verts.append(plane.projectPoint(v.Point)) if wire.isClosed(): verts.append(o) w = Part.makePolygon(verts) return w
def renderOutlineSVG(self, objs, direction): plane = None plane = WorkingPlane.plane() if direction != Vector(0, 0, 0): plane.alignToPointAndAxis(Vector(0, 0, 0), fcvec.neg(direction), 0) else: direction = Vector(0, 0, -1) faces = [] for obj in objs: for face in obj.Shape.Faces: normal = face.normalAt(0, 0) if normal.getAngle(direction) > math.pi / 2: faces.append(face) print "faces:", faces if faces: base = faces.pop() for face in faces: base = base.oldFuse(face) result = self.getPath(base, plane) return result
def __init__(self, wp=None): """ Creates a renderer with a default Draft WorkingPlane Use like this: import ArchVRM p = ArchVRM.Renderer() p.add(App.ActiveDocument.ActiveObject) p.sort() p.buildDummy() """ self.wp = wp self.faces = [] self.oriented = False self.trimmed = False self.sorted = False if not self.wp: import WorkingPlane self.wp = WorkingPlane.plane() if DEBUG: print "Renderer initialized on " + str(self.wp)
def __init__(self, wp=None): """ Creates a renderer with a default Draft WorkingPlane Use like this: import ArchVRM p = ArchVRM.Renderer() p.add(App.ActiveDocument.ActiveObject) p.sort() p.buildDummy() """ self.reset() if wp: self.wp = wp else: import WorkingPlane self.wp = WorkingPlane.plane() if DEBUG: print("Renderer initialized on " + str(self.wp))
def __init__(self,wp=None): """ Creates a renderer with a default Draft WorkingPlane Use like this: import ArchVRM p = ArchVRM.Renderer() p.add(App.ActiveDocument.ActiveObject) p.sort() p.buildDummy() """ self.reset() if wp: self.wp = wp else: import WorkingPlane self.wp = WorkingPlane.plane() if DEBUG: print "Renderer initialized on " + str(self.wp)
def order(self, face, right=False): """order(face,[right]): returns a list of vertices ordered clockwise. The first vertex will be the lefmost one, unless right is True, in which case the first vertex will be the rightmost one""" verts = [v.Point for v in face.OuterWire.OrderedVertexes] # flatten the polygon on the XY plane wp = WorkingPlane.plane() wp.alignToPointAndAxis(face.CenterOfMass, face.normalAt(0, 0)) pverts = [] for v in verts: vx = DraftVecUtils.project(v, wp.u) lx = vx.Length if vx.getAngle(wp.u) > 1: lx = -lx vy = DraftVecUtils.project(v, wp.v) ly = vy.Length if vy.getAngle(wp.v) > 1: ly = -ly pverts.append(FreeCAD.Vector(lx, ly, 0)) pverts.append(pverts[0]) # https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order s = 0 for i in range(len(pverts) - 1): s += (pverts[i + 1].x - pverts[i].x) * (pverts[i + 1].y + pverts[i].y) if s < 0: verts.reverse() elif s == 0: print("error computing winding direction") return return verts
def order(self,face,right=False): """order(face,[right]): returns a list of vertices ordered clockwise. The first vertex will be the lefmost one, unless right is True, in which case the first vertex will be the rightmost one""" verts = [v.Point for v in face.OuterWire.OrderedVertexes] # flatten the polygon on the XY plane wp = WorkingPlane.plane() wp.alignToPointAndAxis(face.CenterOfMass,face.normalAt(0,0)) pverts = [] for v in verts: vx = DraftVecUtils.project(v,wp.u) lx = vx.Length if vx.getAngle(wp.u) > 1: lx = -lx vy = DraftVecUtils.project(v,wp.v) ly = vy.Length if vy.getAngle(wp.v) > 1: ly = -ly pverts.append(FreeCAD.Vector(lx,ly,0)) pverts.append(pverts[0]) # https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order s = 0 for i in range(len(pverts)-1): s += (pverts[i+1].x-pverts[i].x)*(pverts[i+1].y+pverts[i].y) if s < 0: verts.reverse() elif s == 0: print("error computing winding direction") return return verts
def getViewPlane(view): """getViewPlane(View): Returns view_plane corresponding to view, where view can be "Front", "Rear", "Left", "Right", "Top" or "Bottom". """ if view == "Front": view_plane = WorkingPlane.plane() view_plane.axis = FreeCAD.Vector(0, -1, 0) view_plane.v = FreeCAD.Vector(0, 0, -1) view_plane.u = FreeCAD.Vector(1, 0, 0) elif view == "Rear": view_plane = WorkingPlane.plane() view_plane.axis = FreeCAD.Vector(0, 1, 0) view_plane.v = FreeCAD.Vector(0, 0, -1) view_plane.u = FreeCAD.Vector(-1, 0, 0) elif view == "Left": view_plane = WorkingPlane.plane() view_plane.axis = FreeCAD.Vector(-1, 0, 0) view_plane.v = FreeCAD.Vector(0, 0, -1) view_plane.u = FreeCAD.Vector(0, -1, 0) elif view == "Right": view_plane = WorkingPlane.plane() view_plane.axis = FreeCAD.Vector(1, 0, 0) view_plane.v = FreeCAD.Vector(0, 0, -1) view_plane.u = FreeCAD.Vector(0, 1, 0) elif view == "Top": view_plane = WorkingPlane.plane() view_plane.axis = FreeCAD.Vector(0, 0, 1) view_plane.v = FreeCAD.Vector(0, -1, 0) view_plane.u = FreeCAD.Vector(1, 0, 0) elif view == "Bottom": view_plane = WorkingPlane.plane() view_plane.axis = FreeCAD.Vector(0, 0, -1) view_plane.v = FreeCAD.Vector(0, 1, 0) view_plane.u = FreeCAD.Vector(1, 0, 0) else: FreeCAD.Console.PrintError( 'Invalid/Unsupported view. Valid views are: "Front", "Rear", ' '"Left", "Right" "Top" and "Bottom".\n') return None return view_plane
import FreeCAD as App import FreeCADGui as Gui from FreeCAD import Vector import Draft import DraftGeomUtils import DraftVecUtils from DraftTrackers import editTracker, Tracker, lineTracker from DraftTools import Modifier, Creator, DraftTool from DraftTools import selectObject, getPoint, redraw3DView #from .edit_tracker import editTracker plane = WorkingPlane.plane() class radiusTracker(Tracker): "A tracker that displays a transparent sphere to inicate a radius" def __init__(self, position=App.Vector(0, 0, 0), radius=1): self.trans = coin.SoTransform() self.trans.translation.setValue([position.x, position.y, position.z]) self.line = coin.SoLineSet() self.line.numVertices.setValue(4) m = coin.SoMaterial() m.transparency.setValue(0.9) m.diffuseColor.setValue([0, 1, 0]) self.coords = coin.SoCoordinate3() self.coords.point.set1Value(0, [0.0, 0.0, 0.0])
def isEnabled(self,but): "returns true if the given button is turned on" for b in self.toolbarButtons: if str(b.objectName()) == "SnapButton" + but: return (b.isEnabled() and b.isChecked()) return False def show(self): "shows the toolbar" if not hasattr(self,"toolbar"): self.makeSnapToolBar() mw = getMainWindow() bt = mw.findChild(QtGui.QToolBar,"Draft Snap") if not bt: mw.addToolBar(self.toolbar) self.toolbar.show() def setGrid(self): "sets the grid, if visible" if self.grid: if self.grid.Visible: self.grid.set() if not hasattr(FreeCADGui,"Snapper"): FreeCADGui.Snapper = Snapper() if not hasattr(FreeCAD,"DraftWorkingPlane"): import WorkingPlane, Draft_rc FreeCAD.DraftWorkingPlane = WorkingPlane.plane() print FreeCAD.DraftWorkingPlane FreeCADGui.addIconPath(":/icons")
def getSVG(section,allOn=False,renderMode="Wireframe",showHidden=False,showFill=False,scale=1,linewidth=1,fontsize=1,techdraw=False,rotation=0): """getSVG(section,[allOn,renderMode,showHidden,showFill,scale,linewidth,fontsize]) : returns an SVG fragment from an Arch section plane. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern""" if not section.Objects: return import Part,DraftGeomUtils p = FreeCAD.Placement(section.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) objs = Draft.getGroupContents(section.Objects,walls=True,addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces and Draft objects spaces = [] nonspaces = [] drafts = [] windows = [] cutface = None for o in objs: if Draft.getType(o) == "Space": spaces.append(o) elif Draft.getType(o) in ["Dimension","Annotation"]: drafts.append(o) elif o.isDerivedFrom("Part::Part2DObject"): drafts.append(o) else: nonspaces.append(o) if Draft.getType(o) == "Window": windows.append(o) objs = nonspaces svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if renderMode in ["Solid",1]: # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(section.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if showHidden: render.cut(section.Shape,showHidden) else: render.cut(section.Shape) svg += '<g transform="scale(1,-1)">\n' svg += render.getViewSVG(linewidth="LWPlaceholder") svg += fillpattern svg += render.getSectionSVG(linewidth="SWPlaceholder",fillpattern="sectionfill") if showHidden: svg += render.getHiddenSVG(linewidth="LWPlaceholder") svg += '</g>\n' # print render.info() else: # render using the Drawing module import Drawing, Part shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,section,showHidden) if shapes: baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG(baseshape,direction) if svgf: svgf = svgf.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width="1"','stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width:0.01','stroke-width:LWPlaceholder') svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) svgh = Drawing.projectToSVG(hshapes,direction) if svgh: svgh = svgh.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width="1"','stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width:0.01','stroke-width:LWPlaceholder') svgh = svgh.replace('fill="none"','fill="none"\nstroke-dasharray="DAPlaceholder"') svg += svgh if sshapes: svgs = "" if showFill: #svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: #f = Draft.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0)) # temporarily disabling fill patterns f = Draft.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="#aaaaaa",color=(0,0,0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) svgs += Drawing.projectToSVG(sshapes,direction) if svgs: svgs = svgs.replace('stroke-width="0.35"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width="1"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.01','stroke-width:SWPlaceholder') svgs = svgs.replace('stroke-width="0.35 px"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.35','stroke-width:SWPlaceholder') svg += svgs scaledlinewidth = linewidth/scale st = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("CutLineThickness",2) yt = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("SymbolLineThickness",0.6) da = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetString("archHiddenPattern","30,10") da = da.replace(" ","") svg = svg.replace('LWPlaceholder', str(scaledlinewidth) + 'px') svg = svg.replace('SWPlaceholder', str(scaledlinewidth*st) + 'px') svg = svg.replace('DAPlaceholder', str(da)) if drafts: if not techdraw: svg += '<g transform="scale(1,-1)">' for d in drafts: svg += Draft.getSVG(d,scale=scale,linewidth=linewidth*yt,fontsize=fontsize,direction=direction,techdraw=techdraw,rotation=rotation) if not techdraw: svg += '</g>' # filter out spaces not cut by the section plane if cutface and spaces: spaces = [s for s in spaces if s.Shape.BoundBox.intersect(cutface.BoundBox)] if spaces: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in spaces: svg += Draft.getSVG(s,scale=scale,linewidth=linewidth*yt,fontsize=fontsize,direction=direction,techdraw=techdraw,rotation=rotation) if not techdraw: svg += '</g>' # add additional edge symbols from windows if cutface and windows: cutwindows = [w.Name for w in windows if w.Shape.BoundBox.intersect(cutface.BoundBox)] if windows: sh = [] for w in windows: if not hasattr(w.Proxy,"sshapes"): w.Proxy.execute(w) if hasattr(w.Proxy,"sshapes"): if w.Proxy.sshapes and (w.Name in cutwindows): c = Part.makeCompound(w.Proxy.sshapes) c.Placement = w.Placement sh.append(c) # buggy for now... #if hasattr(w.Proxy,"vshapes"): # if w.Proxy.vshapes: # c = Part.makeCompound(w.Proxy.vshapes) # c.Placement = w.Placement # sh.append(c) if sh: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in sh: svg += Draft.getSVG(s,scale=scale,linewidth=linewidth*yt,fontsize=fontsize,fillstyle="none",direction=direction,techdraw=techdraw,rotation=rotation) if not techdraw: svg += '</g>' #print "complete node:",svg return svg
def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale=1, rotation=0, linewidth=1, lineColor=(0.0,0.0,0.0), fontsize=1, showFill=False, fillColor=(0.8,0.8,0.8), techdraw=False): """getSVG(section, [renderMode, allOn, showHidden, scale, rotation, linewidth, lineColor, fontsize, showFill, fillColor, techdraw]): returns an SVG fragment from an Arch section plane. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern. lineColor -- Color of lines for the renderMode "Wireframe". fillColor -- If showFill is True and renderMode is "Wireframe", the cut areas are filled with fillColor. """ if not section.Objects: return "" import Part,DraftGeomUtils p = FreeCAD.Placement(section.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) objs = Draft.getGroupContents(section.Objects,walls=True,addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces and Draft objects spaces = [] nonspaces = [] drafts = [] windows = [] cutface = None for o in objs: if Draft.getType(o) == "Space": spaces.append(o) elif Draft.getType(o) in ["Dimension","Annotation","Label"]: drafts.append(o) elif o.isDerivedFrom("Part::Part2DObject"): drafts.append(o) else: nonspaces.append(o) if Draft.getType(o) == "Window": windows.append(o) objs = nonspaces archUserParameters = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") scaledLineWidth = linewidth/scale svgLineWidth = str(scaledLineWidth) + 'px' st = archUserParameters.GetFloat("CutLineThickness",2) svgCutLineWidth = str(scaledLineWidth * st) + 'px' yt = archUserParameters.GetFloat("SymbolLineThickness",0.6) svgSymbolLineWidth = str(linewidth * yt) hiddenPattern = archUserParameters.GetString("archHiddenPattern","30,10") svgHiddenPattern = hiddenPattern.replace(" ","") fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' svgLineColor = Draft.getrgb(lineColor) svg = '' # reading cached version svgcache = None if hasattr(section.Proxy,"svgcache") and section.Proxy.svgcache: svgcache = section.Proxy.svgcache[0] if section.Proxy.svgcache[1] != renderMode: svgcache = None if section.Proxy.svgcache[2] != showHidden: svgcache = None if section.Proxy.svgcache[3] != showFill: svgcache = None # generating SVG if renderMode in ["Solid",1]: if not svgcache: svgcache = '' # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(section.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if showHidden: render.cut(section.Shape,showHidden) else: render.cut(section.Shape) svgcache += '<g transform="scale(1,-1)">\n' svgcache += render.getViewSVG(linewidth="SVGLINEWIDTH") svgcache += fillpattern svgcache += render.getSectionSVG(linewidth="SVGCUTLINEWIDTH", fillpattern="sectionfill") if showHidden: svgcache += render.getHiddenSVG(linewidth="SVGLINEWIDTH") svgcache += '</g>\n' # print(render.info()) section.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill] else: if not svgcache: svgcache = "" # render using the Drawing module import Drawing, Part shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,section,showHidden) if shapes: baseshape = Part.makeCompound(shapes) style = {'stroke': "SVGLINECOLOR", 'stroke-width': "SVGLINEWIDTH"} svgcache += Drawing.projectToSVG( baseshape, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) if hshapes: hshapes = Part.makeCompound(hshapes) style = {'stroke': "SVGLINECOLOR", 'stroke-width': "SVGLINEWIDTH", 'stroke-dasharray': "SVGHIDDENPATTERN"} svgcache += Drawing.projectToSVG( hshapes, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) if sshapes: if showFill: #svgcache += fillpattern svgcache += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: #svg += Draft.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0)) # temporarily disabling fill patterns svgcache += Draft.getSVG(s, direction=direction.negative(), linewidth=0, fillstyle=Draft.getrgb(fillColor), color=lineColor) svgcache += "</g>\n" sshapes = Part.makeCompound(sshapes) style = {'stroke': "SVGLINECOLOR", 'stroke-width': "SVGCUTLINEWIDTH"} svgcache += Drawing.projectToSVG( sshapes, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) section.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill] svgcache = svgcache.replace("SVGLINECOLOR",svgLineColor) svgcache = svgcache.replace("SVGLINEWIDTH",svgLineWidth) svgcache = svgcache.replace("SVGHIDDENPATTERN",svgHiddenPattern) svgcache = svgcache.replace("SVGCUTLINEWIDTH",svgCutLineWidth) svg += svgcache if drafts: if not techdraw: svg += '<g transform="scale(1,-1)">' for d in drafts: svg += Draft.getSVG(d, scale=scale, linewidth=svgSymbolLineWidth, fontsize=fontsize, direction=direction, color=lineColor, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' # filter out spaces not cut by the section plane if cutface and spaces: spaces = [s for s in spaces if s.Shape.BoundBox.intersect(cutface.BoundBox)] if spaces: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in spaces: svg += Draft.getSVG(s, scale=scale, linewidth=svgSymbolLineWidth, fontsize=fontsize, direction=direction, color=lineColor, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' # add additional edge symbols from windows cutwindows = [] if cutface and windows: cutwindows = [w.Name for w in windows if w.Shape.BoundBox.intersect(cutface.BoundBox)] if windows: sh = [] for w in windows: if not hasattr(w.Proxy,"sshapes"): w.Proxy.execute(w) if hasattr(w.Proxy,"sshapes"): if w.Proxy.sshapes and (w.Name in cutwindows): c = Part.makeCompound(w.Proxy.sshapes) c.Placement = w.Placement sh.append(c) # buggy for now... #if hasattr(w.Proxy,"vshapes"): # if w.Proxy.vshapes: # c = Part.makeCompound(w.Proxy.vshapes) # c.Placement = w.Placement # sh.append(c) if sh: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in sh: svg += Draft.getSVG(s, scale=scale, linewidth=svgSymbolLineWidth, fontsize=fontsize, fillstyle="none", direction=direction, color=lineColor, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' return svg
def getSVG(obj, scale=1, linewidth=0.35, fontsize=12, fillstyle="shape color", direction=None, linestyle=None, color=None, linespacing=None, techdraw=False, rotation=0, fillSpaces=False, override=True): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' import Part, DraftGeomUtils # if this is a group, gather all the svg views of its children if hasattr(obj, "isDerivedFrom"): if obj.isDerivedFrom("App::DocumentObjectGroup") or getType( obj) == "Layer": svg = "" for child in obj.Group: svg += getSVG(child, scale, linewidth, fontsize, fillstyle, direction, linestyle, color, linespacing, techdraw, rotation, fillSpaces, override) return svg pathdata = [] svg = "" linewidth = float(linewidth) / scale if not override: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "LineWidth"): if hasattr(obj.ViewObject.LineWidth, "Value"): lw = obj.ViewObject.LineWidth.Value else: lw = obj.ViewObject.LineWidth linewidth = lw * linewidth fontsize = (float(fontsize) / scale) / 2 if linespacing: linespacing = float(linespacing) / scale else: linespacing = 0.5 #print obj.Label," line spacing ",linespacing,"scale ",scale pointratio = .75 # the number of times the dots are smaller than the arrow size plane = None if direction: if isinstance(direction, FreeCAD.Vector): if direction != Vector(0, 0, 0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(Vector(0, 0, 0), direction.negative().negative(), 0) elif isinstance(direction, WorkingPlane.plane): plane = direction stroke = "#000000" if color and override: if "#" in color: stroke = color else: stroke = getrgb(color) elif gui: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "LineColor"): stroke = getrgb(obj.ViewObject.LineColor) elif hasattr(obj.ViewObject, "TextColor"): stroke = getrgb(obj.ViewObject.TextColor) lstyle = "none" if override: lstyle = getLineStyle(linestyle, scale) else: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "DrawStyle"): lstyle = getLineStyle(obj.ViewObject.DrawStyle, scale) def getPath(edges=[], wires=[], pathname=None): svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] first = True for w in wires: w1 = w.copy() if first: first = False else: # invert further wires to create holes w1 = DraftGeomUtils.invert(w1) w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs = () #skipped for the first edge for edgeindex, e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point - previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M ' + str(v.x) + ' ' + str(v.y) + ' ' else: if (vs[0].Point - previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD, "DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0, 0, 1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal), 2) in [0, 3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG( e, drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[ 1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes ) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = [ getProj( c.value((c.LastParameter - c.FirstParameter) / 2.0), plane), getProj(vs[-1].Point, plane) ] else: endpoints = [getProj(vs[-1].Point, plane)] # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -= 180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt( e.FirstParameter + (e.LastParameter - e.FirstParameter) / 10) flag_sweep = (DraftVecUtils.angle( t1, t2, drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' ' else: bspline = e.Curve.toBSpline(e.FirstParameter, e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline = bspline.approximateBSpline( 0.05, 50, 3, 'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree > 3: #should not happen raise AssertionError elif bezierseg.Degree == 1: edata += 'L ' elif bezierseg.Degree == 2: edata += 'Q ' elif bezierseg.Degree == 3: edata += 'C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) + ' ' + str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg def getCircle(edge): cen = getProj(edge.Curve.Center, plane) rad = edge.Curve.Radius if hasattr(FreeCAD, "DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0, 0, 1) if plane: drawing_plane_normal = plane.axis if round(edge.Curve.Axis.getAngle(drawing_plane_normal), 2) in [0, 3.14]: # perpendicular projection: circle svg = '<circle cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" r="' + str(rad) + '" ' else: # any other projection: ellipse svg = '<path d="' svg += getDiscretized(edge, plane) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getEllipse(edge): cen = getProj(edge.Curve.Center, plane) mir = edge.Curve.MinorRadius mar = edge.Curve.MajorRadius svg = '<ellipse cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" rx="' + str(mar) svg += '" ry="' + str(mir) + '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getArrow(arrowtype, point, arrowsize, color, linewidth, angle=0): svg = "" if gui: if not obj.ViewObject: return svg if obj.ViewObject.ArrowType == "Circle": svg += '<circle cx="' + str(point.x) + '" cy="' + str(point.y) svg += '" r="' + str(arrowsize) + '" ' svg += 'fill="none" stroke="' + color + '" ' svg += 'style="stroke-width:' + str( linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Dot": svg += '<circle cx="' + str(point.x) + '" cy="' + str(point.y) svg += '" r="' + str(arrowsize) + '" ' svg += 'fill="' + color + '" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Arrow": svg += '<path transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale(' + str(arrowsize) + ',' + str( arrowsize) + ')" freecad:skip="1" ' svg += 'fill="' + color + '" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick": svg += '<path transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale(' + str(arrowsize) + ',' + str( arrowsize) + ')" freecad:skip="1" ' svg += 'fill="' + color + '" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick-2": svg += '<line transform="rotate(' + str( math.degrees(angle) + 45) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="' + color + '" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:' + str(linewidth) + '" ' svg += 'x1="-' + str(arrowsize * 2) + '" y1="0" ' svg += 'x2="' + str(arrowsize * 2) + '" y2="0" />\n' else: print("getSVG: arrow type not implemented") return svg def getOvershoot(point, shootsize, color, linewidth, angle=0): svg = '<line transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="' + color + '" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:' + str(linewidth) + '" ' svg += 'x1="0" y1="0" ' svg += 'x2="' + str(shootsize * -1) + '" y2="0" />\n' return svg def getText(tcolor, fontsize, fontname, angle, base, text, linespacing=0.5, align="center", flip=True): if isinstance(angle, FreeCAD.Rotation): if not plane: angle = angle.Angle else: if plane.axis.getAngle(angle.Axis) < 0.001: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis) - math.pi) < 0.001: if abs(angle.Angle) > 0.1: angle = -angle.Angle else: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis) - math.pi / 2) < 0.001: return "" # text is perpendicular to view, so it shouldn't appear else: angle = 0 #TODO maybe there is something better to do here? if not isinstance(text, list): text = text.split("\n") if align.lower() == "center": anchor = "middle" elif align.lower() == "left": anchor = "start" else: anchor = "end" if techdraw: svg = "" for i in range(len(text)): t = text[i].replace("&", "&").replace("<", "<").replace( ">", ">") if six.PY2 and not isinstance(t, six.text_type): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") svg += '<text stroke-width="0" stroke="' + tcolor + '" fill="' + tcolor + '" font-size="' + str( fontsize) + '" ' svg += 'style="text-anchor:' + anchor + ';text-align:' + align.lower( ) + ';' svg += 'font-family:' + fontname + '" ' svg += 'transform="rotate(' + str(math.degrees(angle)) svg += ',' + str( base.x) + ',' + str(base.y - linespacing * i) + ') ' svg += 'translate(' + str( base.x) + ',' + str(base.y - linespacing * i) + ') ' svg += 'scale(1,-1)" ' #svg += '" freecad:skip="1"' svg += '>\n' + t + '</text>\n' else: svg = '<text stroke-width="0" stroke="' + tcolor + '" fill="' svg += tcolor + '" font-size="' svg += str(fontsize) + '" ' svg += 'style="text-anchor:' + anchor + ';text-align:' + align.lower( ) + ';' svg += 'font-family:' + fontname + '" ' svg += 'transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(base.x) + ',' + str(base.y) + ') ' if flip: svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')' else: svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')' #svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') ' if flip: svg += ' scale(1,-1) ' else: svg += ' scale(1,1) ' svg += '" freecad:skip="1"' svg += '>\n' if len(text) == 1: try: svg += text[0].replace("&", "&").replace( "<", "<").replace(">", ">") except: svg += text[0].decode("utf8").replace( "&", "&").replace("<", "<").replace(">", ">") else: for i in range(len(text)): if i == 0: svg += '<tspan>' else: svg += '<tspan x="0" dy="' + str(linespacing) + '">' try: svg += text[i].replace("&", "&").replace( "<", "<").replace(">", ">") except: svg += text[i].decode("utf8").replace( "&", "&").replace("<", "<").replace(">", ">") svg += '</tspan>\n' svg += '</text>\n' return svg if not obj: pass elif isinstance(obj, Part.Shape): if "#" in fillstyle: fill = fillstyle elif fillstyle == "shape color": fill = "#888888" else: fill = 'url(#' + fillstyle + ')' svg += getPath(obj.Edges, pathname="") elif getType(obj) in ["Dimension", "LinearDimension"]: if gui: if not obj.ViewObject: print( "export of dimensions to SVG is only available in GUI mode" ) elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy, "p1"): prx = obj.ViewObject.Proxy ts = (len(prx.string) * obj.ViewObject.FontSize.Value) / 4.0 rm = ((prx.p3.sub(prx.p2)).Length / 2.0) - ts p2a = getProj( prx.p2.add( DraftVecUtils.scaleTo(prx.p3.sub(prx.p2), rm)), plane) p2b = getProj( prx.p3.add( DraftVecUtils.scaleTo(prx.p2.sub(prx.p3), rm)), plane) p1 = getProj(prx.p1, plane) p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) p4 = getProj(prx.p4, plane) tbase = getProj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() rv = FreeCAD.Rotation(r[0], r[1], r[2], r[3]).multVec( FreeCAD.Vector(1, 0, 0)) angle = -DraftVecUtils.angle(getProj(rv, plane)) #angle = -DraftVecUtils.angle(p3.sub(p2)) svg = '' nolines = False if hasattr(obj.ViewObject, "ShowLine"): if not obj.ViewObject.ShowLine: nolines = True # drawing lines if not nolines: svg += '<path ' if obj.ViewObject.DisplayMode == "2D": tangle = angle if tangle > math.pi / 2: tangle = tangle - math.pi #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle+math.pi #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) if rotation != 0: #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string if abs(tangle + math.radians(rotation)) < 0.0001: tangle += math.pi tbase = tbase.add( DraftVecUtils.rotate( Vector(0, 2 / scale, 0), tangle)) if not nolines: svg += 'd="M ' + str(p1.x) + ' ' + str(p1.y) + ' ' svg += 'L ' + str(p2.x) + ' ' + str(p2.y) + ' ' svg += 'L ' + str(p3.x) + ' ' + str(p3.y) + ' ' svg += 'L ' + str(p4.x) + ' ' + str(p4.y) + '" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase.add(Vector(0, -2.0 / scale, 0)) if not nolines: svg += 'd="M ' + str(p1.x) + ' ' + str(p1.y) + ' ' svg += 'L ' + str(p2.x) + ' ' + str(p2.y) + ' ' svg += 'L ' + str(p2a.x) + ' ' + str(p2a.y) + ' ' svg += 'M ' + str(p2b.x) + ' ' + str(p2b.y) + ' ' svg += 'L ' + str(p3.x) + ' ' + str(p3.y) + ' ' svg += 'L ' + str(p4.x) + ' ' + str(p4.y) + '" ' if not nolines: svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="' + str(p1.x) + ' ' + str( p1.y) + '" ' svg += 'freecad:basepoint2="' + str(p4.x) + ' ' + str( p4.y) + '" ' svg += 'freecad:dimpoint="' + str(p2.x) + ' ' + str( p2.y) + '"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(obj.ViewObject, "DimOvershoot" ) and obj.ViewObject.DimOvershoot.Value: shootsize = obj.ViewObject.DimOvershoot.Value / pointratio svg += getOvershoot(p2, shootsize, stroke, linewidth, angle) svg += getOvershoot(p3, shootsize, stroke, linewidth, angle + math.pi) if hasattr(obj.ViewObject, "ExtOvershoot" ) and obj.ViewObject.ExtOvershoot.Value: shootsize = obj.ViewObject.ExtOvershoot.Value / pointratio shootangle = -DraftVecUtils.angle(p1.sub(p2)) svg += getOvershoot(p2, shootsize, stroke, linewidth, shootangle) svg += getOvershoot(p3, shootsize, stroke, linewidth, shootangle) # drawing arrows if hasattr(obj.ViewObject, "ArrowType"): arrowsize = obj.ViewObject.ArrowSize.Value / pointratio if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: angle = angle + math.pi svg += getArrow(obj.ViewObject.ArrowType, p2, arrowsize, stroke, linewidth, angle) svg += getArrow(obj.ViewObject.ArrowType, p3, arrowsize, stroke, linewidth, angle + math.pi) # drawing text svg += getText(stroke, fontsize, obj.ViewObject.FontName, tangle, tbase, prx.string) elif getType(obj) == "AngularDimension": if gui: if not obj.ViewObject: print( "export of dimensions to SVG is only available in GUI mode" ) elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy, "circle"): prx = obj.ViewObject.Proxy # drawing arc fill = "none" if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: if hasattr(prx, "circle1"): svg += getPath([prx.circle1]) svg += getPath([prx.circle2]) else: svg += getPath([prx.circle]) # drawing arrows if hasattr(obj.ViewObject, "ArrowType"): p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value / pointratio arrowlength = 4 * obj.ViewObject.ArrowSize.Value u1 = getProj( (prx.circle.valueAt(prx.circle.FirstParameter + arrowlength) ).sub( prx.circle.valueAt( prx.circle.FirstParameter)), plane) u2 = getProj( (prx.circle.valueAt(prx.circle.LastParameter - arrowlength) ).sub(prx.circle.valueAt( prx.circle.LastParameter)), plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1 + math.pi angle2 = angle2 + math.pi svg += getArrow(obj.ViewObject.ArrowType, p2, arrowsize, stroke, linewidth, angle1) svg += getArrow(obj.ViewObject.ArrowType, p3, arrowsize, stroke, linewidth, angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": t = prx.circle.tangentAt(prx.circle.FirstParameter + (prx.circle.LastParameter - prx.circle.FirstParameter) / 2.0) t = getProj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi / 2) or (tangle > math.pi / 2): tangle = tangle + math.pi tbase = getProj( prx.circle.valueAt(prx.circle.FirstParameter + (prx.circle.LastParameter - prx.circle.FirstParameter) / 2.0), plane) tbase = tbase.add( DraftVecUtils.rotate(Vector(0, 2.0 / scale, 0), tangle)) #print(tbase) else: tangle = 0 tbase = getProj(prx.tbase, plane) svg += getText(stroke, fontsize, obj.ViewObject.FontName, tangle, tbase, prx.string) elif getType(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property def format_point(coords, action='L'): return "{action}{x},{y}".format(x=coords.x, y=coords.y, action=action) # Draw multisegment line proj_points = list(map(lambda x: getProj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.format( stroke=stroke, linewidth=linewidth, directions=path_dir_str) svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) angle = -DraftVecUtils.angle(getProj(last_segment, plane)) + math.pi svg += getArrow(arrowtype=obj.ViewObject.ArrowType, point=proj_points[-1], arrowsize=obj.ViewObject.ArrowSize.Value / pointratio, color=stroke, linewidth=linewidth, angle=angle) # print text if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: fontname = obj.ViewObject.TextFont position = getProj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += getText(stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif getType(obj) in ["Annotation", "DraftText"]: "returns an svg representation of a document annotation" if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: n = obj.ViewObject.FontName if getType(obj) == "Annotation": p = getProj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText p = getProj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += getText(stroke, fontsize, n, r, p, t, linespacing, j) elif getType(obj) == "Axis": "returns the SVG representation of an Arch Axis system" if gui: if not obj.ViewObject: print("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject lorig = lstyle fill = 'none' rad = vobj.BubbleSize.Value / 2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += getPath([e]) lstyle = "none" pos = ["Start"] if hasattr(vobj, "BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start", "End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad, rad, rad)) svg += getCircle(Part.makeCircle(rad, center)) if hasattr(vobj.Proxy, "bubbletexts"): if len(vobj.Proxy.bubbletexts) >= n: svg += '<text fill="' + stroke + '" ' svg += 'font-size="' + str(rad) + '" ' svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="translate(' + str( center.x + rad / 4.0) + ',' + str(center.y - rad / 3.0) + ') ' svg += 'scale(1,-1)"> ' svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[ n].string.getValues()[0] + '</tspan>\n' svg += '</text>\n' n += 1 lstyle = lorig elif getType(obj) == "Pipe": fill = stroke if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve, Part.Circle): svg += getCircle(f.Edges[0]) elif getType(obj) == "Rebar": fill = "none" if obj.Proxy: if not hasattr(obj.Proxy, "wires"): obj.Proxy.execute(obj) if hasattr(obj.Proxy, "wires"): svg += getPath(wires=obj.Proxy.wires) elif getType(obj) == "PipeConnector": pass elif getType(obj) == "Space": "returns an SVG fragment for the text of a space" if gui: if not obj.ViewObject: print("export of spaces to SVG is only available in GUI mode") else: if fillSpaces: if hasattr(obj, "Proxy"): if not hasattr(obj.Proxy, "face"): obj.Proxy.getArea(obj, notouch=True) if hasattr(obj.Proxy, "face"): # setting fill if gui: fill = getrgb(obj.ViewObject.ShapeColor, testbw=False) fill_opacity = 1 - ( obj.ViewObject.Transparency / 100.0) else: fill = "#888888" svg += getPath(wires=[obj.Proxy.face.OuterWire]) c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = obj.ViewObject.Proxy.text1.string.getValues() t2 = obj.ViewObject.Proxy.text2.string.getValues() scale = obj.ViewObject.FirstLine.Value / obj.ViewObject.FontSize.Value f1 = fontsize * scale p2 = obj.Placement.multVec( FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation. getValue().getValue())) lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation. getValue().getValue()) p1 = p2.add(lspc) j = obj.ViewObject.TextAlign t3 = getText(c, f1, n, a, getProj(p1, plane), t1, linespacing, j, flip=True) svg += t3 if t2: ofs = FreeCAD.Vector(0, -lspc.Length, 0) if a: ofs = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -rotation).multVec(ofs) t4 = getText(c, fontsize, n, a, getProj(p1, plane).add(ofs), t2, linespacing, j, flip=True) svg += t4 elif obj.isDerivedFrom('Part::Feature'): if obj.Shape.isNull(): return '' # setting fill if obj.Shape.Faces: if gui: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None if (m != "Wireframe"): if fillstyle == "shape color": fill = getrgb(obj.ViewObject.ShapeColor, testbw=False) fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = 'url(#' + fillstyle + ')' svg += getPattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i, f in enumerate(obj.Shape.Faces): # place outer wire first wires = [f.OuterWire] wires.extend([ w for w in f.Wires if w.hashCode() != f.OuterWire.hashCode() ]) svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) else: for i, w in enumerate(obj.Shape.Wires): svg += getPath(w.Edges,pathname='%s_w%04d' % \ (obj.Name,i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i, e in enumerate(obj.Shape.Edges): if (DraftGeomUtils.findEdge(e, wiredEdges) is None): svg += getPath([e],pathname='%s_nwe%04d' % \ (obj.Name,i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve, Part.Circle): svg = getCircle(obj.Shape.Edges[0]) else: svg = getPath(obj.Shape.Edges) if FreeCAD.GuiUp: if hasattr(obj.ViewObject, "EndArrow") and hasattr( obj.ViewObject, "ArrowType") and (len(obj.Shape.Vertexes) > 1): if obj.ViewObject.EndArrow: p1 = getProj(obj.Shape.Vertexes[-1].Point, plane) p2 = getProj(obj.Shape.Vertexes[-2].Point, plane) angle = -DraftVecUtils.angle(p2.sub(p1)) arrowsize = obj.ViewObject.ArrowSize.Value / pointratio svg += getArrow(obj.ViewObject.ArrowType, p1, arrowsize, stroke, linewidth, angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">\n ' + svg + '</g>\n' return svg
def action(self, arg): """Handle the 3D scene events. This is installed as an EventCallback in the Inventor view. Parameters ---------- arg: dict Dictionary with strings that indicates the type of event received from the 3D view. """ if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection (self.point, ctrlPoint, info) = gui_tool_utils.getPoint(self, arg) if len(self.node) > 0: last = self.node[-1] if self.ghost: if self.point != last: # TODO: the following doesn't work at the moment mu = self.point.sub(last).normalize() # This part used to test for the GUI to obtain # the camera view but this is unnecessary # as this command is always launched in the GUI. _view = Gui.ActiveDocument.ActiveView mv = _view.getViewDirection().negative() mw = mv.cross(mu) _plane = WorkingPlane.plane(u=mu, v=mv, w=mw, pos=last) tm = _plane.getPlacement().toMatrix() m = self.ghost.getMatrix() m = m.multiply(tm.inverse()) m.scale(App.Vector(1, 1, -1)) m = m.multiply(tm) m.scale(App.Vector(-1, 1, 1)) self.ghost.setMatrix(m) if self.extendedCopy: if not gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): self.finish() gui_tool_utils.redraw3DView() elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): if self.point: self.ui.redraw() if (self.node == []): self.node.append(self.point) self.ui.isRelative.show() if self.ghost: self.ghost.on() _msg( translate("draft", "Pick end point of mirror line")) if self.planetrack: self.planetrack.set(self.point) else: last = self.node[0] if (self.ui.isCopy.isChecked() or gui_tool_utils.hasMod( arg, gui_tool_utils.MODALT)): self.mirror(last, self.point, True) else: self.mirror(last, self.point) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): self.extendedCopy = True else: self.finish(cont=True)
def getSVG(section, allOn=False, renderMode="Wireframe", showHidden=False, showFill=False, scale=1, linewidth=1, fontsize=1, techdraw=False, rotation=0): """getSVG(section,[allOn,renderMode,showHidden,showFill,scale,linewidth,fontsize]) : returns an SVG fragment from an Arch section plane. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern""" if not section.Objects: return import Part, DraftGeomUtils p = FreeCAD.Placement(section.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) objs = Draft.getGroupContents(section.Objects, walls=True, addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces and Draft objects spaces = [] nonspaces = [] drafts = [] windows = [] cutface = None for o in objs: if Draft.getType(o) == "Space": spaces.append(o) elif Draft.getType(o) in ["Dimension", "Annotation"]: drafts.append(o) elif o.isDerivedFrom("Part::Part2DObject"): drafts.append(o) else: nonspaces.append(o) if Draft.getType(o) == "Window": windows.append(o) objs = nonspaces svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if renderMode in ["Solid", 1]: # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(section.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if showHidden: render.cut(section.Shape, showHidden) else: render.cut(section.Shape) svg += '<g transform="scale(1,-1)">\n' svg += render.getViewSVG(linewidth="LWPlaceholder") svg += fillpattern svg += render.getSectionSVG(linewidth="SWPlaceholder", fillpattern="sectionfill") if showHidden: svg += render.getHiddenSVG(linewidth="LWPlaceholder") svg += '</g>\n' # print render.info() else: # render using the Drawing module import Drawing, Part shapes, hshapes, sshapes, cutface, cutvolume, invcutvolume = getCutShapes( objs, section, showHidden) if shapes: baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG(baseshape, direction) if svgf: svgf = svgf.replace('stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width="1"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width:0.01', 'stroke-width:LWPlaceholder') svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) svgh = Drawing.projectToSVG(hshapes, direction) if svgh: svgh = svgh.replace('stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width="1"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width:0.01', 'stroke-width:LWPlaceholder') svgh = svgh.replace( 'fill="none"', 'fill="none"\nstroke-dasharray="DAPlaceholder"') svg += svgh if sshapes: svgs = "" if showFill: #svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: #f = Draft.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0)) # temporarily disabling fill patterns f = Draft.getSVG(s, direction=direction.negative(), linewidth=0, fillstyle="#aaaaaa", color=(0, 0, 0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) svgs += Drawing.projectToSVG(sshapes, direction) if svgs: svgs = svgs.replace('stroke-width="0.35"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width="1"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.01', 'stroke-width:SWPlaceholder') svgs = svgs.replace('stroke-width="0.35 px"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.35', 'stroke-width:SWPlaceholder') svg += svgs scaledlinewidth = linewidth / scale st = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Arch").GetFloat( "CutLineThickness", 2) yt = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Arch").GetFloat( "SymbolLineThickness", 0.6) da = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Arch").GetString( "archHiddenPattern", "30,10") da = da.replace(" ", "") svg = svg.replace('LWPlaceholder', str(scaledlinewidth) + 'px') svg = svg.replace('SWPlaceholder', str(scaledlinewidth * st) + 'px') svg = svg.replace('DAPlaceholder', str(da)) if drafts: if not techdraw: svg += '<g transform="scale(1,-1)">' for d in drafts: svg += Draft.getSVG(d, scale=scale, linewidth=linewidth * yt, fontsize=fontsize, direction=direction, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' # filter out spaces not cut by the section plane if cutface and spaces: spaces = [ s for s in spaces if s.Shape.BoundBox.intersect(cutface.BoundBox) ] if spaces: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in spaces: svg += Draft.getSVG(s, scale=scale, linewidth=linewidth * yt, fontsize=fontsize, direction=direction, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' # add additional edge symbols from windows if cutface and windows: cutwindows = [ w.Name for w in windows if w.Shape.BoundBox.intersect(cutface.BoundBox) ] if windows: sh = [] for w in windows: if not hasattr(w.Proxy, "sshapes"): w.Proxy.execute(w) if hasattr(w.Proxy, "sshapes"): if w.Proxy.sshapes and (w.Name in cutwindows): c = Part.makeCompound(w.Proxy.sshapes) c.Placement = w.Placement sh.append(c) # buggy for now... #if hasattr(w.Proxy,"vshapes"): # if w.Proxy.vshapes: # c = Part.makeCompound(w.Proxy.vshapes) # c.Placement = w.Placement # sh.append(c) if sh: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in sh: svg += Draft.getSVG(s, scale=scale, linewidth=linewidth * yt, fontsize=fontsize, fillstyle="none", direction=direction, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' #print "complete node:",svg return svg
def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direction=None,linestyle=None,color=None,linespacing=None,techdraw=False,rotation=0): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' # if this is a group, gather all the svg views of its children if hasattr(obj,"isDerivedFrom"): if obj.isDerivedFrom("App::DocumentObjectGroup"): svg = "" for child in obj.Group: svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) return svg pathdata = [] svg = "" linewidth = float(linewidth)/scale fontsize = (float(fontsize)/scale)/2 if linespacing: linespacing = float(linespacing)/scale else: linespacing = 0.5 #print obj.Label," line spacing ",linespacing,"scale ",scale pointratio = .75 # the number of times the dots are smaller than the arrow size plane = None if direction: if isinstance(direction,FreeCAD.Vector): if direction != Vector(0,0,0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(Vector(0,0,0),direction.negative().negative(),0) elif isinstance(direction,WorkingPlane.plane): plane = direction stroke = "#000000" if color: if "#" in color: stroke = color else: stroke = getrgb(color) elif gui: if hasattr(obj,"ViewObject"): if hasattr(obj.ViewObject,"LineColor"): stroke = getrgb(obj.ViewObject.LineColor) def getPath(edges=[],wires=[],pathname=None): import Part,DraftGeomUtils svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] for w in wires: w1=w.copy() w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs=() #skipped for the first edge for edgeindex,e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' else: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG(e,drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = (getProj(c.value((c.LastParameter-\ c.FirstParameter)/2.0), plane), \ getProj(vs[-1].Point, plane)) else: endpoints = (getProj(vs[-1].Point), plane) # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -=180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' else: bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline=bspline.approximateBSpline(0.05,50, 3,'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree>3: #should not happen raise AssertionError elif bezierseg.Degree==1: edata +='L ' elif bezierseg.Degree==2: edata +='Q ' elif bezierseg.Degree==3: edata +='C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) +' '+ str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg def getCircle(edge): cen = getProj(edge.Curve.Center, plane) rad = edge.Curve.Radius if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis if round(edge.Curve.Axis.getAngle(drawing_plane_normal),2) == 0: # perpendicular projection: circle svg = '<circle cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" r="' + str(rad)+'" ' else: # any other projection: ellipse svg = '<path d="' svg += getDiscretized(edge, plane) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getEllipse(edge): cen = getProj(edge.Curve.Center, plane) mir = edge.Curve.MinorRadius mar = edge.Curve.MajorRadius svg = '<ellipse cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" rx="' + str(mar) svg += '" ry="' + str(mir)+'" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getArrow(arrowtype,point,arrowsize,color,linewidth,angle=0): svg = "" if gui: if not obj.ViewObject: return svg if obj.ViewObject.ArrowType == "Circle": svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y) svg += '" r="'+str(arrowsize)+'" ' svg += 'fill="none" stroke="'+ color + '" ' svg += 'style="stroke-width:'+ str(linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Dot": svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y) svg += '" r="'+str(arrowsize)+'" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Arrow": svg += '<path transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick": svg += '<path transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick-2": svg += '<line transform="rotate('+str(math.degrees(angle)+45) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="'+ color +'" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:'+ str(linewidth) +'" ' svg += 'x1="-'+ str(arrowsize*2) +'" y1="0" ' svg += 'x2="' + str(arrowsize*2) +'" y2="0" />\n' else: print("getSVG: arrow type not implemented") return svg def getOvershoot(point,shootsize,color,linewidth,angle=0): svg = '<line transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="'+ color +'" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:'+ str(linewidth) +'" ' svg += 'x1="0" y1="0" ' svg += 'x2="'+ str(shootsize*-1) +'" y2="0" />\n' return svg def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): if isinstance(angle,FreeCAD.Rotation): if not plane: angle = angle.Angle else: if plane.axis.getAngle(angle.Axis) < 0.001: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis)-math.pi) < 0.001: return "" # text is perpendicular to view, so it shouldn't appear else: angle = 0 #TODO maybe there is something better to do here? if not isinstance(text,list): text = text.split("\n") if align.lower() == "center": anchor = "middle" elif align.lower() == "left": anchor = "start" else: anchor = "end" if techdraw: svg = "" for i in range(len(text)): t = text[i] if sys.version_info.major < 3 and (not isinstance(t,unicode)): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") svg += '<text fill="' + color +'" font-size="' + str(fontsize) + '" ' svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';' svg += 'font-family:'+ fontname +'" ' svg += 'transform="rotate('+str(math.degrees(angle)) svg += ','+ str(base.x) + ',' + str(base.y-linespacing*i) + ') ' svg += 'translate(' + str(base.x) + ',' + str(base.y-linespacing*i) + ') ' svg += 'scale(1,-1)" ' #svg += '" freecad:skip="1"' svg += '>\n' + t + '</text>\n' else: svg = '<text fill="' svg += color +'" font-size="' svg += str(fontsize) + '" ' svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';' svg += 'font-family:'+ fontname +'" ' svg += 'transform="rotate('+str(math.degrees(angle)) svg += ','+ str(base.x) + ',' + str(base.y) + ') ' if flip: svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')' else: svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')' #svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') ' if flip: svg += ' scale(1,-1) ' else: svg += ' scale(1,1) ' svg += '" freecad:skip="1"' svg += '>\n' if len(text) == 1: try: svg += text[0] except: svg += text[0].decode("utf8") else: for i in range(len(text)): if i == 0: svg += '<tspan>' else: svg += '<tspan x="0" dy="'+str(linespacing)+'">' try: svg += text[i] except: svg += text[i].decode("utf8") svg += '</tspan>\n' svg += '</text>\n' return svg if not obj: pass elif isinstance(obj,Part.Shape): if "#" in fillstyle: fill = fillstyle elif fillstyle == "shape color": fill = "#888888" else: fill = 'url(#'+fillstyle+')' lstyle = getLineStyle(linestyle, scale) svg += getPath(obj.Edges,pathname="") elif getType(obj) == "Dimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"p1"): prx = obj.ViewObject.Proxy ts = (len(prx.string)*obj.ViewObject.FontSize.Value)/4.0 rm = ((prx.p3.sub(prx.p2)).Length/2.0)-ts p2a = getProj(prx.p2.add(DraftVecUtils.scaleTo(prx.p3.sub(prx.p2),rm)), plane) p2b = getProj(prx.p3.add(DraftVecUtils.scaleTo(prx.p2.sub(prx.p3),rm)), plane) p1 = getProj(prx.p1, plane) p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) p4 = getProj(prx.p4, plane) tbase = getProj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() rv = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(1,0,0)) angle = -DraftVecUtils.angle(getProj(rv, plane)) #angle = -DraftVecUtils.angle(p3.sub(p2)) # drawing lines svg = '<path ' if obj.ViewObject.DisplayMode == "2D": tangle = angle if tangle > math.pi/2: tangle = tangle-math.pi #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle+math.pi #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) if rotation != 0: #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string if abs(tangle+math.radians(rotation)) < 0.0001: tangle += math.pi tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase.add(Vector(0,-2.0/scale,0)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p2a.x)+' '+str(p2a.y)+' ' svg += 'M '+str(p2b.x)+' '+str(p2b.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="'+str(p1.x)+' '+str(p1.y)+'" ' svg += 'freecad:basepoint2="'+str(p4.x)+' '+str(p4.y)+'" ' svg += 'freecad:dimpoint="'+str(p2.x)+' '+str(p2.y)+'"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(obj.ViewObject,"DimOvershoot") and obj.ViewObject.DimOvershoot.Value: shootsize = obj.ViewObject.DimOvershoot.Value/pointratio svg += getOvershoot(p2,shootsize,stroke,linewidth,angle) svg += getOvershoot(p3,shootsize,stroke,linewidth,angle+math.pi) if hasattr(obj.ViewObject,"ExtOvershoot") and obj.ViewObject.ExtOvershoot.Value: shootsize = obj.ViewObject.ExtOvershoot.Value/pointratio shootangle = -DraftVecUtils.angle(p1.sub(p2)) svg += getOvershoot(p2,shootsize,stroke,linewidth,shootangle) svg += getOvershoot(p3,shootsize,stroke,linewidth,shootangle) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): arrowsize = obj.ViewObject.ArrowSize.Value/pointratio if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle = angle+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle+math.pi) # drawing text svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "AngularDimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"circle"): prx = obj.ViewObject.Proxy # drawing arc fill= "none" lstyle = getLineStyle(linestyle, scale) if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: if hasattr(prx,"circle1"): svg += getPath([prx.circle1]) svg += getPath([prx.circle2]) else: svg += getPath([prx.circle]) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio arrowlength = 4*obj.ViewObject.ArrowSize.Value u1 = getProj((prx.circle.valueAt(prx.circle.FirstParameter+arrowlength)).sub(prx.circle.valueAt(prx.circle.FirstParameter)), plane) u2 = getProj((prx.circle.valueAt(prx.circle.LastParameter-arrowlength)).sub(prx.circle.valueAt(prx.circle.LastParameter)), plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1+math.pi angle2 = angle2+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle1) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": t = prx.circle.tangentAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0) t = getProj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi/2) or (tangle > math.pi/2): tangle = tangle + math.pi tbase = getProj(prx.circle.valueAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0), plane) tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2.0/scale,0),tangle)) #print(tbase) else: tangle = 0 tbase = getProj(prx.tbase, plane) svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property def format_point(coords, action='L'): return "{action}{x},{y}".format( x=coords.x, y=coords.y, action=action ) # Draw multisegment line proj_points = list(map(lambda x: getProj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.format( stroke=stroke, linewidth=linewidth, directions=path_dir_str ) svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) angle = -DraftVecUtils.angle(getProj(last_segment, plane)) + math.pi svg += getArrow( arrowtype=obj.ViewObject.ArrowType, point=proj_points[-1], arrowsize=obj.ViewObject.ArrowSize.Value/pointratio, color=stroke, linewidth=linewidth, angle=angle ) # print text if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: fontname = obj.ViewObject.TextFont position = getProj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += getText(stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif getType(obj) in ["Annotation","DraftText"]: "returns an svg representation of a document annotation" if gui: if not obj.ViewObject: print ("export of texts to SVG is only available in GUI mode") else: n = obj.ViewObject.FontName if getType(obj) == "Annotation": p = getProj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText p = getProj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += getText(stroke,fontsize,n,r,p,t,linespacing,j) elif getType(obj) == "Axis": "returns the SVG representation of an Arch Axis system" if gui: if not obj.ViewObject: print ("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject lorig = getLineStyle(linestyle, scale) fill = 'none' rad = vobj.BubbleSize.Value/2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += getPath([e]) lstyle = "none" pos = ["Start"] if hasattr(vobj,"BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start","End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad,rad,rad)) svg += getCircle(Part.makeCircle(rad,center)) if hasattr(vobj.Proxy,"bubbletexts"): if len (vobj.Proxy.bubbletexts) >= n: svg += '<text fill="' + stroke + '" ' svg += 'font-size="' + str(rad) + '" ' svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="translate(' + str(center.x+rad/4.0) + ',' + str(center.y-rad/3.0) + ') ' svg += 'scale(1,-1)"> ' svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '</tspan>\n' svg += '</text>\n' n += 1 elif getType(obj) == "Pipe": fill = stroke lstyle = getLineStyle(linestyle, scale) if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve,Part.Circle): svg += getCircle(f.Edges[0]) elif getType(obj) == "Rebar": fill = "none" lstyle = getLineStyle(linestyle, scale) if obj.Proxy: if not hasattr(obj.Proxy,"wires"): obj.Proxy.execute(obj) if hasattr(obj.Proxy,"wires"): svg += getPath(wires=obj.Proxy.wires) elif getType(obj) == "PipeConnector": pass elif getType(obj) == "Space": "returns an SVG fragment for the text of a space" if gui: if not obj.ViewObject: print ("export of spaces to SVG is only available in GUI mode") else: c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = obj.ViewObject.Proxy.text1.string.getValues() t2 = obj.ViewObject.Proxy.text2.string.getValues() scale = obj.ViewObject.FirstLine.Value/obj.ViewObject.FontSize.Value f1 = fontsize*scale p2 = FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation.getValue().getValue()) lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation.getValue().getValue()) p1 = p2.add(lspc) j = obj.ViewObject.TextAlign svg += getText(c,f1,n,a,getProj(p1, plane),t1,linespacing,j,flip=True) if t2: ofs = FreeCAD.Vector(0,lspc.Length,0) if a: ofs = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),-rotation).multVec(ofs) svg += getText(c,fontsize,n,a,getProj(p1, plane).add(ofs),t2,linespacing,j,flip=True) elif obj.isDerivedFrom('Part::Feature'): if obj.Shape.isNull(): return '' # setting fill if obj.Shape.Faces: if gui: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None if (m != "Wireframe"): if fillstyle == "shape color": fill = getrgb(obj.ViewObject.ShapeColor,testbw=False) fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = 'url(#'+fillstyle+')' svg += getPattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' lstyle = getLineStyle(linestyle, scale) if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i,f in enumerate(obj.Shape.Faces): svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) else: for i,w in enumerate(obj.Shape.Wires): svg += getPath(w.Edges,pathname='%s_w%04d' % \ (obj.Name,i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i,e in enumerate(obj.Shape.Edges): if (DraftGeomUtils.findEdge(e,wiredEdges) == None): svg += getPath([e],pathname='%s_nwe%04d' % \ (obj.Name,i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve,Part.Circle): svg = getCircle(obj.Shape.Edges[0]) else: svg = getPath(obj.Shape.Edges) if FreeCAD.GuiUp: if hasattr(obj.ViewObject,"EndArrow") and hasattr(obj.ViewObject,"ArrowType") and (len(obj.Shape.Vertexes) > 1): if obj.ViewObject.EndArrow: p1 = getProj(obj.Shape.Vertexes[-2].Point, plane) p2 = getProj(obj.Shape.Vertexes[-1].Point, plane) angle = -DraftVecUtils.angle(p2.sub(p1)) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">'+svg+'</g>' return svg
def renderClassicSVG(self, objs, direction, base=None): """returns an svg fragment from a SectionPlane object, a direction vector and optionally a base point""" def intersection(p1, p2, p3, p4): "returns the intersection of line (p1,p2) with plane (p3,p4)" # http://paulbourke.net/geometry/planeline/ dn = p4.dot(p2.sub(p1)) if dn != 0: u = (p4.dot(p3.sub(p1))) / dn p = p1.add((p2.sub(p1)).scale(u, u, u)) return p else: # line is parallel to normal vp = fcvec.project(p3.sub(p1), p2.sub(p1)) l = vp.Length if vp.getAngle(p2.sub(p1)) > 1: l = -l return fcvec.scaleTo(p2.sub(p1), l) def getFirstIndex(list1, list2): "returns the first index from list2 where there is an item of list1" for i1 in range(len(list1)): for i2 in range(len(list2)): if list1[i1].hashCode() == list2[i2].hashCode(): return i2 return None def getLastIndex(list1, list2): "returns the last index from list2 where there is an item of list1" i = None for i1 in range(len(list1)): for i2 in range(len(list2)): if list1[i1].hashCode() == list2[i2].hashCode(): i = i2 return i def findPrevious(base, dir, faces): "returns the highest index in faces that is crossed by the given line" for i in range(len(faces) - 1, -1, -1): print "p1:", base, " p2: ", base.add(dir) obb = faces[i].BoundBox print "bo: ", obb op = intersection(base, base.add(dir), faces[i].CenterOfMass, faces[i].normalAt(0, 0)) print "int:", op if obb.isInside(op): dv = op.sub(base) if dv.getAngle(dir) < math.pi / 2: return i return None def findNext(base, dir, faces): "returns the lowest index in faces that is crossed by the given line" for i in range(len(faces)): obb = faces[i].BoundBox op = intersection(base, base.add(dir), faces[i].CenterOfMass, faces[i].normalAt(0, 0)) if obb.isInside(op): dv = op.sub(base) if dv.getAngle(dir) > math.pi / 2: return i return None print "getting representation at ", direction, " =======================================>" # using Draft WorkingPlane plane = None plane = WorkingPlane.plane() if direction != Vector(0, 0, 0): plane.alignToPointAndAxis(Vector(0, 0, 0), fcvec.neg(direction), 0) else: direction = Vector(0, 0, -1) print "plane:", plane sortedFaces = [] if not base: # getting the base point = first point from the bounding box bb = FreeCAD.BoundBox() for o in objs: bb.add(o.Shape.BoundBox) rad = bb.DiagonalLength / 2 rv = bb.Center.add(direction) rv = fcvec.scaleTo(rv, rad) rv = fcvec.neg(rv) base = bb.Center.add(rv) print "base:", base # getting faces unsortedFaces = [] notFoundFaces = [] for o in objs: unsortedFaces.append(o.Name) unsortedFaces.extend(o.Shape.Faces[:]) print "analyzing ", len(unsortedFaces), " faces" for face in unsortedFaces: if isinstance(face, str): print "OBJECT ", face, " =======================================>" continue print "testing face ", unsortedFaces.index(face) # testing if normal points outwards normal = face.normalAt(0, 0) if normal.getAngle(direction) <= math.pi / 2: print "normal pointing outwards" continue fprev = 0 fnext = len(sortedFaces) notFound = True print "checking ", len(face.Vertexes), " verts" for v in face.Vertexes: vprev = findPrevious(v.Point, direction, sortedFaces) vnext = findNext(v.Point, direction, sortedFaces) print "temp indexes:", vprev, vnext if (vprev != None): notfound = False if (vprev > fprev): fprev = vprev if (vnext != None): notfound = False if (vnext < fnext): fnext = vnext print "fprev:", fprev print "fnext:", fnext print "notFound", notFound if fnext < fprev: raise "Error, impossible index" elif fnext == fprev: sortedFaces.insert(fnext, face) else: sortedFaces.insert(fnext, face) print len(sortedFaces), " sorted faces:", sortedFaces # building SVG representation in correct order svg = '' for f in sortedFaces: svg += self.getPath(f, plane) return svg
def onChanged(self, obj, prop): if prop in ["Source", "RenderingMode", "ShowCut"]: import Part, DraftGeomUtils if hasattr(obj, "Source"): if obj.Source: if obj.Source.Objects: objs = Draft.getGroupContents(obj.Source.Objects, walls=True, addgroups=True) objs = Draft.removeHidden(objs) # separate spaces self.spaces = [] os = [] for o in objs: if Draft.getType(o) == "Space": self.spaces.append(o) else: os.append(o) objs = os self.svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if obj.RenderingMode == "Solid": # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(obj.Source.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if hasattr(obj, "ShowCut"): render.cut(obj.Source.Shape, obj.ShowCut) else: render.cut(obj.Source.Shape) self.svg += '<g transform="scale(1,-1)">\n' self.svg += render.getViewSVG( linewidth="LWPlaceholder") self.svg += fillpattern self.svg += render.getSectionSVG( linewidth="SWPlaceholder", fillpattern="sectionfill") if hasattr(obj, "ShowCut"): if obj.ShowCut: self.svg += render.getHiddenSVG( linewidth="LWPlaceholder") self.svg += '</g>\n' # print render.info() else: # render using the Drawing module import Drawing, Part shapes = [] hshapes = [] sshapes = [] p = FreeCAD.Placement(obj.Source.Placement) self.direction = p.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) for o in objs: if o.isDerivedFrom("Part::Feature"): if o.Shape.isNull(): pass #FreeCAD.Console.PrintWarning(translate("Arch","Skipping empty object: ")+o.Name) elif o.Shape.isValid(): if hasattr(obj.Source, "OnlySolids"): if obj.Source.OnlySolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape) else: shapes.extend(o.Shape.Solids) else: FreeCAD.Console.PrintWarning( translate( "Arch", "Skipping invalid object: ") + o.Name) cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume( obj.Source.Shape.copy(), shapes) if cutvolume: nsh = [] for sh in shapes: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires( s.Edges) for w in wires: f = Part.Face(w) sshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" sshapes.append(s) nsh.extend(c.Solids) #sshapes.append(s) if hasattr(obj, "ShowCut"): if obj.ShowCut: c = sol.cut(invcutvolume) hshapes.append(c) shapes = nsh if shapes: self.shapes = shapes self.baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG( self.baseshape, self.direction) if svgf: svgf = svgf.replace( 'stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace( 'stroke-width="1"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace( 'stroke-width:0.01', 'stroke-width:LWPlaceholder') self.svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) self.hiddenshape = hshapes svgh = Drawing.projectToSVG( hshapes, self.direction) if svgh: svgh = svgh.replace( 'stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace( 'stroke-width="1"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace( 'stroke-width:0.01', 'stroke-width:LWPlaceholder') svgh = svgh.replace( 'fill="none"', 'fill="none"\nstroke-dasharray="DAPlaceholder"' ) self.svg += svgh if sshapes: svgs = "" if hasattr(obj, "ShowFill"): if obj.ShowFill: svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: f = Draft.getSVG( s, direction=self.direction. negative(), linewidth=0, fillstyle="sectionfill", color=(0, 0, 0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) self.sectionshape = sshapes svgs += Drawing.projectToSVG( sshapes, self.direction) if svgs: svgs = svgs.replace( 'stroke-width="0.35"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace( 'stroke-width="1"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace( 'stroke-width:0.01', 'stroke-width:SWPlaceholder') svgs = svgs.replace( 'stroke-width="0.35 px"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace( 'stroke-width:0.35', 'stroke-width:SWPlaceholder') self.svg += svgs
def getSVG(section, allOn=False, renderMode="Wireframe", showHidden=False, showFill=False, scale=1, linewidth=1, fontsize=1): """getSVG(section,[allOn,renderMode,showHidden,showFill,scale,linewidth,fontsize]) : returns an SVG fragment from an Arch section plane. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern""" if not section.Objects: return import DraftGeomUtils p = FreeCAD.Placement(section.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) objs = Draft.getGroupContents(section.Objects, walls=True, addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces spaces = [] nonspaces = [] for o in objs: if Draft.getType(o) == "Space": spaces.append(o) else: nonspaces.append(o) objs = nonspaces svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if renderMode == "Solid": # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(section.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if showHidden: render.cut(section.Shape, showHidden) else: render.cut(section.Shape) svg += '<g transform="scale(1,-1)">\n' svg += render.getViewSVG(linewidth="LWPlaceholder") svg += fillpattern svg += render.getSectionSVG(linewidth="SWPlaceholder", fillpattern="sectionfill") if showHidden: svg += render.getHiddenSVG(linewidth="LWPlaceholder") svg += '</g>\n' # print render.info() else: # render using the Drawing module import Drawing, Part shapes = [] hshapes = [] sshapes = [] for o in objs: if o.isDerivedFrom("Part::Feature"): if o.Shape.isNull(): pass elif o.Shape.isValid(): if section.OnlySolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape) else: print section.Label, ": Skipping invalid object:", o.Label cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume( section.Shape.copy(), shapes) if cutvolume: nsh = [] for sh in shapes: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires(s.Edges) for w in wires: f = Part.Face(w) sshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" sshapes.append(s) nsh.extend(c.Solids) #sshapes.append(s) if showHidden: c = sol.cut(invcutvolume) hshapes.append(c) shapes = nsh if shapes: baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG(baseshape, direction) if svgf: svgf = svgf.replace('stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width="1"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width:0.01', 'stroke-width:LWPlaceholder') svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) svgh = Drawing.projectToSVG(hshapes, direction) if svgh: svgh = svgh.replace('stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width="1"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width:0.01', 'stroke-width:LWPlaceholder') svgh = svgh.replace( 'fill="none"', 'fill="none"\nstroke-dasharray="DAPlaceholder"') svg += svgh if sshapes: svgs = "" if showFill: svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: f = Draft.getSVG(s, direction=direction.negative(), linewidth=0, fillstyle="sectionfill", color=(0, 0, 0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) svgs += Drawing.projectToSVG(sshapes, direction) if svgs: svgs = svgs.replace('stroke-width="0.35"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width="1"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.01', 'stroke-width:SWPlaceholder') svgs = svgs.replace('stroke-width="0.35 px"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.35', 'stroke-width:SWPlaceholder') svg += svgs linewidth = linewidth / scale st = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Arch").GetFloat( "CutLineThickness", 2) da = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Arch").GetString( "archHiddenPattern", "30,10") da = da.replace(" ", "") svg = svg.replace('LWPlaceholder', str(linewidth) + 'px') svg = svg.replace('SWPlaceholder', str(linewidth * st) + 'px') svg = svg.replace('DAPlaceholder', str(da)) if spaces and round( direction.getAngle(FreeCAD.Vector(0, 0, 1)), Draft.precision()) in [0, round(math.pi, Draft.precision())]: svg += '<g transform="scale(1,-1)">' for s in spaces: svg += Draft.getSVG(s, scale=scale, fontsize=fontsize, direction=direction) svg += '</g>' # print "complete node:",svg return svg
def get_svg(obj, scale=1, linewidth=0.35, fontsize=12, fillstyle="shape color", direction=None, linestyle=None, color=None, linespacing=None, techdraw=False, rotation=0, fillspaces=False, override=True): """Return a string containing an SVG representation of the object. Paramaeters ----------- scale: float, optional It defaults to 1. It allows scaling line widths down, so they are resolution-independent. linewidth: float, optional It defaults to 0.35. fontsize: float, optional It defaults to 12, which is interpreted as `pt` unit (points). It is used if the given object contains any text. fillstyle: str, optional It defaults to 'shape color'. direction: Base::Vector3, optional It defaults to `None`. It is an arbitrary projection vector or a `WorkingPlane.Plane` instance. linestyle: optional It defaults to `None`. color: optional It defaults to `None`. linespacing: float, optional It defaults to `None`. techdraw: bool, optional It defaults to `False`. If it is `True`, it sets some options for generating SVG strings for displaying inside TechDraw. rotation: float, optional It defaults to 0. fillspaces: bool, optional It defaults to `False`. override: bool, optional It defaults to `True`. """ # If this is a group, recursively call this function to gather # all the SVG strings from the contents of the group if hasattr(obj, "isDerivedFrom"): if (obj.isDerivedFrom("App::DocumentObjectGroup") or utils.get_type(obj) == "Layer"): svg = "" for child in obj.Group: svg += get_svg(child, scale, linewidth, fontsize, fillstyle, direction, linestyle, color, linespacing, techdraw, rotation, fillspaces, override) return svg pathdata = [] svg = "" linewidth = float(linewidth) / scale if not override: if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "LineWidth"): if hasattr(obj.ViewObject.LineWidth, "Value"): lw = obj.ViewObject.LineWidth.Value else: lw = obj.ViewObject.LineWidth linewidth = lw * linewidth fontsize = (float(fontsize) / scale) / 2 if linespacing: linespacing = float(linespacing) / scale else: linespacing = 0.5 # print(obj.Label, "line spacing", linespacing, "scale", scale) # The number of times the dots are smaller than the arrow size pointratio = 0.75 plane = None if direction: if isinstance(direction, App.Vector): if direction != App.Vector(0, 0, 0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(App.Vector(0, 0, 0), direction.negative().negative(), 0) elif isinstance(direction, WorkingPlane.plane): plane = direction stroke = "#000000" if color and override: if "#" in color: stroke = color else: stroke = utils.get_rgb(color) elif App.GuiUp: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "LineColor"): stroke = utils.get_rgb(obj.ViewObject.LineColor) elif hasattr(obj.ViewObject, "TextColor"): stroke = utils.get_rgb(obj.ViewObject.TextColor) lstyle = "none" if override: lstyle = get_line_style(linestyle, scale) else: if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "DrawStyle"): lstyle = get_line_style(obj.ViewObject.DrawStyle, scale) if not obj: pass elif isinstance(obj, Part.Shape): svg = _svg_shape(svg, obj, plane, fillstyle, pathdata, stroke, linewidth, lstyle) elif utils.get_type(obj) in ["Dimension", "LinearDimension"]: svg = _svg_dimension(obj, plane, scale, linewidth, fontsize, stroke, pointratio, techdraw, rotation) elif utils.get_type(obj) == "AngularDimension": if not App.GuiUp: _wrn("Export of dimensions to SVG is only available in GUI mode") if App.GuiUp: if obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy, "circle"): prx = obj.ViewObject.Proxy # drawing arc fill = "none" if obj.ViewObject.DisplayMode == "2D": svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle]) else: if hasattr(prx, "circle1"): svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle1]) svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle2]) else: svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle]) # drawing arrows if hasattr(obj.ViewObject, "ArrowType"): p2 = get_proj(prx.p2, plane) p3 = get_proj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value / pointratio arrowlength = 4 * obj.ViewObject.ArrowSize.Value _v1a = prx.circle.valueAt(prx.circle.FirstParameter + arrowlength) _v1b = prx.circle.valueAt(prx.circle.FirstParameter) _v2a = prx.circle.valueAt(prx.circle.LastParameter - arrowlength) _v2b = prx.circle.valueAt(prx.circle.LastParameter) u1 = get_proj(_v1a - _v1b, plane) u2 = get_proj(_v2a - _v2b, plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1 + math.pi angle2 = angle2 + math.pi svg += get_arrow(obj, obj.ViewObject.ArrowType, p2, arrowsize, stroke, linewidth, angle1) svg += get_arrow(obj, obj.ViewObject.ArrowType, p3, arrowsize, stroke, linewidth, angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": _diff = (prx.circle.LastParameter - prx.circle.FirstParameter) t = prx.circle.tangentAt(prx.circle.FirstParameter + _diff / 2.0) t = get_proj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi / 2) or (tangle > math.pi / 2): tangle = tangle + math.pi _diff = (prx.circle.LastParameter - prx.circle.FirstParameter) _va = prx.circle.valueAt(prx.circle.FirstParameter + _diff / 2.0) tbase = get_proj(_va, plane) _v = App.Vector(0, 2.0 / scale, 0) tbase = tbase + DraftVecUtils.rotate(_v, tangle) # print(tbase) else: tangle = 0 tbase = get_proj(prx.tbase, plane) svg += svgtext.get_text(plane, techdraw, stroke, fontsize, obj.ViewObject.FontName, tangle, tbase, prx.string) elif utils.get_type(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # Some Labels may have no Line property # Draw multisegment line proj_points = list(map(lambda x: get_proj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = '<path ' svg_path += 'fill="none" ' svg_path += 'stroke="{}" '.format(stroke) svg_path += 'stroke-width="{}" '.format(linewidth) svg_path += 'd="{}"'.format(path_dir_str) svg_path += '/>' svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = App.Vector(obj.Points[-1] - obj.Points[-2]) _v = get_proj(last_segment, plane) angle = -DraftVecUtils.angle(_v) + math.pi svg += get_arrow(obj, obj.ViewObject.ArrowType, proj_points[-1], obj.ViewObject.ArrowSize.Value / pointratio, stroke, linewidth, angle) if not App.GuiUp: _wrn("Export of texts to SVG is only available in GUI mode") # print text if App.GuiUp: fontname = obj.ViewObject.TextFont position = get_proj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += svgtext.get_text(plane, techdraw, stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif utils.get_type(obj) in ["Annotation", "DraftText", "Text"]: # returns an svg representation of a document annotation if not App.GuiUp: _wrn("Export of texts to SVG is only available in GUI mode") if App.GuiUp: n = obj.ViewObject.FontName if utils.get_type(obj) == "Annotation": p = get_proj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText (old) or Text (new, 0.19) p = get_proj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += svgtext.get_text(plane, techdraw, stroke, fontsize, n, r, p, t, linespacing, j) elif utils.get_type(obj) == "Axis": # returns the SVG representation of an Arch Axis system if not App.GuiUp: _wrn("Export of axes to SVG is only available in GUI mode") if App.GuiUp: vobj = obj.ViewObject lorig = lstyle fill = 'none' rad = vobj.BubbleSize.Value / 2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[e]) lstyle = "none" pos = ["Start"] if hasattr(vobj, "BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start", "End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad, rad, rad)) svg += get_circle(plane, fill, stroke, linewidth, lstyle, Part.makeCircle(rad, center)) if (hasattr(vobj.Proxy, "bubbletexts") and len(vobj.Proxy.bubbletexts) >= n): bubb = vobj.Proxy.bubbletexts svg += '<text ' svg += 'fill="{}" '.format(stroke) svg += 'font-size="{}" '.format(rad) svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="' svg += 'translate({},{}) '.format( center.x + rad / 4.0, center.y - rad / 3.0) svg += 'scale(1,-1)"> ' svg += '<tspan>' svg += bubb[n].string.getValues()[0] svg += '</tspan>\n' svg += '</text>\n' n += 1 lstyle = lorig elif utils.get_type(obj) == "Pipe": fill = stroke if obj.Base and obj.Diameter: svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve, Part.Circle): svg += get_circle(plane, fill, stroke, linewidth, lstyle, f.Edges[0]) elif utils.get_type(obj) == "Rebar": fill = "none" basewire = obj.Base.Shape.Wires[0].copy() # Not applying rounding because the results are not correct # if hasattr(obj, "Rounding") and obj.Rounding: # basewire = DraftGeomUtils.filletWire( # basewire, obj.Rounding * obj.Diameter.Value # ) wires = [] for placement in obj.PlacementList: wire = basewire.copy() wire.Placement = placement.multiply(basewire.Placement) wires.append(wire) svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, wires=wires) elif utils.get_type(obj) == "PipeConnector": pass elif utils.get_type(obj) == "Space": fill_opacity = 1 # returns an SVG fragment for the text of a space if not App.GuiUp: _wrn("Export of spaces to SVG is only available in GUI mode") if App.GuiUp: vobj = obj.ViewObject if fillspaces and hasattr(obj, "Proxy"): if not hasattr(obj.Proxy, "face"): obj.Proxy.getArea(obj, notouch=True) if hasattr(obj.Proxy, "face"): # setting fill if App.GuiUp: fill = utils.get_rgb(vobj.ShapeColor, testbw=False) fill_opacity = 1 - vobj.Transparency / 100.0 else: fill = "#888888" svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, wires=[obj.Proxy.face.OuterWire]) c = utils.get_rgb(vobj.TextColor) n = vobj.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = vobj.Proxy.text1.string.getValues() t2 = vobj.Proxy.text2.string.getValues() scale = vobj.FirstLine.Value / vobj.FontSize.Value f1 = fontsize * scale if round(plane.axis.getAngle(App.Vector(0, 0, 1)), 2) not in [0, 3.14]: # if not in XY view, place the label at center p2 = obj.Shape.CenterOfMass else: _v = vobj.Proxy.coords.translation.getValue().getValue() p2 = obj.Placement.multVec(App.Vector(_v)) _h = vobj.Proxy.header.translation.getValue().getValue() lspc = App.Vector(_h) p1 = p2 + lspc j = vobj.TextAlign t3 = svgtext.get_text(plane, techdraw, c, f1, n, a, get_proj(p1, plane), t1, linespacing, j, flip=True) svg += t3 if t2: ofs = App.Vector(0, -lspc.Length, 0) if a: Z = App.Vector(0, 0, 1) ofs = App.Rotation(Z, -rotation).multVec(ofs) t4 = svgtext.get_text(plane, techdraw, c, fontsize, n, a, get_proj(p1, plane).add(ofs), t2, linespacing, j, flip=True) svg += t4 elif hasattr(obj, 'Shape'): # In the past we tested for a Part Feature # elif obj.isDerivedFrom('Part::Feature'): # # however, this doesn't work for App::Links; instead we # test for a 'Shape'. All Part::Features should have a Shape, # and App::Links can have one as well. if obj.Shape.isNull(): return '' fill_opacity = 1 # setting fill if obj.Shape.Faces: if App.GuiUp: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None vobj = obj.ViewObject if m != "Wireframe": if fillstyle == "shape color": fill = utils.get_rgb(vobj.ShapeColor, testbw=False) fill_opacity = 1 - vobj.Transparency / 100.0 else: fill = 'url(#' + fillstyle + ')' svg += get_pattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i, f in enumerate(obj.Shape.Faces): # place outer wire first wires = [f.OuterWire] wires.extend([ w for w in f.Wires if w.hashCode() != f.OuterWire.hashCode() ]) svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, wires=f.Wires, pathname='%s_f%04d' % (obj.Name, i)) wiredEdges.extend(f.Edges) else: for i, w in enumerate(obj.Shape.Wires): svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=w.Edges, pathname='%s_w%04d' % (obj.Name, i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i, e in enumerate(obj.Shape.Edges): if DraftGeomUtils.findEdge(e, wiredEdges) is None: svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=[e], pathname='%s_nwe%04d' % (obj.Name, i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve, Part.Circle): svg = get_circle(plane, fill, stroke, linewidth, lstyle, obj.Shape.Edges[0]) else: svg = get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=obj.Shape.Edges) if (App.GuiUp and hasattr(obj.ViewObject, "EndArrow") and obj.ViewObject.EndArrow and hasattr(obj.ViewObject, "ArrowType") and len(obj.Shape.Vertexes) > 1): p1 = get_proj(obj.Shape.Vertexes[-1].Point, plane) p2 = get_proj(obj.Shape.Vertexes[-2].Point, plane) angle = -DraftVecUtils.angle(p2 - p1) arrowsize = obj.ViewObject.ArrowSize.Value / pointratio svg += get_arrow(obj, obj.ViewObject.ArrowType, p1, arrowsize, stroke, linewidth, angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">\n ' + svg + '</g>\n' return svg
def getSVG(section,allOn=False,renderMode="Wireframe",showHidden=False,showFill=False,scale=1,linewidth=1,fontsize=1): """getSVG(section,[allOn,renderMode,showHidden,showFill,scale,linewidth,fontsize]) : returns an SVG fragment from an Arch section plane. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern""" if not section.Objects: return import DraftGeomUtils p = FreeCAD.Placement(section.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) objs = Draft.getGroupContents(section.Objects,walls=True,addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces spaces = [] nonspaces = [] for o in objs: if Draft.getType(o) == "Space": spaces.append(o) else: nonspaces.append(o) objs = nonspaces svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if renderMode == "Solid": # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(section.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if showHidden: render.cut(section.Shape,showHidden) else: render.cut(section.Shape) svg += '<g transform="scale(1,-1)">\n' svg += render.getViewSVG(linewidth="LWPlaceholder") svg += fillpattern svg += render.getSectionSVG(linewidth="SWPlaceholder",fillpattern="sectionfill") if showHidden: svg += render.getHiddenSVG(linewidth="LWPlaceholder") svg += '</g>\n' # print render.info() else: # render using the Drawing module import Drawing, Part shapes = [] hshapes = [] sshapes = [] for o in objs: if o.isDerivedFrom("Part::Feature"): if o.Shape.isNull(): pass elif o.Shape.isValid(): if section.OnlySolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape) else: print section.Label,": Skipping invalid object:",o.Label cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(section.Shape.copy(),shapes) if cutvolume: nsh = [] for sh in shapes: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires(s.Edges) for w in wires: f = Part.Face(w) sshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" sshapes.append(s) nsh.extend(c.Solids) #sshapes.append(s) if showHidden: c = sol.cut(invcutvolume) hshapes.append(c) shapes = nsh if shapes: baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG(baseshape,direction) if svgf: svgf = svgf.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width="1"','stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width:0.01','stroke-width:LWPlaceholder') svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) svgh = Drawing.projectToSVG(hshapes,direction) if svgh: svgh = svgh.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width="1"','stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width:0.01','stroke-width:LWPlaceholder') svgh = svgh.replace('fill="none"','fill="none"\nstroke-dasharray="DAPlaceholder"') svg += svgh if sshapes: svgs = "" if showFill: svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: f = Draft.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) svgs += Drawing.projectToSVG(sshapes,direction) if svgs: svgs = svgs.replace('stroke-width="0.35"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width="1"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.01','stroke-width:SWPlaceholder') svgs = svgs.replace('stroke-width="0.35 px"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.35','stroke-width:SWPlaceholder') svg += svgs linewidth = linewidth/scale st = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("CutLineThickness",2) da = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetString("archHiddenPattern","30,10") da = da.replace(" ","") svg = svg.replace('LWPlaceholder', str(linewidth) + 'px') svg = svg.replace('SWPlaceholder', str(linewidth*st) + 'px') svg = svg.replace('DAPlaceholder', str(da)) if spaces and round(direction.getAngle(FreeCAD.Vector(0,0,1)),Draft.precision()) in [0,round(math.pi,Draft.precision())]: svg += '<g transform="scale(1,-1)">' for s in spaces: svg += Draft.getSVG(s,scale=scale,fontsize=fontsize,direction=direction) svg += '</g>' # print "complete node:",svg return svg
def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale=1, rotation=0, linewidth=1, lineColor=(0.0, 0.0, 0.0), fontsize=1, showFill=False, fillColor=(0.8, 0.8, 0.8), techdraw=False): """getSVG(section, [renderMode, allOn, showHidden, scale, rotation, linewidth, lineColor, fontsize, showFill, fillColor, techdraw]): returns an SVG fragment from an Arch section plane. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern. lineColor -- Color of lines for the renderMode "Wireframe". fillColor -- If showFill is True and renderMode is "Wireframe", the cut areas are filled with fillColor. """ if not section.Objects: return "" import Part, DraftGeomUtils p = FreeCAD.Placement(section.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) objs = Draft.getGroupContents(section.Objects, walls=True, addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces and Draft objects spaces = [] nonspaces = [] drafts = [] windows = [] cutface = None for o in objs: if Draft.getType(o) == "Space": spaces.append(o) elif Draft.getType(o) in ["Dimension", "Annotation"]: drafts.append(o) elif o.isDerivedFrom("Part::Part2DObject"): drafts.append(o) else: nonspaces.append(o) if Draft.getType(o) == "Window": windows.append(o) objs = nonspaces archUserParameters = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Arch") scaledLineWidth = linewidth / scale svgLineWidth = str(scaledLineWidth) + 'px' st = archUserParameters.GetFloat("CutLineThickness", 2) svgCutLineWidth = str(scaledLineWidth * st) + 'px' yt = archUserParameters.GetFloat("SymbolLineThickness", 0.6) svgSymbolLineWidth = str(linewidth * yt) hiddenPattern = archUserParameters.GetString("archHiddenPattern", "30,10") svgHiddenPattern = hiddenPattern.replace(" ", "") fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' svgLineColor = Draft.getrgb(lineColor) svg = '' # reading cached version svgcache = None if hasattr(section.Proxy, "svgcache") and section.Proxy.svgcache: svgcache = section.Proxy.svgcache[0] if section.Proxy.svgcache[1] != renderMode: svgcache = None if section.Proxy.svgcache[2] != showHidden: svgcache = None if section.Proxy.svgcache[3] != showFill: svgcache = None # generating SVG if renderMode in ["Solid", 1]: if not svgcache: svgcache = '' # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(section.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if showHidden: render.cut(section.Shape, showHidden) else: render.cut(section.Shape) svgcache += '<g transform="scale(1,-1)">\n' svgcache += render.getViewSVG(linewidth="SVGLINEWIDTH") svgcache += fillpattern svgcache += render.getSectionSVG(linewidth="SVGCUTLINEWIDTH", fillpattern="sectionfill") if showHidden: svgcache += render.getHiddenSVG(linewidth="SVGLINEWIDTH") svgcache += '</g>\n' # print(render.info()) section.Proxy.svgcache = [ svgcache, renderMode, showHidden, showFill ] else: if not svgcache: svgcache = "" # render using the Drawing module import Drawing, Part shapes, hshapes, sshapes, cutface, cutvolume, invcutvolume = getCutShapes( objs, section, showHidden) if shapes: baseshape = Part.makeCompound(shapes) style = { 'stroke': "SVGLINECOLOR", 'stroke-width': "SVGLINEWIDTH" } svgcache += Drawing.projectToSVG(baseshape, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) if hshapes: hshapes = Part.makeCompound(hshapes) style = { 'stroke': "SVGLINECOLOR", 'stroke-width': "SVGLINEWIDTH", 'stroke-dasharray': "SVGHIDDENPATTERN" } svgcache += Drawing.projectToSVG(hshapes, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) if sshapes: if showFill: #svgcache += fillpattern svgcache += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: #svg += Draft.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0)) # temporarily disabling fill patterns svgcache += Draft.getSVG( s, direction=direction.negative(), linewidth=0, fillstyle=Draft.getrgb(fillColor), color=lineColor) svgcache += "</g>\n" sshapes = Part.makeCompound(sshapes) style = { 'stroke': "SVGLINECOLOR", 'stroke-width': "SVGCUTLINEWIDTH" } svgcache += Drawing.projectToSVG(sshapes, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) section.Proxy.svgcache = [ svgcache, renderMode, showHidden, showFill ] svgcache = svgcache.replace("SVGLINECOLOR", svgLineColor) svgcache = svgcache.replace("SVGLINEWIDTH", svgLineWidth) svgcache = svgcache.replace("SVGHIDDENPATTERN", svgHiddenPattern) svgcache = svgcache.replace("SVGCUTLINEWIDTH", svgCutLineWidth) svg += svgcache if drafts: if not techdraw: svg += '<g transform="scale(1,-1)">' for d in drafts: svg += Draft.getSVG(d, scale=scale, linewidth=svgSymbolLineWidth, fontsize=fontsize, direction=direction, color=lineColor, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' # filter out spaces not cut by the section plane if cutface and spaces: spaces = [ s for s in spaces if s.Shape.BoundBox.intersect(cutface.BoundBox) ] if spaces: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in spaces: svg += Draft.getSVG(s, scale=scale, linewidth=svgSymbolLineWidth, fontsize=fontsize, direction=direction, color=lineColor, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' # add additional edge symbols from windows cutwindows = [] if cutface and windows: cutwindows = [ w.Name for w in windows if w.Shape.BoundBox.intersect(cutface.BoundBox) ] if windows: sh = [] for w in windows: if not hasattr(w.Proxy, "sshapes"): w.Proxy.execute(w) if hasattr(w.Proxy, "sshapes"): if w.Proxy.sshapes and (w.Name in cutwindows): c = Part.makeCompound(w.Proxy.sshapes) c.Placement = w.Placement sh.append(c) # buggy for now... #if hasattr(w.Proxy,"vshapes"): # if w.Proxy.vshapes: # c = Part.makeCompound(w.Proxy.vshapes) # c.Placement = w.Placement # sh.append(c) if sh: if not techdraw: svg += '<g transform="scale(1,-1)">' for s in sh: svg += Draft.getSVG(s, scale=scale, linewidth=svgSymbolLineWidth, fontsize=fontsize, fillstyle="none", direction=direction, color=lineColor, techdraw=techdraw, rotation=rotation) if not techdraw: svg += '</g>' return svg
def Activated(self): b = [] selection = FreeCADGui.Selection.getSelectionEx() if selection: if len(selection) == 4: base = FreeCAD.ActiveDocument.getObject( (selection[0].ObjectName)) b = base.WingPanels _rootRib = FreeCAD.ActiveDocument.getObject( (selection[1].ObjectName)) _path = FreeCAD.ActiveDocument.getObject( (selection[2].ObjectName)) base.WingEdges.append(_path) _envelope = FreeCAD.ActiveDocument.getObject( (selection[3].ObjectName)) base.WingEdges.append(_envelope) _leadingedge = FreeCAD.ActiveDocument.getObject( (selection[3].ObjectName)) # A netoyer en doublon wingsketch = FreeCAD.ActiveDocument.getObject( (selection[3].ObjectName)) # Separate leadInEdge & trailingEdge edges = Part.sortEdges( wingsketch.Shape.Edges ) # Separate, #edges=Part.sortEdges(wingsketch.Shape.Edges) # deprecated ? paths = Part.__sortEdges__(_path.Shape.Edges) leadInEdges = edges[0] #id the leading edge trailingEdges = edges[1] #id the trailing edge FreeCAD.Console.PrintMessage("Edges number : " + str(len(edges)) + "\n") if len(edges) != 2: FreeCAD.Console.PrintMessage("Edges must be 2 and not " + str(len(edges)) + "\n") return nbOfPanels = len(leadInEdges) FreeCAD.Console.PrintMessage( "-------------------- Wing Panel --------------------" + "\n") FreeCAD.Console.PrintMessage(" Rib :" + str(_rootRib.Label) + "\n") FreeCAD.Console.PrintMessage(" Wing :" + str(base.Label) + "\n") FreeCAD.Console.PrintMessage(" Path :" + str(_path.Label) + "\n") FreeCAD.Console.PrintMessage(" envelope :" + str(_envelope.Label) + "\n") FreeCAD.Console.PrintMessage(" envelope placement:" + str(_envelope.Placement) + "\n") FreeCAD.Console.PrintMessage(" Number of Panels :" + str(nbOfPanels) + "\n") pmin = 0 pmax = 0 for i in range(0, nbOfPanels): # for each panel pmin = 0 #pmax pmax = leadInEdges[i].Length param = leadInEdges[i].getParameterByLength(pmin) param2 = leadInEdges[i].getParameterByLength(pmax) direction = leadInEdges[i].tangentAt(param) posvec = leadInEdges[i].valueAt(param) posvec2 = leadInEdges[i].valueAt(param2) #normal = CurvedShapes.getNormal(obj.Base) #rotaxis = normal.cross(direction) #angle = math.degrees(normal.getAngle(direction)) bbox = leadInEdges[i].BoundBox bbox2 = trailingEdges[i].BoundBox FreeCAD.Console.PrintMessage(" Panel n° " + str(i) + " ------------------------\n") FreeCAD.Console.PrintMessage(" leadInEdges id :" + str(leadInEdges[i]) + "\n") FreeCAD.Console.PrintMessage(" leadInEdges Length :" + str(leadInEdges[i].Length) + "\n") FreeCAD.Console.PrintMessage(" trailingEdges id :" + str(trailingEdges[i]) + "\n") FreeCAD.Console.PrintMessage(" trailingEdges Length :" + str(trailingEdges[i].Length) + "\n") FreeCAD.Console.PrintMessage(" direction :" + str(direction) + "\n") FreeCAD.Console.PrintMessage(" pmin : " + str(pmin) + "\n") FreeCAD.Console.PrintMessage(" Param : " + str(param) + "\n") FreeCAD.Console.PrintMessage(" Position : " + str(posvec) + "\n") FreeCAD.Console.PrintMessage(" Position2 : " + str(posvec2) + "\n") FreeCAD.Console.PrintMessage( " BoundBox leadInEdges : " + str(bbox) + "\n") FreeCAD.Console.PrintMessage( " BoundBox trailingEdges : " + str(bbox2) + "\n") FreeCADGui.activateWorkbench("DraftWorkbench") plane = WorkingPlane.plane() FreeCAD.DraftWorkingPlane = plane #workplane = WorkingPlane.plane() workplane = FreeCAD.DraftWorkingPlane v1 = FreeCAD.Vector( 0, 1, 0).normalize() #paths[i].tangentAt(param).normalize() v2 = FreeCAD.Vector(0, 0, 1).normalize() #workplane.alignToPointAndAxis(v1, v2, 0) #FreeCAD.DraftWorkingPlane.alignToPointAndAxis(v1, v2, 0) #FreeCADGui.Snapper.toggleGrid() FreeCAD.Console.PrintMessage(" V1 : " + str(v1) + "\n") FreeCAD.Console.PrintMessage(" V2 : " + str(v2) + "\n") # workplane.alignToPointAndAxis( v1, v2, 0) #FreeCADGui.Snapper.toggleGrid() FreeCADGui.activeDocument().activeView( ).setCameraOrientation(_path.Placement.Rotation) #paths[i]. FreeCAD.Console.PrintMessage(" pathline : " + str(paths[i]) + "\n") #pathline=Part.Line(paths[i].Geometry) myObj0 = Draft.makeSketch(paths[i], autoconstraints=True) #myObj1=Part.Line(paths[i].Content) myObj0.Label = "path" + str(i) vec = _envelope.Placement.Rotation FreeCADGui.activeDocument().activeView( ).setCameraOrientation(vec) #(0,0,0,1)) myObj1 = Draft.makeSketch(leadInEdges[i], name="leadInEdges" + str(i), autoconstraints=True) #myObj1=Part.Line() myObj1.Label = "leadInEdges" + str(i) FreeCADGui.activeDocument().activeView( ).setCameraOrientation(vec) myObj2 = Draft.makeSketch(trailingEdges[i], autoconstraints=True) myObj2.Label = "trailingEdge" + str(i) obj = FreeCAD.ActiveDocument.addObject( "Part::FeaturePython", "WingPanel" + str(i)) WingPanel(obj, _rootRib, myObj0, _envelope, myObj1, myObj2, 200, 100, 100, 0, 0) #WingPanel(obj,_rootRib,_path,_envelope,myObj1,myObj2,200,100,100,0,0) #WingPanel(obj,_rootRib,_path,_envelope,leadInEdges[i],trailingEdges[i],200,100,100,0,0) #WingPanel(obj,_rootRib,_path,leadInEdges[i],trailingEdges[i],200,100,100,0,0) ViewProviderPanel(obj.ViewObject) b.append(obj) FreeCAD.ActiveDocument.recompute() else: #---------------------création des nervures temporaires #_rootRib=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","RibRoot_") #WingRib(_rootRib,"/Users/fredericnivoix/Library/Preferences/FreeCAD/Mod/AirPlaneDesign/wingribprofil/naca/naca2412.dat",False,0,200,0,0,0,0,0,0) #ViewProviderWingRib(_rootRib.ViewObject) #_tipRib=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","RibTip_") #WingRib(_tipRib,"/Users/fredericnivoix/Library/Preferences/FreeCAD/Mod/AirPlaneDesign/wingribprofil/naca/naca2412.dat",False,0,200,0,500,0,0,0,0) #ViewProviderWingRib(_tipRib.ViewObject) #---------- obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "WingPanel") WingPanel(obj, None, None, None, None, None, 200, 100, 100, 0, 0) ViewProviderPanel(obj.ViewObject) if selection: #selection==None : try: #b=base.WingPanels #b.append(obj) base.WingPanels = b except: print("The selection is not a wing") FreeCAD.Gui.activeDocument().activeView().viewAxonometric() FreeCAD.Gui.SendMsgToActiveView("ViewFit")
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * #*************************************************************************** import FreeCADGui , WorkingPlane, math, re from pivy import coin from FreeCAD import Vector from drawGui import * #from draftlibs import fcvec, fcgeo import fcvec, fcgeo from functools import partial import Part # sets the default working plane plane = WorkingPlane.plane() FreeCAD.DDADockWidget = plane plane.alignToPointAndAxis(Vector(0, 0, 0), Vector(0, 0, 1), 0) #--------------------------------------------------------------------------- # Trackers #--------------------------------------------------------------------------- class Tracker: "A generic Draft Tracker, to be used by other specific trackers" # 辅助绘图工具 def __init__(self, dotted=False, scolor=None, swidth=None, children=[], ontop=False): self.ontop = ontop color = coin.SoBaseColor() color.rgb = scolor or FreeCADGui.DDADockWidget.getDefaultColor("ui")
def onChanged(self, obj, prop): if prop in ["Source","RenderingMode","ShowCut"]: import Part, DraftGeomUtils if hasattr(obj,"Source"): if obj.Source: if obj.Source.Objects: objs = Draft.getGroupContents(obj.Source.Objects,walls=True) objs = Draft.removeHidden(objs) # separate spaces self.spaces = [] os = [] for o in objs: if Draft.getType(o) == "Space": self.spaces.append(o) else: os.append(o) objs = os self.svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if obj.RenderingMode == "Solid": # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(obj.Source.Placement) wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if hasattr(obj,"ShowCut"): render.cut(obj.Source.Shape,obj.ShowCut) else: render.cut(obj.Source.Shape) self.svg += render.getViewSVG(linewidth="LWPlaceholder") self.svg += fillpattern self.svg += render.getSectionSVG(linewidth="SWPlaceholder",fillpattern="sectionfill") if hasattr(obj,"ShowCut"): if obj.ShowCut: self.svg += render.getHiddenSVG(linewidth="LWPlaceholder") # print render.info() else: # render using the Drawing module import Drawing, Part shapes = [] hshapes = [] sshapes = [] p = FreeCAD.Placement(obj.Source.Placement) self.direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) for o in objs: if o.isDerivedFrom("Part::Feature"): if o.Shape.isNull(): pass #FreeCAD.Console.PrintWarning(translate("Arch","Skipping empty object: ")+o.Name) elif o.Shape.isValid(): if hasattr(obj.Source,"OnlySolids"): if obj.Source.OnlySolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape) else: shapes.extend(o.Shape.Solids) else: FreeCAD.Console.PrintWarning(translate("Arch","Skipping invalid object: ")+o.Name) cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(obj.Source.Shape.copy(),shapes) if cutvolume: nsh = [] for sh in shapes: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires(s.Edges) for w in wires: f = Part.Face(w) sshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" sshapes.append(s) nsh.extend(c.Solids) #sshapes.append(s) if hasattr(obj,"ShowCut"): if obj.ShowCut: c = sol.cut(invcutvolume) hshapes.append(c) shapes = nsh if shapes: self.shapes = shapes self.baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG(self.baseshape,self.direction) if svgf: svgf = svgf.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width="1"','stroke-width="LWPlaceholder"') svgf = svgf.replace('stroke-width:0.01','stroke-width:LWPlaceholder') self.svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) self.hiddenshape = hshapes svgh = Drawing.projectToSVG(hshapes,self.direction) if svgh: svgh = svgh.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width="1"','stroke-width="LWPlaceholder"') svgh = svgh.replace('stroke-width:0.01','stroke-width:LWPlaceholder') svgh = svgh.replace('fill="none"','fill="none"\nstroke-dasharray="DAPlaceholder"') self.svg += svgh if sshapes: svgs = "" if hasattr(obj,"ShowFill"): if obj.ShowFill: svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: f = Draft.getSVG(s,direction=self.direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) self.sectionshape = sshapes svgs += Drawing.projectToSVG(sshapes,self.direction) if svgs: svgs = svgs.replace('stroke-width="0.35"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width="1"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.01','stroke-width:SWPlaceholder') svgs = svgs.replace('stroke-width="0.35 px"','stroke-width="SWPlaceholder"') svgs = svgs.replace('stroke-width:0.35','stroke-width:SWPlaceholder') self.svg += svgs
import draftguitools.gui_trackers as trackers # The module is used to prevent complaints from code checkers (flake8) True if Draft_rc.__name__ else False True if DraftGui.__name__ else False __title__ = "FreeCAD Draft Workbench GUI Tools" __author__ = ("Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline, " "Dmitry Chigrin") __url__ = "https://www.freecadweb.org" if not hasattr(FreeCADGui, "Snapper"): FreeCADGui.Snapper = gui_snapper.Snapper() if not hasattr(FreeCAD, "DraftWorkingPlane"): FreeCAD.DraftWorkingPlane = WorkingPlane.plane() # --------------------------------------------------------------------------- # Commands that have been migrated to their own modules # --------------------------------------------------------------------------- import draftguitools.gui_edit import draftguitools.gui_selectplane import draftguitools.gui_planeproxy from draftguitools.gui_lineops import FinishLine from draftguitools.gui_lineops import CloseLine from draftguitools.gui_lineops import UndoLine from draftguitools.gui_togglemodes import ToggleConstructionMode from draftguitools.gui_togglemodes import ToggleContinueMode from draftguitools.gui_togglemodes import ToggleDisplayMode from draftguitools.gui_groups import AddToGroup from draftguitools.gui_groups import SelectGroup