diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 5c9a75713..5802e5e0a 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -205,51 +205,6 @@ def makeFace(wires,method=2,cleanup=False): print "final face:",mf.Faces return mf.Faces[0] -def getCutVolume(objects,placement): - '''getCutVolume(objects,placement): returns a tuple with 2 objects: a face, positioned - at the given placement's position, and wide enough so the projection of all objects - in the list fits into it, and an extrusion vector, that can be used to extrude the - plane so it includes all objects in the list.''' - import Part - placement = FreeCAD.Placement(placement) - if not objects: - return None - bb = objects[0].Shape.BoundBox - for obj in objects[1:]: - bb.add(obj.Shape.BoundBox) - bb.enlarge(1) - u = placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) - v = placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) - w = placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) - um = vm = wm = 0 - if not bb.isCutPlane(placement.Base,w): - return None - corners = [FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin), - FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMin), - FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMin), - FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMin), - FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMax), - FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMax), - FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMax), - FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMax)] - for c in corners: - dv = c.sub(placement.Base) - um1 = fcvec.project(dv,u).Length - um = max(um,um1) - vm1 = fcvec.project(dv,v).Length - vm = max(vm,vm1) - wm1 = fcvec.project(dv,w).Length - wm = max(wm,wm1) - p1 = FreeCAD.Vector(-um,vm,0) - p2 = FreeCAD.Vector(um,vm,0) - p3 = FreeCAD.Vector(um,-vm,0) - p4 = FreeCAD.Vector(-um,-vm,0) - f = Part.makePolygon([p1,p2,p3,p4,p1]) - f = Part.Face(f) - f.Placement = placement - n = fcvec.scaleTo(w,wm) - return (f,n) - def meshToShape(obj,mark=True): '''meshToShape(object,[mark]): turns a mesh into a shape, joining coplanar facets. If mark is True (default), non-solid objects will be marked in red''' diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index 7113f4fab..d4ae54c21 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -181,77 +181,34 @@ class _ArchDrawingView: if obj.Source.Objects: svg = '' - # getting section plane - cp = ArchCommands.getCutVolume(obj.Source.Objects,obj.Source.Placement) - self.sections = [] - if cp: - cutvolume = cp[0].extrude(cp[1]) - shapes = [] - colors = [] - - # sorting - if join: - walls = [] - structs = [] - objs = [] - for o in obj.Source.Objects: - t = Draft.getType(o) - if t == "Wall": - walls.append(o) - elif t == "Structure": - structs.append(o) - else: - objs.append(o) - for g in [walls,structs]: - if g: - print "group:",g - col = g[0].ViewObject.DiffuseColor[0] - s = g[0].Shape - for o in g[1:]: - try: - fs = s.fuse(o.Shape) - fs = fs.removeSplitter() - except: - print "shape fusion failed" - objs.append([o.Shape,o.ViewObject.DiffuseColor[0]]) - else: - s = fs - objs.append([s,col]) - else: - objs = obj.Source.Objects - - # shapes extraction - for o in objs: - print "object:",o - if isinstance(o,list): - shape = o[0] - color = o[1] - else: - shape = o.Shape - color = o.ViewObject.DiffuseColor[0] - if cp: - for s in shape.Solids: - shapes.append(s.cut(cutvolume)) - colors.append(color) - sec = shape.section(cp[0]) - if sec.Edges: - wires = fcgeo.findWires(sec.Edges) - for w in wires: - sec = Part.Wire(fcgeo.sortEdges(w.Edges)) - sec = Part.Face(sec) - self.sections.append(sec) - else: - shapes.append(shape) - colors.append(color) - # generating SVG linewidth = obj.LineWidth/obj.Scale if obj.RenderingMode == "Solid": - svg += self.renderVRM(shapes,obj.Source.Placement,colors,linewidth) + # render using the Arch Vector Renderer + import ArchVRM + render = ArchVRM.Renderer() + render.setWorkingPlane(obj.Source.Placement) + render.addObjects(obj.Source.Objects) + render.cut(obj.Source.Shape) + svg += render.getViewSVG(linewidth=linewidth) + svg += render.getSectionSVG(linewidth=linewidth*2) + # print render.info() + else: - svg += self.renderOCC(shapes,obj.Source.Proxy.getNormal(obj.Source),linewidth) - for s in self.sections: - svg += self.renderSection(s,obj.Source.Placement,linewidth*2) + # render using the Drawing module + shapes = [] + for o in obj.Source.Objects: + if o.isDerivedFrom("Part::Feature"): + shapes.append(o.Shape) + if shapes: + base = shape.pop() + for sh in shapes: + base = base.fuse(sh) + svgf = Drawing.projectToSVG(base,fcvec.neg(direction)) + if svgf: + svgf = svgf.replace('stroke-width="0.35"','stroke-width="' + str(linewidth) + 'px"') + svg += svgf + result = '' result += ' 1: - x = -vu.Length - else: - x = vu.Length - vv = fcvec.project(vt.Point,v) - if vv.getAngle(v) > 1: - y = -vv.Length - else: - y = vv.Length - pts.append([x,y]) - svg =' 1: sh.reverse() - return sh + return [sh]+face[1:] def flattenFace(self,face): "Returns a face where all vertices have Z = 0" wires = [] - for w in face.Wires: + for w in face[0].Wires: verts = [] edges = fcgeo.sortEdges(w.Edges) for e in edges: @@ -209,29 +207,113 @@ class Renderer: if DEBUG: print "Error: Unable to flatten face" return None else: - return sh + return [sh]+face[1:] + + def cut(self,cutplane): + "Cuts through the shapes with a given cut plane and builds section faces" + if self.iscut: + return + if not self.shapes: + if DEBUG: print "No objects to make sections" + else: + fill = (1.0,1.0,1.0,1.0) + placement = FreeCAD.Placement(cutplane.Placement) + + # building boundbox + bb = self.shapes[0][0].BoundBox + for sh in self.shapes[1:]: + bb.add(sh[0].BoundBox) + bb.enlarge(1) + um = vm = wm = 0 + if not bb.isCutPlane(placement.Base,self.wp.axis): + if DEBUG: print "No objects are cut by the plane" + else: + corners = [FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin), + FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMin), + FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMin), + FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMin), + FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMax), + FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMax), + FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMax), + FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMax)] + for c in corners: + dv = c.sub(placement.Base) + um1 = fcvec.project(dv,self.wp.u).Length + um = max(um,um1) + vm1 = fcvec.project(dv,self.wp.v).Length + vm = max(vm,vm1) + wm1 = fcvec.project(dv,self.wp.axis).Length + wm = max(wm,wm1) + p1 = FreeCAD.Vector(-um,vm,0) + p2 = FreeCAD.Vector(um,vm,0) + p3 = FreeCAD.Vector(um,-vm,0) + p4 = FreeCAD.Vector(-um,-vm,0) + cutface = Part.makePolygon([p1,p2,p3,p4,p1]) + cutface = Part.Face(cutface) + cutface.Placement = placement + cutnormal = fcvec.scaleTo(self.wp.axis,wm) + cutvolume = cutface.extrude(cutnormal) + shapes = [] + faces = [] + sections = [] + for sh in self.shapes: + for sol in sh[0].Solids: + c = sol.cut(cutvolume) + shapes.append([c]+sh[1:]) + for f in c.Faces: + faces.append([f]+sh[1:]) + sec = sol.section(cutface) + if sec.Edges: + wires = fcgeo.findWires(sec.Edges) + for w in wires: + sec = Part.Face(w) + sections.append([sec,fill]) + self.shapes = shapes + self.faces = faces + self.sections = sections + if DEBUG: print "Built ",len(self.sections)," sections, ", len(self.faces), " faces retained" + self.iscut = True + self.oriented = False + self.trimmed = False + self.sorted = False + self.joined = False def isInside(self,vert,face): "Returns True if the vert is inside the face in Z projection" - + + # http://paulbourke.net/geometry/insidepoly/ + count = 0 + p = self.wp.getLocalCoords(vert.Point) + for e in face[0].Edges: + p1 = e.Vertexes[0].Point + p2 = e.Vertexes[-1].Point + if p.y > min(p1.y,p2.y): + if p.y <= max(p1.y,p2.y): + if p.x <= max(p1.x,p2.x): + if p1.y != p2.y: + xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x + if (p1.x == p2.x) or (p.x <= xinters): + count += 1 + if count % 2 == 0: + return False + else: + return True def zOverlaps(self,face1,face2): "Checks if face1 overlaps face2 in Z direction" - face1 = self.flattenFace(face1) face2 = self.flattenFace(face2) - + # first we check if one of the verts is inside the other face - for v in face1.Vertexes: + for v in face1[0].Vertexes: if self.isInside(v,face2): return True # even so, faces can still overlap if their edges cross each other - for e1 in face1.Edges: - for e2 in face2.Edges: + for e1 in face1[0].Edges: + for e2 in face2[0].Edges: if fcgeo.findIntersection(e1,e2): return True - return False def compare(self,face1,face2): @@ -241,8 +323,8 @@ class Renderer: # http://www.siggraph.org/education/materials/HyperGraph/scanline/visibility/painter.htm # and practical application http://vrm.ao2.it/ (blender vector renderer) - b1 = face1.BoundBox - b2 = face2.BoundBox + b1 = face1[0].BoundBox + b2 = face2[0].BoundBox # test 1: if faces don't overlap, no comparison possible if DEBUG: print "doing test 1" @@ -266,11 +348,11 @@ class Renderer: # test 3: all verts of face1 are in front or behind the plane of face2 if DEBUG: print "doing test 3" - norm = face2.normalAt(0,0) + norm = face2[0].normalAt(0,0) behind = 0 front = 0 - for v in face1.Vertexes: - dv = v.Point.sub(face2.Vertexes[0].Point) + for v in face1[0].Vertexes: + dv = v.Point.sub(face2[0].Vertexes[0].Point) dv = fcvec.project(dv,norm) if fcvec.isNull(dv): behind += 1 @@ -281,19 +363,19 @@ class Renderer: else: front += 1 if DEBUG: print "front: ",front," behind: ",behind - if behind == len(face1.Vertexes): + if behind == len(face1[0].Vertexes): return 2 - elif front == len(face1.Vertexes): + elif front == len(face1[0].Vertexes): return 1 if DEBUG: print "failed, cannot say if face 1 is in front or behind" # test 4: all verts of face2 are in front or behind the plane of face1 if DEBUG: print "doing test 4" - norm = face1.normalAt(0,0) + norm = face1[0].normalAt(0,0) behind = 0 front = 0 - for v in face2.Vertexes: - dv = v.Point.sub(face1.Vertexes[0].Point) + for v in face2[0].Vertexes: + dv = v.Point.sub(face1[0].Vertexes[0].Point) dv = fcvec.project(dv,norm) if fcvec.isNull(dv): behind += 1 @@ -304,9 +386,9 @@ class Renderer: else: front += 1 if DEBUG: print "front: ",front," behind: ",behind - if behind == len(face2.Vertexes): + if behind == len(face2[0].Vertexes): return 1 - elif front == len(face2.Vertexes): + elif front == len(face2[0].Vertexes): return 2 if DEBUG: print "failed, cannot say if face 2 is in front or behind" @@ -321,6 +403,35 @@ class Renderer: if DEBUG: print "Houston, all tests passed, and still no results" return 0 + def join(self,otype): + "joins the objects of same type" + walls = [] + structs = [] + objs = [] + for o in obj.Source.Objects: + t = Draft.getType(o) + if t == "Wall": + walls.append(o) + elif t == "Structure": + structs.append(o) + else: + objs.append(o) + for g in [walls,structs]: + if g: + print "group:",g + col = g[0].ViewObject.DiffuseColor[0] + s = g[0].Shape + for o in g[1:]: + try: + fs = s.fuse(o.Shape) + fs = fs.removeSplitter() + except: + print "shape fusion failed" + objs.append([o.Shape,o.ViewObject.DiffuseColor[0]]) + else: + s = fs + objs.append([s,col]) + def findPosition(self,f1,faces): "Finds the position of a face in a list of faces" l = None @@ -345,24 +456,22 @@ class Renderer: def sort(self): "projects a shape on the WP" - if not self.faces: - return - if len(self.faces) == 1: + if len(self.faces) <= 1: return if not self.trimmed: self.removeHidden() + if len(self.faces) == 1: + return if not self.oriented: self.reorient() faces = self.faces[:] if DEBUG: print "sorting ",len(self.faces)," faces" sfaces = [] - sfills = [] loopcount = 0 notfoundstack = 0 while faces: if DEBUG: print "loop ", loopcount f1 = faces[0] - fi1 = self.fills[self.faces.index(f1)] if sfaces and (notfoundstack < len(faces)): if DEBUG: print "using ordered stack, notfound = ",notfoundstack p = self.findPosition(f1,sfaces) @@ -375,32 +484,26 @@ class Renderer: # position found, we insert it faces.remove(f1) sfaces.insert(p,f1) - sfills.insert(p,fi1) notfoundstack = 0 else: # either there is no stack, or no more face can be compared # find a root, 2 faces that can be compared if DEBUG: print "using unordered stack, notfound = ",notfoundstack for f2 in faces[1:]: - fi2 = self.fills[self.faces.index(f2)] if DEBUG: print "comparing face",str(self.faces.index(f1))," with face",str(self.faces.index(f2)) r = self.compare(f1,f2) if r == 1: faces.remove(f2) sfaces.append(f2) - sfills.append(fi2) faces.remove(f1) sfaces.append(f1) - sfills.append(fi1) notfoundstack = 0 break elif r == 2: faces.remove(f1) sfaces.append(f1) - sfills.append(fi1) faces.remove(f2) sfaces.append(f2) - sfills.append(fi2) notfoundstack = 0 break else: @@ -414,7 +517,6 @@ class Renderer: if DEBUG: print "done Z sorting. ", len(sfaces), " faces retained, ", len(self.faces)-len(sfaces), " faces lost." self.faces = sfaces - self.fills = sfills self.sorted = True def buildDummy(self): @@ -424,7 +526,7 @@ class Renderer: self.sort() faces = [] for f in self.faces[:]: - ff = self.flattenFace(f) + ff = self.flattenFace(f)[0] ff.translate(FreeCAD.Vector(0,0,z)) faces.append(ff) z += 1 @@ -440,30 +542,35 @@ class Renderer: col = "#"+r+g+b return col - def getSVG(self,linewidth=0.01): - "Returns a SVG fragment" - if DEBUG: print len(self.faces), " faces and ", len(self.fills), " fills." + def getPathData(self,w): + "Returns a SVG path data string from a 2D wire" + edges = fcgeo.sortEdges(w.Edges) + v = edges[0].Vertexes[0].Point + svg = 'M '+ str(v.x) +' '+ str(v.y) + ' ' + for e in edges: + if isinstance(e.Curve,Part.Line) or isinstance(e.Curve,Part.BSplineCurve): + v = e.Vertexes[-1].Point + svg += 'L '+ str(v.x) +' '+ str(v.y) + ' ' + elif isinstance(e.Curve,Part.Circle): + r = e.Curve.Radius + v = e.Vertexes[-1].Point + svg += 'A '+ str(r) + ' '+ str(r) +' 0 0 1 '+ str(v.x) +' ' + svg += str(v.y) + ' ' + svg += 'z ' + return svg + + def getViewSVG(self,linewidth=0.01): + "Returns a SVG fragment from viewed faces" + if DEBUG: print "Printing ", len(self.faces), " faces" if not self.sorted: self.sort() svg = '' - for i in range(len(self.faces)): - fill = self.getFill(self.fills[i]) + for f in self.faces: + fill = self.getFill(f[1]) svg +='