#!BPY """ Name: 'Cross Section' Blender: 246 Group: 'Object' Tooltip: 'Creates cross-sections between selected objects with active Plane' """ __author__ = ["Cambo, Yorik van Havre, alxarch"] __url__ = ("blender", "blenderartists", "http://yorik.orgfree.com") __version__ = "0.1.6" __bpydoc__ = """\ Script made by Yorik based upon a code by Cambo fouond on blenderArtists This scripts creates cross-sections in selected objects, at intersection with active Plane object. Select objects you want to cut, then select (make active) the cutting plane, then run the script. The resulting section parts will be filled and nicely placed inside a group for easy selection A couple of limitations: - Only Mesh and Surface (by approximation) objects will be cut - The cutting object must be a plane (it must have only one face) - The cutting plane shouldn't have any aprents, if it does, the rotation from the parents will not affect the section's position and rotation """ # ***** BEGIN GPL LICENSE BLOCK ***** # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** import Blender, BPyMessages, BPyMesh from Blender import Scene, Mesh, Window, sys, Group, Object, Draw from Blender.Mathutils import \ Matrix, Vector, ProjectVecs, AngleBetweenVecs, TranslationMatrix def centerMass(me): """ Finds the Center of Mass on a mesh ( no Vertex Weights taken into account ). me: Blender Mesh - the mesh whose center you want to find Returns: Vector - The center of mass of the given geometry NOTE: The Center of Mass differs from the Bounding Box Center and is quite usefull for 2D Shapes. """ sum = Vector() for v in me.verts: sum += v.co center = (sum)/len(me.verts) return center def invRotation(mx): """ Finds the matrix that does the invert rotation of the given matrix but takes scale out of the way to keep things sane mx: Matrix - the matrix whose rotation you want to invert Returns: Matrix - a matrix of the inverse rotation """ r = mx.rotationPart() s = mx.scalePart() smx = Matrix() for i in range(0,3): smx[i][i] = s[i] rmx = r.resize4x4().invert() * smx return rmx def dupTest(object): """ Checks objects for duplicates enabled (any type) object: Blender Object. Returns: Boolean - True if object has any kind of duplicates enabled. """ if (object.enableDupFrames or \ object.enableDupGroup or \ object.enableDupVerts): return True else: return False def getObjectsAndDuplis(oblist,MATRICES=False,HACK=False): """ Return a list of real objects and duplicates and optionally their matrices oblist: List of Blender Objects MATRICES: Boolean - Check to also get the objects matrices default=False HACK: Boolean - See note default=False Returns: List of objects or List of tuples of the form:(ob,matrix) if MATRICES is set to True NOTE: There is an ugly hack here that excludes all objects whose name starts with "dpl_" to exclude objects that are parented to a duplicating object, User must name objects properly if hack is used. """ result = [] for ob in oblist: if dupTest(ob): dup_obs=ob.DupObjects if len(dup_obs): for dup_ob, dup_mx in dup_obs: if MATRICES: result.append((dup_ob,dup_mx)) else: result.append(dup_ob) else: if HACK: if ob.getName()[0:4] != "dpl_": if MATRICES: mx = ob.mat result.append((ob,mx)) else: result.append(ob) else: if MATRICES: ma = ob.mat result.append((ob,mx)) else: result.append(ob) return result def section(cut_me,mx,pp,pno,FILL=True): """ Finds the section mesh between a mesh and a plane cut_me: Blender Mesh - the mesh to be cut mx: Matrix - The matrix of object of the mesh for correct coordinates pp: Vector - A point on the plane pno: Vector - The cutting plane's normal FILL: Boolean - Check if you want to fill the resulting mesh, default=True Returns: Mesh - the resulting mesh of the section if any or Boolean - False if no section exists """ verts = [] ed_xsect = {} for ed in cut_me.edges: # getting a vector from each edge vertices to a point on the #plane #first apply transformation matrix so we get the real section v1 = ed.v1.co * mx co1 = v1 - pp v2 = ed.v2.co * mx co2 = v2 - pp # projecting them on the normal vector proj1 = ProjectVecs(co1,pno).length proj2 = ProjectVecs(co2,pno).length if (proj1 != 0): angle1 = AngleBetweenVecs(co1,pno) else: angle1 = 0 if (proj2 != 0): angle2 = AngleBetweenVecs(co2,pno) else: angle2 = 0 #Check to see if edge intersects. Also check if edge is coplanar to the #cutting plane (proj1=proj2=0) if ((proj1 == 0) or (proj2 == 0) or \ (angle1 > 90) != (angle2 > 90)) and \ (proj1+proj2 > 0) : #edge intersects. proj1 /= proj1+proj2 co = ((v2-v1)*proj1)+v1 verts.append(co) #store a mapping between the new vertices and the mesh's edges ed_xsect[ed.key] = len(ed_xsect) edges = [] for f in cut_me.faces: # get the edges that the intersecting points form # to explain this better: # If a face has an edge that is proven to be crossed then use the # mapping we created earlier to connect the edges properly ps = [ ed_xsect[key] for key in f.edge_keys if key in ed_xsect] if len(ps) == 2: edges.append(tuple(ps)) if edges: x_me = Mesh.New() x_me.verts.extend(verts) x_me.edges.extend(edges) #create a temp object and link it to the current sceneto be able to #apply rem Doubles and fill tmp_ob = Object.New('Mesh', 'tmp') tmp_ob.link(x_me) sce = Scene.getCurrent() sce.objects.link(tmp_ob) # do a remove doubles to cleanup the mesh, this is needed when there # is one or more edges coplanar to the plane. x_me.remDoubles(0.00001) if FILL: x_me.fill() #Cleanup sce.objects.unlink(tmp_ob) del tmp_ob return x_me else: return False def main(): sce = Scene.GetCurrent() ob_act = sce.objects.active sel_group = sce.objects.selected if not ob_act or ob_act.type != 'Mesh': BPyMessages.Error_NoMeshActive() return if len(sel_group)>=2: Window.WaitCursor(1) t = sys.time() #we need to be in edge or vertex mode otherwise fill() won't work global MODE MODE = Mesh.Mode() Mesh.Mode(Mesh.SelectModes.EDGE) cp = ob_act.getData(mesh=1) #Ask if we fill the closed parts or not fillCheck = Draw.PupMenu("Fill closed shapes?%t|Yes|No") #Get section Plane's transformation matrix and euler rotation p_mx = ob_act.matrix p_rot = ob_act.rot #Get the plane normal vector and a point on the plane pno = cp.faces[0].no * p_mx.rotationPart() pp = cp.verts[0].co * p_mx #filter selection to get back any duplis to cut them as well oblist = getObjectsAndDuplis(sel_group,MATRICES=True,HACK=True) #deselect all selected objects so we can select new ones and put #them into the group that will contain all new objects for o in sel_group: o.sel=False #create a list to hold all objects that we'll create parts = [] for o,mx in oblist: typ=o.getType() if o != ob_act and \ (typ =="Mesh" or typ=="Surf" or typ=="Curve"): #Use BpyMesh to get mesh data so that modifiers are applied #and to be able to cut surfaces and curves (curves behave #strange though and don't seem to be very usefull cut_me = BPyMesh.getMeshFromObject(o) #Run the main function x_me = section(cut_me,mx,pp,pno,fillCheck) #if there's no intersection just skip the object creation if x_me: part_ob = Object.New('Mesh','partofsection') part_ob.link(x_me) # Reset section part's center and orientation # Since it's a 2d object it's best to use the objects # center of mass instead of bounding box center loc = Vector(part_ob.getLocation()) center = centerMass(x_me) + loc lmx = TranslationMatrix(loc-center) x_me.transform(lmx,True) rmx = invRotation(p_mx) x_me.transform(rmx,True) part_ob.setLocation(center) part_ob.setEuler(p_rot) parts.append(part_ob) sce.objects.link(part_ob) # select the parts so that the user can do sth with them # immidiately after the script is done part_ob.sel=True else: print "object does not intersect, continuing hapilly" #Put the parts of the section into a new group so that it's easy to # select them all sect_grp = Group.New('section') sect_grp.objects=parts else: print "selection is empty, no object to cut!" #Set the selection mode to whatever it was before we changed it Mesh.Mode(MODE) print 'CrossSection finished in %.2f seconds' % (sys.time()-t) Window.WaitCursor(0) Blender.Redraw() # This lets you import the script without running it if __name__ == '__main__': main()