#!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()