Big cleanup in Arch vector renderer
This commit is contained in:
parent
724e40627e
commit
857d9c00ad
|
@ -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'''
|
||||
|
|
|
@ -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 += '<g id="' + obj.Name + '"'
|
||||
result += ' transform="'
|
||||
|
@ -261,72 +218,8 @@ class _ArchDrawingView:
|
|||
result += '">\n'
|
||||
result += svg
|
||||
result += '</g>\n'
|
||||
#print "complete node:",result
|
||||
# print "complete node:",result
|
||||
return result
|
||||
return ''
|
||||
|
||||
def renderOCC(self,shapes,direction,linewidth):
|
||||
"renders an SVG fragment with the OCC method"
|
||||
shapes = shapes[:]
|
||||
if shapes:
|
||||
base = shape.pop()
|
||||
for sh in shapes:
|
||||
base = base.fuse(sh)
|
||||
result = Drawing.projectToSVG(base,fcvec.neg(direction))
|
||||
if result:
|
||||
result = result.replace('stroke-width="0.35"','stroke-width="' + str(linewidth) + 'px"')
|
||||
return result
|
||||
return ''
|
||||
|
||||
def renderVRM(self,shapes,placement,colors,linewidth):
|
||||
"renders an SVG fragment with the ArchVRM method"
|
||||
import ArchVRM
|
||||
render = ArchVRM.Renderer()
|
||||
render.setWorkingPlane(FreeCAD.Placement(placement))
|
||||
for i in range(len(shapes)):
|
||||
if colors:
|
||||
render.add(shapes[i],colors[i])
|
||||
else:
|
||||
render.add(shapes[i])
|
||||
svg = render.getSVG(linewidth=linewidth)
|
||||
#print render.info()
|
||||
return svg
|
||||
|
||||
def renderSection(self,shape,placement,linewidth):
|
||||
"renders a plane parallel to the section plane"
|
||||
placement = FreeCAD.Placement(placement)
|
||||
u = placement.Rotation.multVec(FreeCAD.Vector(1,0,0))
|
||||
v = placement.Rotation.multVec(FreeCAD.Vector(0,1,0))
|
||||
pts = []
|
||||
for vt in shape.Vertexes:
|
||||
vu = fcvec.project(vt.Point,u)
|
||||
if vu.getAngle(u) > 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 ='<path '
|
||||
svg += 'd="M '+ str(pts[0][0]) +' '+ str(pts[0][1]) + ' '
|
||||
for p in pts[1:]:
|
||||
svg += 'L '+ str(p[0]) +' '+ str(p[1]) + ' '
|
||||
svg += 'z '
|
||||
svg += '" '
|
||||
svg += 'stroke="#000000" '
|
||||
svg += 'stroke-width="' + str(linewidth) + 'px" '
|
||||
svg += 'style="stroke-width:' + str(linewidth) + ';'
|
||||
svg += 'stroke-miterlimit:1;'
|
||||
svg += 'stroke-linejoin:round;'
|
||||
svg += 'stroke-dasharray:none;'
|
||||
svg += 'fill:#ffffff;'
|
||||
svg += 'fill-rule: evenodd'
|
||||
svg += '"/>\n'
|
||||
return svg
|
||||
|
||||
|
||||
|
||||
FreeCADGui.addCommand('Arch_SectionPlane',_CommandSectionPlane())
|
||||
|
|
|
@ -29,6 +29,9 @@ from draftlibs import fcvec,fcgeo
|
|||
DEBUG = True # if we want debug messages
|
||||
MAXLOOP = 10 # the max number of loop before abort
|
||||
|
||||
# WARNING: in this module, faces are lists whose first item is the actual OCC face, the
|
||||
# other items being additional information such as color, etc.
|
||||
|
||||
class Renderer:
|
||||
"A renderer object"
|
||||
def __init__(self,wp=None):
|
||||
|
@ -43,22 +46,35 @@ class Renderer:
|
|||
p.sort()
|
||||
p.buildDummy()
|
||||
"""
|
||||
|
||||
self.defaultFill = (0.9,0.9,0.9,1.0) # the default fill color
|
||||
self.wp = wp
|
||||
self.faces = []
|
||||
self.fills = []
|
||||
self.oriented = False
|
||||
self.trimmed = False
|
||||
self.sorted = False
|
||||
if not self.wp:
|
||||
|
||||
self.reset()
|
||||
if wp:
|
||||
self.wp = wp
|
||||
else:
|
||||
import WorkingPlane
|
||||
self.wp = WorkingPlane.plane()
|
||||
|
||||
if DEBUG: print "Renderer initialized on " + str(self.wp)
|
||||
|
||||
def __str__(self):
|
||||
return "Arch Renderer: " + str(len(self.faces)) + " faces projected on " + str(self.wp)
|
||||
|
||||
def reset(self):
|
||||
"removes all faces from this renderer"
|
||||
self.objects = []
|
||||
self.shapes = []
|
||||
self.faces = []
|
||||
self.resetFlags()
|
||||
|
||||
def resetFlags(self):
|
||||
"resets all flags of this renderer"
|
||||
self.oriented = False
|
||||
self.trimmed = False
|
||||
self.sorted = False
|
||||
self.iscut = False
|
||||
self.joined = False
|
||||
self.sections = []
|
||||
|
||||
def setWorkingPlane(self,wp):
|
||||
"sets a Draft WorkingPlane or Placement for this renderer"
|
||||
if isinstance(wp,FreeCAD.Placement):
|
||||
|
@ -67,52 +83,35 @@ class Renderer:
|
|||
self.wp = wp
|
||||
if DEBUG: print "Renderer set on " + str(self.wp)
|
||||
|
||||
def add(self,faces,colors=None):
|
||||
"add faces, shape or object to this renderer, optionally with face colors"
|
||||
def addFaces(self,faces,color=(0.9,0.9,0.9,1.0)):
|
||||
"add individual faces to this renderer, optionally with a color"
|
||||
if DEBUG: print "adding ", len(faces), " faces. Warning, these will get lost if using cut() or join()"
|
||||
for f in faces:
|
||||
self.faces.append([f,color])
|
||||
self.resetFlags()
|
||||
|
||||
def setcolors(colors,n):
|
||||
if colors:
|
||||
if isinstance(colors,tuple) and len(colors) == 4:
|
||||
for i in range(n):
|
||||
self.fills.append(colors)
|
||||
elif len(colors) == n:
|
||||
self.fills.extend(colors)
|
||||
else:
|
||||
c = []
|
||||
for i in range(n):
|
||||
c.append(colors[0])
|
||||
self.fills.extend(c)
|
||||
else:
|
||||
c = []
|
||||
for i in range(n):
|
||||
c.append(self.defaultFill)
|
||||
self.fills.extend(c)
|
||||
|
||||
if isinstance(faces,list):
|
||||
f = faces
|
||||
setcolors(colors,len(f))
|
||||
elif hasattr(faces,"Faces"):
|
||||
f = faces.Faces
|
||||
setcolors(colors,len(f))
|
||||
elif hasattr(faces,"Shape"):
|
||||
f = faces.Shape.Faces
|
||||
if hasattr(faces,"ViewObject") and not colors:
|
||||
colors = faces.ViewObject.DiffuseColor
|
||||
setcolors(colors,len(f))
|
||||
|
||||
if DEBUG: print "adding ", len(f), " faces"
|
||||
self.faces.extend(f)
|
||||
self.oriented = False
|
||||
self.trimmed = False
|
||||
self.sorted = False
|
||||
|
||||
def clean(self):
|
||||
"removes all faces from this renderer"
|
||||
self.faces = []
|
||||
self.fills = []
|
||||
self.oriented = False
|
||||
self.trimmed = False
|
||||
self.sorted = False
|
||||
def addObjects(self,objs):
|
||||
"add objects to this renderer"
|
||||
for o in objs:
|
||||
if o.isDerivedFrom("Part::Feature"):
|
||||
self.objects.append(o)
|
||||
color = o.ViewObject.ShapeColor
|
||||
if o.Shape.Faces:
|
||||
self.shapes.append([o.Shape,color])
|
||||
for f in o.Shape.Faces:
|
||||
self.faces.append([f,color])
|
||||
self.resetFlags()
|
||||
if DEBUG: print "adding ", len(self.objects), " objects, ", len(self.faces), " faces"
|
||||
|
||||
def addShapes(self,shapes,color=(0.9,0.9,0.9,1.0)):
|
||||
"add shapes to this renderer, optionally with a color. Warning, these will get lost if using join()"
|
||||
if DEBUG: print "adding ", len(shapes), " shapes"
|
||||
for s in shapes:
|
||||
if s.Faces:
|
||||
self.shapes.append([s,color])
|
||||
for f in s.Faces:
|
||||
self.faces.append([f,color])
|
||||
self.resetFlags()
|
||||
|
||||
def info(self):
|
||||
"Prints info about the contents of this renderer"
|
||||
|
@ -122,10 +121,10 @@ class Renderer:
|
|||
r += "sorted: " + str(self.sorted) + "\n"
|
||||
r += "contains " + str(len(self.faces)) + " faces\n"
|
||||
for i in range(len(self.faces)):
|
||||
r += " face " + str(i) + " : center " + str(self.faces[i].CenterOfMass)
|
||||
r += " : normal " + str(self.faces[i].normalAt(0,0))
|
||||
r += ", " + str(len(self.faces[i].Vertexes)) + " verts"
|
||||
r += ", color: " + self.getFill(self.fills[i]) + "\n"
|
||||
r += " face " + str(i) + " : center " + str(self.faces[i][0].CenterOfMass)
|
||||
r += " : normal " + str(self.faces[i][0].normalAt(0,0))
|
||||
r += ", " + str(len(self.faces[i][0].Vertexes)) + " verts"
|
||||
r += ", color: " + self.getFill(self.faces[i][1]) + "\n"
|
||||
return r
|
||||
|
||||
def addLabels(self):
|
||||
|
@ -133,13 +132,13 @@ class Renderer:
|
|||
c = 0
|
||||
for f in self.faces:
|
||||
l = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel","facelabel")
|
||||
l.BasePosition = f.CenterOfMass
|
||||
l.BasePosition = f[0].CenterOfMass
|
||||
l.LabelText = str(c)
|
||||
c += 1
|
||||
|
||||
def isVisible(self,face):
|
||||
"returns True if the given face points in the view direction"
|
||||
normal = face.normalAt(0,0)
|
||||
normal = face[0].normalAt(0,0)
|
||||
if DEBUG: print "checking face normal ", normal, " against ", self.wp.axis, " : ", math.degrees(normal.getAngle(self.wp.axis))
|
||||
if normal.getAngle(self.wp.axis) < math.pi/2:
|
||||
return True
|
||||
|
@ -150,6 +149,8 @@ class Renderer:
|
|||
if not self.faces:
|
||||
return
|
||||
self.faces = [self.projectFace(f) for f in self.faces]
|
||||
if self.sections:
|
||||
self.sections = [self.projectFace(f) for f in self.sections]
|
||||
self.oriented = True
|
||||
|
||||
def removeHidden(self):
|
||||
|
@ -157,21 +158,18 @@ class Renderer:
|
|||
if not self.faces:
|
||||
return
|
||||
faces = []
|
||||
fills = []
|
||||
for i in range(len(self.faces)):
|
||||
if self.isVisible(self.faces[i]):
|
||||
faces.append(self.faces[i])
|
||||
fills.append(self.fills[i])
|
||||
for f in self.faces:
|
||||
if self.isVisible(f):
|
||||
faces.append(f)
|
||||
if DEBUG: print len(self.faces)-len(faces) , " faces removed, ", len(faces), " faces retained"
|
||||
self.faces = faces
|
||||
self.fills = fills
|
||||
self.trimmed = True
|
||||
|
||||
def projectFace(self,face):
|
||||
"projects a single face on the WP"
|
||||
wires = []
|
||||
norm = face.normalAt(0,0)
|
||||
for w in face.Wires:
|
||||
norm = face[0].normalAt(0,0)
|
||||
for w in face[0].Wires:
|
||||
verts = []
|
||||
edges = fcgeo.sortEdges(w.Edges)
|
||||
for e in edges:
|
||||
|
@ -190,12 +188,12 @@ class Renderer:
|
|||
vnorm = self.wp.getLocalCoords(norm)
|
||||
if vnorm.getAngle(sh.normalAt(0,0)) > 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 +='<path '
|
||||
svg += 'd="'
|
||||
for w in self.faces[i].Wires:
|
||||
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 '
|
||||
for w in f[0].Wires:
|
||||
svg += self.getPathData(w)
|
||||
svg += '" '
|
||||
svg += 'stroke="#000000" '
|
||||
svg += 'stroke-width="' + str(linewidth) + '" '
|
||||
|
@ -475,3 +582,28 @@ class Renderer:
|
|||
svg += 'fill-rule: evenodd'
|
||||
svg += '"/>\n'
|
||||
return svg
|
||||
|
||||
def getSectionSVG(self,linewidth=0.02):
|
||||
"Returns a SVG fragment from cut faces"
|
||||
if DEBUG: print "Printing ", len(self.sections), " cutfaces"
|
||||
if not self.oriented:
|
||||
self.reorient()
|
||||
svg = ''
|
||||
for f in self.sections:
|
||||
fill = self.getFill(f[1])
|
||||
svg +='<path '
|
||||
svg += 'd="'
|
||||
for w in f[0].Wires:
|
||||
svg += self.getPathData(w)
|
||||
svg += '" '
|
||||
svg += 'stroke="#000000" '
|
||||
svg += 'stroke-width="' + str(linewidth) + '" '
|
||||
svg += 'style="stroke-width:0.01;'
|
||||
svg += 'stroke-miterlimit:1;'
|
||||
svg += 'stroke-linejoin:round;'
|
||||
svg += 'stroke-dasharray:none;'
|
||||
svg += 'fill:' + fill + ';'
|
||||
svg += 'fill-rule: evenodd'
|
||||
svg += '"/>\n'
|
||||
return svg
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ class plane:
|
|||
|
||||
def setFromPlacement(self,pl):
|
||||
"sets the working plane from a placement (rotaton ONLY)"
|
||||
rot= pl.Rotation
|
||||
rot = FreeCAD.Placement(pl).Rotation
|
||||
self.u = rot.multVec(FreeCAD.Vector(1,0,0))
|
||||
self.v = rot.multVec(FreeCAD.Vector(0,1,0))
|
||||
self.axis = rot.multVec(FreeCAD.Vector(0,0,1))
|
||||
|
|
|
@ -559,6 +559,7 @@ def findWires(edgeslist):
|
|||
|
||||
edges = edgeslist[:]
|
||||
wires = []
|
||||
lost = []
|
||||
while edges:
|
||||
e = edges[0]
|
||||
if not wires:
|
||||
|
@ -568,19 +569,28 @@ def findWires(edgeslist):
|
|||
else:
|
||||
found = False
|
||||
for w in wires:
|
||||
if found:
|
||||
break
|
||||
for we in w:
|
||||
if touches(e,we):
|
||||
edges.remove(e)
|
||||
w.append(e)
|
||||
found = True
|
||||
break
|
||||
else:
|
||||
# edge doesn't connect with any existing group
|
||||
edges.remove(e)
|
||||
wires.append([e])
|
||||
return wires
|
||||
if not found:
|
||||
for we in w:
|
||||
if touches(e,we):
|
||||
edges.remove(e)
|
||||
w.append(e)
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if e in lost:
|
||||
# we already tried this edge, and still nothing
|
||||
edges.remove(e)
|
||||
wires.append([e])
|
||||
lost = []
|
||||
else:
|
||||
# put to the end of the list
|
||||
edges.remove(e)
|
||||
edges.append(e)
|
||||
lost.append(e)
|
||||
nwires = []
|
||||
for w in wires:
|
||||
nwires.append(Part.Wire(w))
|
||||
return nwires
|
||||
|
||||
def superWire(edgeslist,closed=False):
|
||||
'''superWire(edges,[closed]): forces a wire between edges that don't necessarily
|
||||
|
@ -993,7 +1003,7 @@ def isPlanar(shape):
|
|||
return False
|
||||
return True
|
||||
|
||||
def findWires(edges):
|
||||
def findWiresOld(edges):
|
||||
'''finds connected edges in the list, and returns a list of lists containing edges
|
||||
that can be connected'''
|
||||
def verts(shape):
|
||||
|
|
Loading…
Reference in New Issue
Block a user