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 xxmax: 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]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