Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2006-08-09 15:39:35 +0400
committerCampbell Barton <ideasman42@gmail.com>2006-08-09 15:39:35 +0400
commit567f1bef144da9577babc0094a9892be5987d2d4 (patch)
tree13237d39e777876830d1d2d0ff699ef940630751 /release
parent8cd491750e45931cceecbe85362c8b7ca6805c31 (diff)
Adding a cookie cutter that can cut multiple polyline meshes into multiple meshes into a mesh - allong the view axis.
leaving the faces inside the polyline selected. UV's are maintained but not vertex colors at the moment.
Diffstat (limited to 'release')
-rwxr-xr-xrelease/scripts/object_cookie_cutter.py659
1 files changed, 659 insertions, 0 deletions
diff --git a/release/scripts/object_cookie_cutter.py b/release/scripts/object_cookie_cutter.py
new file mode 100755
index 00000000000..ce03500f4de
--- /dev/null
+++ b/release/scripts/object_cookie_cutter.py
@@ -0,0 +1,659 @@
+#!BPY
+"""
+Name: 'Cookie Cut from View'
+Blender: 234
+Group: 'Object'
+Tooltip: 'Cut from the view axis, (Sel Meshes (only edges) into other meshes with faces)'
+"""
+__author__= "Campbell Barton"
+__url__= ["blender", "blenderartist"]
+__version__= "1.0"
+
+__bpydoc__= """\
+This script takes the selected mesh objects, devides them into 2 groups
+Cutters and The objects to be cut.
+
+Cutters are meshes with no faces, just edge loops. and any meshes with faces will be cut.
+
+Usage:
+
+Select 2 or more meshes, one with no faces (a closed polyline) and one with faces to cut.
+
+Align the view on the axis you want to cut.
+For shapes that have overlapping faces (from the view), hide any backfacing faces so they will be ignored during the cut.
+Run the script.
+
+You can choose to make the cut verts lie on the face that they were cut from or on the edge that cut them.
+This script supports UV coordinates and images.
+"""
+
+
+import Blender
+import BPyMathutils
+from math import sqrt
+reload(BPyMathutils)
+lineIntersect2D= BPyMathutils.lineIntersect2D
+Vector= Blender.Mathutils.Vector
+
+# Auto class
+def auto_class(slots):
+ exec('class container_class(object): __slots__=%s' % slots)
+ return container_class
+
+
+bignum= 1<<30
+def bounds_xy(iter_item):
+ '''
+ Works with types
+ MMesh.verts
+ MFace
+ MEdge
+ '''
+ xmin= ymin= bignum
+ xmax= ymax= -bignum
+ for v in iter_item:
+ x= v.co.x
+ y= v.co.y
+ if x<xmin: xmin= x
+ if y<ymin: ymin= y
+ if x>xmax: xmax= x
+ if y>ymax: ymax= y
+
+ return xmin, ymin, xmax, ymax
+
+def bounds_intersect(a,b):
+ '''
+ each tuple is
+ xmin, ymin, xmax, ymax
+ '''
+ if\
+ a[0]>b[2] or\
+ a[1]>b[3] or\
+ a[2]<b[0] or\
+ a[3]<b[1]:
+ return False
+ else:
+ return True
+
+def point_in_bounds(pt, bounds):
+ '''
+ each tuple is
+ xmin, ymin, xmax, ymax
+ '''
+ if\
+ pt.x<bounds[0] or\
+ pt.y<bounds[1] or\
+ pt.x>bounds[2] or\
+ pt.y>bounds[3]:
+ return False
+ else:
+ return True
+
+
+def point_in_poly2d(pt, fvco):
+ crazy_point= Vector(pt) # A point far outside the range of the terrain.
+ crazy_point.x= crazy_point.x - 10000000
+
+ #fvco= [v.co for v in face]
+ isect=0
+ for i in xrange(len(fvco)):
+ isect+= (lineIntersect2D(pt, crazy_point, fvco[i], fvco[i-1])[0] != None)
+
+ return isect%2 # odd number is an intersect which wouold be true (inside the face)
+
+
+# reuse me more.
+def sorted_edge_indicies(ed):
+ i1= ed.v1.index
+ i2= ed.v2.index
+ if i1>i2:
+ i1,i2= i2,i1
+ return i1, i2
+
+def sorted_indicies(i1, i2):
+ if i1>i2:
+ i1,i2= i2,i1
+ return i1, i2
+
+def fake_length2d(pt1, pt2):
+ '''
+ Only used for comparison so dont sqrt
+ '''
+ #return math.sqrt(abs(pow(x1-x2, 2)+ pow(y1-y2, 2)))
+ return pow(pt1[0]-pt2[0], 2) + pow(pt1[1]- pt2[1], 2)
+
+def length2d(pt1, pt2):
+ '''
+ Only used for comparison so dont sqrt
+ '''
+ #return math.sqrt(abs(pow(x1-x2, 2)+ pow(y1-y2, 2)))
+ return sqrt(pow(pt1[0]-pt2[0], 2) + pow(pt1[1]- pt2[1], 2))
+
+
+
+def tri_area_2d(v1, v2, v3):
+ e1 = length2d(v1, v2)
+ e2 = length2d(v2, v3)
+ e3 = length2d(v3, v1)
+ p = e1+e2+e3
+ return 0.25 * sqrt(abs(p*(p-2*e1)*(p-2*e2)*(p-2*e3)))
+
+def tri_pt_find_z_2d(pt, tri):
+ """ Takes a face and 3d vector and assigns teh vectors Z to its on the face"""
+
+ l1= tri_area_2d(tri[1], tri[2], pt)
+ l2= tri_area_2d(tri[0], tri[2], pt)
+ l3= tri_area_2d(tri[0], tri[1], pt)
+
+ tot= l1+l2+l3
+ # Normalize
+ l1=l1/tot
+ l2=l2/tot
+ l3=l3/tot
+
+ z1= tri[0].z*l1
+ z2= tri[1].z*l2
+ z3= tri[2].z*l3
+
+ return z1+z2+z3
+
+
+def tri_pt_find_uv_2d(pt, tri, uvs):
+ """ Takes a face and 3d vector and assigns teh vectors Z to its on the face"""
+
+ l1= tri_area_2d(tri[1], tri[2], pt)
+ l2= tri_area_2d(tri[0], tri[2], pt)
+ l3= tri_area_2d(tri[0], tri[1], pt)
+
+ tot= l1+l2+l3
+ if not tot: # No area, just return the first uv
+ return Vector(uvs[0])
+
+ # Normalize
+ l1=l1/tot
+ l2=l2/tot
+ l3=l3/tot
+
+ uv1= uvs[0]*l1
+ uv2= uvs[1]*l2
+ uv3= uvs[2]*l3
+
+ return uv1+uv2+uv3
+
+
+
+
+def mesh_edge_dict(me):
+ ed_dict= {}
+ for f in me.faces:
+ if not f.hide:
+ fidx= [v.index for v in f]
+ for i in xrange(len(fidx)):
+ edkey= sorted_indicies(fidx[i], fidx[i-1])
+ try:
+ ed_dict[edkey].append(f)
+ except:
+ ed_dict[edkey]= [f]
+
+ return ed_dict
+
+
+
+def terrain_cut_2d(t, c, PREF_Z_LOC):
+ '''
+ t is the terrain
+ c is the cutter
+
+ PREF_Z_LOC: 0 - from terrain face
+ 1 - from cutter edge
+
+ returns nothing
+ '''
+
+ # do we have a 2d intersection
+ if not bounds_intersect(t.bounds, c.bounds):
+ return
+
+ # Local vars
+ me_t= t.mesh
+ me_c= c.mesh
+
+ has_uv= me_t.faceUV
+
+ Blender.Mesh.Mode(Blender.Mesh.SelectModes['VERTEX'])
+ '''
+ first assign a face terrain face for each cutter verticie
+ '''
+ cut_verts_temp= list(me_c.verts)
+ cut_vert_terrain_faces= [None] * len(me_c.verts)
+ vert_z_level= [-10.0] * len(me_c.verts)
+
+ for v in me_c.verts:
+ v_index= v.index
+ v_co= v.co
+ for fidx, f in enumerate(me_t.faces):
+ if not f.hide:
+ if point_in_bounds(v_co, t.face_bounds[fidx]):
+ f_v= [vv.co for vv in f]
+ if point_in_poly2d(v_co, f_v):
+
+
+ if PREF_Z_LOC==0:
+ '''
+ Get the z location from the face.
+ '''
+
+ if len(f_v)==3:
+ vert_z_level[v_index]= tri_pt_find_z_2d(v_co, (f_v[0], f_v[1], f_v[2]) )
+ else:
+ # Quad, which side are we on?
+ a1= tri_area_2d(f_v[0], f_v[1], v_co)
+ a2= tri_area_2d(f_v[1], f_v[2], v_co)
+
+ a3= tri_area_2d(f_v[0], f_v[1], f_v[2])
+
+ if a1+a2<a3:
+ vert_z_level[v_index]= tri_pt_find_z_2d(v_co, (f_v[0], f_v[1], f_v[2]) )
+ else:
+ vert_z_level[v_index]= tri_pt_find_z_2d(v_co, (f_v[0], f_v[2], f_v[3]) )
+
+ else: # PREF_Z_LOC==1
+ '''
+ Get the z location from the vert
+ '''
+ vert_z_level[v_index]= v_co.z
+
+ # Non overlapping faces in terrain mean we can break
+ cut_vert_terrain_faces[v_index]= f
+ break
+
+ del cut_verts_temp
+
+
+
+ edge_intersections= []
+ edge_isect_type= auto_class(['point', 'ed_terrain', 'ed_cut'])
+
+ # intersect cutter faces with terrain edges.
+ for ei_t, ed_t in enumerate(me_t.edges):
+
+ eb_t= t.edge_bounds[ei_t]
+ if bounds_intersect(eb_t, c.bounds): # face/cutter bounds intersect?
+ # Loop through the cutter edges.
+ for ei_c, ed_c in enumerate(me_c.edges):
+ # If the cutter edge has 2 verts inside the same face then we can ignore it
+ # Bothe are different faces or None
+ if cut_vert_terrain_faces[ed_c.v1.index] != cut_vert_terrain_faces[ed_c.v2.index] or\
+ cut_vert_terrain_faces[ed_c.v1.index] == cut_vert_terrain_faces[ed_c.v2.index] == None:
+ eb_c= c.edge_bounds[ei_c]
+ if bounds_intersect(eb_t, eb_c): # face/edge bounds intersect?
+ # Now we know the 2 edges might intersect, we'll do a propper test
+
+ xi, yi= lineIntersect2D(ed_t.v1.co, ed_t.v2.co, ed_c.v1.co, ed_c.v2.co)
+ if xi != None:
+
+ ed_isect= edge_isect_type()
+ ed_isect.point= Vector(xi,yi,0) # fake 3d
+
+ # Find the interpolation Z point
+
+ if PREF_Z_LOC==0:
+ '''
+ Terrains edge
+ '''
+ l1= length2d(ed_isect.point, ed_t.v1.co)
+ l2= length2d(ed_isect.point, ed_t.v2.co)
+ ed_isect.point.z= ((l2*ed_t.v1.co.z) + (l1*ed_t.v2.co.z)) / (l1+l2)
+ else:
+ '''
+ Cutters edge
+ '''
+ l1= length2d(ed_isect.point, ed_c.v1.co)
+ l2= length2d(ed_isect.point, ed_c.v2.co)
+ ed_isect.point.z= ((l2*ed_c.v1.co.z) + (l1*ed_c.v2.co.z)) / (l1+l2)
+
+ ed_isect.ed_terrain= ed_t
+ ed_isect.ed_cut= ed_c
+
+ edge_intersections.append(ed_isect)
+
+ if not edge_intersections:
+ return
+
+ # Now we have collected intersections we need to apply them
+
+ # Find faces that have intersections, these faces will need to be cut.
+ faces_intersecting= {} # face index as key, list of edges as values
+ for ed_isect in edge_intersections:
+
+ try:
+ faces= t.edge_dict[ sorted_edge_indicies(ed_isect.ed_terrain) ]
+ except:
+ # If the faces are hidden then the faces wont exist.
+ faces= []
+
+ for f in faces:
+ try:
+ faces_intersecting[f.index].append(ed_isect)
+ except:
+ faces_intersecting[f.index]= [ed_isect]
+
+ # this list is used to store edges that are totaly inside a face ( no intersections with terrain)
+ # we can remove these as we
+ face_containing_edges= [[] for i in xrange(len(me_t.faces))]
+ for ed_c in me_c.edges:
+ if cut_vert_terrain_faces[ed_c.v1.index]==cut_vert_terrain_faces[ed_c.v2.index] != None:
+ # were inside a face.
+ face_containing_edges[cut_vert_terrain_faces[ed_c.v1.index].index].append(ed_c)
+
+ # New Mesh for filling faces only
+ new_me= Blender.Mesh.New()
+ scn= Blender.Scene.GetCurrent()
+ ob= Blender.Object.New('Mesh')
+ ob.link(new_me)
+ scn.link(ob)
+ ob.sel= True
+
+ new_faces= []
+ new_faces_props= []
+ new_uvs= []
+ new_verts= []
+
+ # Loop through inter
+ for fidx_t, isect_edges in faces_intersecting.iteritems():
+ f= me_t.faces[fidx_t]
+ f_v= f.v
+ fidxs_s= [v.index for v in f_v]
+
+ # Make new fake edges for each edge, each starts as a list of 2 verts, but more verts can be added
+ # This list will then be sorted so the edges are in order from v1 to v2 of the edge.
+ face_new_verts= [ (f_v[i], [], f_v[i-1]) for i in xrange(len(f_v)) ]
+ # if len(face_new_verts) < 3: raise 'weirdo'
+
+ face_edge_dict = dict( [(sorted_indicies(fidxs_s[i], fidxs_s[i-1]), i) for i in xrange(len(f_v))] )
+
+ for ed_isect in isect_edges:
+ edge_index_in_face = face_edge_dict[ sorted_edge_indicies(ed_isect.ed_terrain) ]
+ # Add this intersection to the face
+ face_new_verts[edge_index_in_face][1].append(ed_isect)
+
+ # Now sort the intersections
+ for new_edge in face_new_verts:
+ if len(new_edge[1]) > 1:
+ # We have 2+ verts to sort
+ edv1= tuple(new_edge[0].co) # 3d but well only use the 2d part
+ new_edge[1].sort(lambda a,b: cmp(fake_length2d(a.point, edv1), fake_length2d(b.point, edv1) ))
+
+ # now build up a new face by getting edges
+ random_face_edges= []
+ unique_verts= [] # store vert
+ rem_double_edges= {}
+
+ def add_edge(p1, p2):
+ k1= tuple(p1)
+ k2= tuple(p2)
+
+ # Adds new verts where needed
+ try:
+ i1= rem_double_edges[k1]
+ except:
+ i1= rem_double_edges[k1]= len(rem_double_edges)
+ unique_verts.append(k1)
+
+ try:
+ i2= rem_double_edges[k2]
+ except:
+ i2= rem_double_edges[k2]= len(rem_double_edges)
+ unique_verts.append(k2)
+
+ random_face_edges.append( (i1, i2) )
+
+
+
+ # edges that dont have a vert in the face have to span between to intersection points
+ # since we dont know the other point at any 1 time we need to remember edges that
+ # span a face and add them once we'v collected both
+ # first add outline edges
+ edge_span_face= {}
+ for new_edge in face_new_verts:
+ new_edge_subdiv= len(new_edge[1])
+ if new_edge_subdiv==0:
+ # no subdiv edges, just add
+ add_edge(new_edge[0].co, new_edge[2].co)
+ elif new_edge_subdiv==1:
+ add_edge(new_edge[0].co, new_edge[1][0].point)
+ add_edge(new_edge[1][0].point, new_edge[2].co)
+ else:
+ # 2 or more edges
+ add_edge(new_edge[0].co, new_edge[1][0].point)
+ add_edge(new_edge[1][-1].point, new_edge[2].co)
+
+ # now add multiple
+ for i in xrange(new_edge_subdiv-1):
+ add_edge(new_edge[1][i].point, new_edge[1][i+1].point)
+
+ # done adding outline
+ # while looping through the edge subdivs, add the edges that intersect
+
+
+ for ed_isect in new_edge[1]:
+ ed_cut= ed_isect.ed_cut
+ if cut_vert_terrain_faces[ed_cut.v1.index]==f:
+ # our first vert is inside the face
+ point= Vector(ed_cut.v1.co)
+ point.z= vert_z_level[ed_cut.v1.index]
+
+ add_edge(point, ed_isect.point)
+ elif cut_vert_terrain_faces[ed_cut.v2.index]==f:
+ # assume second vert is inside the face
+ point= Vector(ed_cut.v2.co)
+ point.z= vert_z_level[ed_cut.v2.index]
+ add_edge(point, ed_isect.point)
+ else:
+ # this edge has no verts in the face so it will need to be clipped in 2 places
+ try:
+ point= edge_span_face[ed_cut]
+
+ # if were here it worked ;)
+ add_edge(point, ed_isect.point)
+
+ except:
+ # add the first intersecting point
+ edge_span_face[ed_cut]= ed_isect.point
+
+ # now add all edges that are inside the the face
+ for ed_c in face_containing_edges[fidx_t]:
+ point1= Vector(ed_c.v1.co)
+ point2= Vector(ed_c.v2.co)
+ point1.z= vert_z_level[ed_c.v1.index]
+ point2.z= vert_z_level[ed_c.v2.index]
+ add_edge(point1, point2)
+
+ new_me.verts.extend(unique_verts)
+ new_me.edges.extend(random_face_edges)
+ new_me.sel= 1
+
+ # backup the z values, fill and restore
+
+ backup_z= [v.co.z for v in new_me.verts]
+ for v in new_me.verts: v.co.z= 0
+ #raise 'as'
+ new_me.fill()
+ for i, v in enumerate(new_me.verts): v.co.z= backup_z[i]
+
+
+ # ASSIGN UV's
+ if has_uv:
+ f_uv= f_uv_mod= f.uv
+ f_vco= f_vco_mod= [v.co for v in f]
+
+ # f is the face, get the uv's from that.
+
+ uvs= [None] * len(new_me.verts)
+ for i, v in enumerate(new_me.verts):
+ v_co= v.co
+ f_uv_mod= f_uv
+ f_vco_mod= f_vco
+
+ if len(f_v)==4:
+ # Quad, which side are we on?
+ a1= tri_area_2d(f_vco[0], f_vco[1], v_co)
+ a2= tri_area_2d(f_vco[1], f_vco[2], v_co)
+
+ a3= tri_area_2d(f_vco[0], f_vco[1], f_vco[2])
+ if a1+a2 > a3:
+ # 0,2,3
+ f_uv_mod= f_uv[0], f_uv[2], f_uv[3]
+ f_vco_mod= f_vco[0], f_vco[2], f_vco[3]
+ # else - side of 0,1,2 - dont modify the quad
+
+ uvs[i]= tri_pt_find_uv_2d(v_co, f_vco_mod, f_uv_mod)
+
+ new_uvs.extend(uvs)
+ new_faces_props.extend( [f.image] * len(new_me.faces) )
+
+ # collect the fill results
+ new_verts_len= len(new_verts) + len(me_t.verts)
+ new_faces.extend( [[v.index+new_verts_len for v in ff] for ff in new_me.faces] )
+
+
+
+ new_verts.extend(unique_verts)
+
+ new_me.verts= None
+ #raise 'error'
+
+ # Finished filling
+ scn.unlink(ob)
+
+
+ # Remove faces
+ face_len = len(me_t.faces)
+ verts_len = len(me_t.verts)
+ me_t.verts.extend(new_verts)
+ me_t.faces.extend(new_faces)
+
+ for i in xrange(len(new_faces)):
+ f= me_t.faces[face_len+i]
+
+ if has_uv:
+ img= new_faces_props[i]
+ if img: f.image= img
+
+ f_uv= f.uv
+ for ii, v in enumerate(f):
+ v_index= v.index-verts_len
+ new_uv= new_uvs[v_index]
+ uv= f_uv[ii]
+ uv.x= new_uv.x
+ uv.y= new_uv.y
+
+ for col in f.col:
+ col.r= col.g= col.b= 255
+
+ me_t.faces.delete(1, faces_intersecting.keys())
+ me_t.sel= 1
+ me_t.remDoubles(0.0000001)
+
+
+def main():
+ PREF_Z_LOC= Blender.Draw.PupMenu('Cut Z Location%t|Original Faces|Cutting Polyline')
+
+ if PREF_Z_LOC==-1:
+ return
+ PREF_Z_LOC-=1
+
+ Blender.Window.WaitCursor(1)
+
+ print '\nRunning Cookie Cutter'
+ time= Blender.sys.time()
+
+ obs= [ob for ob in Blender.Object.GetSelected() if ob.getType()=='Mesh']
+
+
+ # Divide into 2 lists- 1 with faces, one with only edges
+ terrains= [] #[me for me in mes if me.faces]
+ cutters= [] #[me for me in mes if not me.faces]
+
+ terrain_type= auto_class(['mesh', 'bounds', 'face_bounds', 'edge_bounds', 'edge_dict', 'cutters', 'matrix'])
+
+ for ob in obs:
+ me= ob.getData(mesh=1)
+
+ # a new terrain instance
+ t= terrain_type()
+
+ t.matrix= ob.matrixWorld * Blender.Window.GetViewMatrix()
+
+ # Transform the object by its matrix
+ me.transform(t.matrix)
+
+ # Set the terrain bounds
+ t.bounds= bounds_xy(me.verts)
+ t.edge_bounds= [bounds_xy(ed) for ed in me.edges]
+ t.mesh= me
+
+
+ if me.faces: # Terrain.
+ t.edge_dict= mesh_edge_dict(me)
+ t.face_bounds= [bounds_xy(f) for f in me.faces]
+ t.cutters= [] # Store cutting objects that cut us here
+ terrains.append(t)
+ elif len(me.edges)>2: # Cutter
+ cutters.append(t)
+
+ totcuts= len(terrains)*len(cutters)
+ if not totcuts:
+ Blender.Window.WaitCursor(0)
+ Blender.Draw.PupMenu('ERROR%t|Select at least 1 closed loop mesh (edges only)|as the cutter...|and 1 or more meshes to cut into')
+
+ crazy_point= Vector(100000, 100000)
+
+ for t in terrains:
+ for c in cutters:
+ # Main curring function
+ terrain_cut_2d(t, c, PREF_Z_LOC)
+
+ # Was the terrain touched?
+ if len(t.face_bounds) != len(t.mesh.faces):
+ t.edge_dict= mesh_edge_dict(t.mesh)
+ # remake the bounds
+ t.edge_bounds= [bounds_xy(ed) for ed in t.mesh.edges]
+ t.face_bounds= [bounds_xy(f) for f in t.mesh.faces]
+ t.cutters.append(c)
+
+ print '\t%i remaining' % totcuts
+ totcuts-=1
+
+ # SELECT INTERNAL FACES ONCE THIS TERRAIN IS CUT
+ Blender.Mesh.Mode(Blender.Mesh.SelectModes['FACE'])
+ t.mesh.sel= 0
+ for c in t.cutters:
+ edge_verts_c= [(ed_c.v1.co, ed_c.v2.co) for ed_c in c.mesh.edges]
+ for f in t.mesh.faces:
+ # How many edges do we intersect on our way to the faces center
+ if not f.hide and not f.sel: # Not alredy selected
+ c= f.cent
+ if point_in_bounds(c, t.bounds):
+ isect_count= 0
+ for edv1, edv2 in edge_verts_c:
+ xi, yi= lineIntersect2D(c, crazy_point, edv1, edv2)
+ if xi!=None:
+ isect_count+=1
+
+ if isect_count%2:
+ f.sel= 1
+ Blender.Mesh.Mode(Blender.Mesh.SelectModes['FACE'])
+
+
+ # Restore the transformation
+ for data in (terrains, cutters):
+ for t in data:
+ t.mesh.transform(t.matrix.inverted())
+
+ Blender.Window.WaitCursor(0)
+ print 'terrains:%i cutters %i %.2f secs taken' % (len(terrains), len(cutters), Blender.sys.time()-time)
+
+
+if __name__=='__main__':
+ main() \ No newline at end of file