Arch vector renderer now supports faces with holes

This commit is contained in:
Yorik van Havre 2012-04-04 17:33:13 -03:00
parent e3a648ea03
commit 300cd877b6
2 changed files with 95 additions and 58 deletions

View File

@ -146,6 +146,59 @@ def splitMesh(obj,mark=True):
return nlist
return [obj]
def makeFace(wires,method=2,cleanup=False):
'''makeFace(wires): makes a face from a list of wires, finding which ones are holes'''
import Part
if not isinstance(wires,list):
return Part.Face(wires)
elif len(wires) == 1:
return Part.Face(wires[0])
wires = wires[:]
print "inner wires found"
ext = None
max_length = 0
# cleaning up rubbish in wires
if cleanup:
for i in range(len(wires)):
wires[i] = fcgeo.removeInterVertices(wires[i])
print "garbage removed"
for w in wires:
# we assume that the exterior boundary is that one with
# the biggest bounding box
if w.BoundBox.DiagonalLength > max_length:
max_length = w.BoundBox.DiagonalLength
ext = w
print "exterior wire",ext
wires.remove(ext)
if method == 1:
# method 1: reverse inner wires
# all interior wires mark a hole and must reverse
# their orientation, otherwise Part.Face fails
for w in wires:
print "reversing",w
w.reverse()
print "reversed"
# make sure that the exterior wires comes as first in the list
wires.insert(0, ext)
print "done sorting", wires
if wires:
return Part.Face(wires)
else:
# method 2: use the cut method
mf = Part.Face(ext)
print "external face:",mf
for w in wires:
f = Part.Face(w)
print "internal face:",f
mf = mf.cut(f)
print "final face:",mf.Faces
return mf.Faces[0]
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'''
@ -165,33 +218,8 @@ def meshToShape(obj,mark=True):
wires = MeshPart.wireFromSegment(mesh, i)
print "wire done"
print wires
if len(wires) > 1:
# a segment can have inner holes
print "inner wires found"
ext = None
max_length = 0
# cleaning up rubbish in wires
for i in range(len(wires)):
wires[i] = fcgeo.removeInterVertices(wires[i])
for w in wires:
# we assume that the exterior boundary is that one with
# the biggest bounding box
if w.BoundBox.DiagonalLength > max_length:
max_length = w.BoundBox.DiagonalLength
ext = w
print "exterior wire",ext
wires.remove(ext)
# all interior wires mark a hole and must reverse
# their orientation, otherwise Part.Face fails
for w in wires:
print "reversing",w
#w.reverse()
print "reversed"
# make sure that the exterior wires comes as first in the list
wires.insert(0, ext)
print "done sorting", wires
if wires:
faces.append(Part.Face(wires))
faces.append(makeFace(wires))
print "done facing"
print "faces",faces

View File

@ -23,7 +23,7 @@
"The FreeCAD Arch Vector Rendering Module"
import FreeCAD,math,Part
import FreeCAD,math,Part,ArchCommands
from draftlibs import fcvec,fcgeo
DEBUG = True # if we want debug messages
@ -137,17 +137,19 @@ class Renderer:
def projectFace(self,face):
"projects a single face on the WP"
verts = []
edges = fcgeo.sortEdges(face.Edges)
wires = []
norm = face.normalAt(0,0)
for e in edges:
v = e.Vertexes[0].Point
v = self.wp.getLocalCoords(v)
verts.append(v)
verts.append(verts[0])
for w in face.Wires:
verts = []
edges = fcgeo.sortEdges(w.Edges)
for e in edges:
v = e.Vertexes[0].Point
v = self.wp.getLocalCoords(v)
verts.append(v)
verts.append(verts[0])
wires.append(Part.makePolygon(verts))
try:
sh = Part.makePolygon(verts)
sh = Part.Face(sh)
sh = ArchCommands.makeFace(wires)
except:
if DEBUG: print "Error: Unable to project face on the WP"
return None
@ -160,15 +162,17 @@ class Renderer:
def flattenFace(self,face):
"Returns a face where all vertices have Z = 0"
verts = []
edges = fcgeo.sortEdges(face.Edges)
for e in edges:
v = e.Vertexes[0].Point
verts.append(FreeCAD.Vector(v.x,v.y,0))
verts.append(verts[0])
wires = []
for w in face.Wires:
verts = []
edges = fcgeo.sortEdges(w.Edges)
for e in edges:
v = e.Vertexes[0].Point
verts.append(FreeCAD.Vector(v.x,v.y,0))
verts.append(verts[0])
wires.append(Part.makePolygon(verts))
try:
sh = Part.makePolygon(verts)
sh = Part.Face(sh)
sh = Part.Face(wires)
except:
if DEBUG: print "Error: Unable to flatten face"
return None
@ -394,24 +398,29 @@ class Renderer:
svg = ''
for f in self.faces:
svg +='<path '
edges = fcgeo.sortEdges(f.Edges)
v = edges[0].Vertexes[0].Point
svg += 'd="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 += 'd="'
for w in f.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 '
svg += '" '
svg += 'stroke="#000000" '
svg += 'stroke-width="0.01 px" '
svg += 'style="stroke-width:0.01;'
svg += 'stroke-miterlimit:1;'
svg += 'stroke-linejoin:round;'
svg += 'stroke-dasharray:none;'
svg += 'fill:#aaaaaa"'
svg += '/>\n'
svg += 'fill:#aaaaaa;'
svg += 'fill-rule: evenodd'
svg += '"/>\n'
return svg