# written by Dealga McArdle
# parts based on Keith (Wahooney) Boshoff, cursor to intersection script and
# Paul Bourke's Shortest Line Between 2 lines
# PKHG from blenderartists.org suggested a few refinements in the Vector code,
# making the code a bit easier to read.
# ***** 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 bpy
import sys
from mathutils import Vector
__author__ = ['zeffii']
__version__ = '0.1'
__bpydoc__ = """
"""
bl_addon_info = {
'name': 'Mesh: Slice Intersect',
'author': 'zeffii',
'version': '0.1',
'blender': (2, 5, 4),
'location': 'View3D > EditMode > Specials',
'wiki_url': '',
'category': 'Mesh'}
##function lifted from 3dview_spacebar_menu
def LineLineIntersect (p1, p2, p3, p4):
# based on Paul Bourke's Shortest Line Between 2 lines
min = 0.0000001
v1 = p1 - p3 #Vector((p1.x - p3.x, p1.y - p3.y, p1.z - p3.z))
v2 = p4 - p3 #Vector((p4.x - p3.x, p4.y - p3.y, p4.z - p3.z))
if abs(v2.x) < min and abs(v2.y) < min and abs(v2.z) < min:
return None
v3 = p2 - p1
if abs(v3.x) < min and abs(v3.y) < min and abs(v3.z) < min:
return None
d1 = v1.dot(v2)
d2 = v2.dot(v3)
d3 = v1.dot(v3)
d4 = v2.dot(v2)
d5 = v3.dot(v3)
d = d5 * d4 - d2 * d2
if abs(d) < min:
return None
n = d1 * d2 - d3 * d4
mua = n / d
mub = (d1 + d2 * (mua)) / d4
#modified for clarity, and to use some neat Vector functionality.
return [p1 + mua * v3 , p3 + mub * v2]
##function partially lifted from 3dview_spacebar_menu
def checkEdges(Edge, obj):
p1 = Vector((Edge[0][0]))
p2 = Vector((Edge[0][1]))
p3 = Vector((Edge[1][0]))
p4 = Vector((Edge[1][1]))
line = LineLineIntersect(p1, p2, p3, p4)
if line == None: return None
tm = obj.matrix_world.copy()
point = ((line[0] + line[1]) / 2)
point = tm * point
return point
def makeGeometry(point,outer_points):
# print("start cutting")
# print("intersection point: " + str(point))
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete(type='EDGE') # removes edges + verts
(vert_count, edge_count) = getVertEdgeCount()
vert_count = len(vert_count)
edge_count = len(edge_count)
'''
print("vert count is now: " + str(vert_count))
print("Edge count is now: " + str(edge_count))
print(outer_points[0][0])
print(outer_points[0][1])
print(outer_points[1][0])
print(outer_points[1][1])
'''
bpy.ops.object.mode_set(mode='OBJECT') # to be sure.
o = bpy.context.active_object
va = Vector((outer_points[0][0]))
vb = point
vc = Vector((outer_points[0][1]))
vd = Vector((outer_points[1][0]))
ve = Vector((outer_points[1][1]))
o.data.vertices.add(5)
o.data.vertices[vert_count].co = va
o.data.vertices[vert_count+1].co = vb
o.data.vertices[vert_count+2].co = vc
o.data.vertices[vert_count+3].co = vd
o.data.vertices[vert_count+4].co = ve
oe = o.data.edges
oe.add(4)
oe[edge_count].vertices = [vert_count,vert_count+1]
oe[edge_count+1].vertices = [vert_count+2,vert_count+1]
oe[edge_count+2].vertices = [vert_count+3,vert_count+1]
oe[edge_count+3].vertices = [vert_count+4,vert_count+1]
def runCleanUp():
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.remove_doubles(limit=0.0001)
#unselect all
bpy.ops.mesh.select_all(action='TOGGLE')
def getMeshMatrix(obj):
is_editmode = (obj.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT')
edges = []
mesh = obj.data
verts = mesh.vertices
for e in mesh.edges:
if e.select:
edges.append(e)
meshMatrix = []
edgenum = 0
for edge_to_test in edges:
p1 = verts[edge_to_test.vertices[0]].co
p2 = verts[edge_to_test.vertices[1]].co
meshMatrix.append([(p1.x,p1.y,p1.z),(p2.x,p2.y,p2.z)])
edgenum += 1
return meshMatrix
def getVertEdgeCount():
bpy.ops.object.mode_set(mode='OBJECT')
vert_count = bpy.context.active_object.data.vertices
edge_count = bpy.context.active_object.data.edges
return (vert_count, edge_count)
def selectNewGeometry():
# potentially this is where i maybe go back to edit mode and select
# the newly added edges.
return
def initScript(context, self):
obj = bpy.context.active_object
meshMatrix = getMeshMatrix(obj)
(vert_count, edge_count) = getVertEdgeCount()
## force edit mode
bpy.ops.object.mode_set(mode='EDIT')
vSel = bpy.context.active_object.data.total_vert_sel
xv = len(vert_count)
xe = len(edge_count)
if len(meshMatrix) != 2:
print(str(len(meshMatrix)) +" select, make sure (only) 2 are selected")
else:
''' debug prints
print("begin--------------------")
print("the object has " + str(xv) + " verts")
print("currently " + str(vSel) + " verts are selected")
print("the object has " + str(xe) + " edges")
print("currently " + str(len(meshMatrix)) + " edges are selected")
print("--------------------end")
'''
if checkEdges(meshMatrix, obj) == None:
print("lines dont intersect")
else:
makeGeometry(checkEdges(meshMatrix, obj),meshMatrix)
runCleanUp()
# selectNewGeometry()
class SliceIntersectingEdges(bpy.types.Operator):
'''Finds visible intersection, existing or projected through extrapolation, creates new edges'''
bl_idname = "Slice at Intersection"
bl_label = "Slice Intersect"
@classmethod
def poll(self, context):
obj = context.active_object
return obj != None and obj.type == 'MESH'
def execute(self, context):
initScript(context, self)
return {'FINISHED'}
menu_func = (lambda self,
context: self.layout.operator(SliceIntersectingEdges.bl_idname,
text="Slice at Edge Intersection"))
def register():
bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
def unregister():
bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
if __name__ == "__main__":
register()
Monday, 27 September 2010
blender 2.54 Mesh Tools 2D Boolean on Edges (part 4)
the code is complete for basic intersection cutting, also extends non parallel edges to their extrapolated intersection point
Saturday, 25 September 2010
blender 2.54 Mesh Tools 2D Boolean on Edges (part 3)
I completed, and shuffled some functions around, can do some brief refactoring.
btw if you dont know what it does. get a mesh, select two intersecting edges, then run this script and look at what happened to the intersection point.
btw if you dont know what it does. get a mesh, select two intersecting edges, then run this script and look at what happened to the intersection point.
import bpy
import sys
from mathutils import Vector
##function lifted from 3dview_spacebar_menu
def LineLineIntersect (p1, p2, p3, p4):
# based on Paul Bourke's Shortest Line Between 2 lines
min = 0.0000001
v1 = p1 - p3 #Vector((p1.x - p3.x, p1.y - p3.y, p1.z - p3.z))
v2 = p4 - p3 #Vector((p4.x - p3.x, p4.y - p3.y, p4.z - p3.z))
if abs(v2.x) < min and abs(v2.y) < min and abs(v2.z) < min:
return None
v3 = p2 - p1
if abs(v3.x) < min and abs(v3.y) < min and abs(v3.z) < min:
return None
d1 = v1.dot(v2)
d2 = v2.dot(v3)
d3 = v1.dot(v3)
d4 = v2.dot(v2)
d5 = v3.dot(v3)
d = d5 * d4 - d2 * d2
if abs(d) < min:
return None
n = d1 * d2 - d3 * d4
mua = n / d
mub = (d1 + d2 * (mua)) / d4
#modified for clarity, and to use some neat Vector functionality.
return [p1 + mua * v3 , p3 + mub * v2]
##function partially lifted from 3dview_spacebar_menu
def checkEdges(Edge, obj):
p1 = Vector((Edge[0][0]))
p2 = Vector((Edge[0][1]))
p3 = Vector((Edge[1][0]))
p4 = Vector((Edge[1][1]))
line = LineLineIntersect(p1, p2, p3, p4)
if line == None: return None
tm = obj.matrix_world.copy()
point = ((line[0] + line[1]) / 2)
point = tm * point
return point
def makeGeometry(point,outer_points):
# print("start cutting")
# print("intersection point: " + str(point))
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete(type='EDGE') # removes edges + verts
(vert_count, edge_count) = getVertEdgeCount()
vert_count = len(vert_count)
edge_count = len(edge_count)
'''
print("vert count is now: " + str(vert_count))
print("Edge count is now: " + str(edge_count))
print(outer_points[0][0])
print(outer_points[0][1])
print(outer_points[1][0])
print(outer_points[1][1])
'''
bpy.ops.object.mode_set(mode='OBJECT') # to be sure.
o = bpy.context.active_object
va = Vector((outer_points[0][0]))
vb = point
vc = Vector((outer_points[0][1]))
vd = Vector((outer_points[1][0]))
ve = Vector((outer_points[1][1]))
o.data.vertices.add(5)
o.data.vertices[vert_count].co = va
o.data.vertices[vert_count+1].co = vb
o.data.vertices[vert_count+2].co = vc
o.data.vertices[vert_count+3].co = vd
o.data.vertices[vert_count+4].co = ve
oe = o.data.edges
oe.add(4)
oe[edge_count].vertices = [vert_count,vert_count+1]
oe[edge_count+1].vertices = [vert_count+2,vert_count+1]
oe[edge_count+2].vertices = [vert_count+3,vert_count+1]
oe[edge_count+3].vertices = [vert_count+4,vert_count+1]
def runCleanUp():
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.remove_doubles(limit=0.0001)
def getMeshMatrix(obj):
is_editmode = (obj.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT')
edges = []
mesh = obj.data
verts = mesh.vertices
for e in mesh.edges:
if e.select:
edges.append(e)
meshMatrix = []
edgenum = 0
for edge_to_test in edges:
p1 = verts[edge_to_test.vertices[0]].co
p2 = verts[edge_to_test.vertices[1]].co
meshMatrix.append([(p1.x,p1.y,p1.z),(p2.x,p2.y,p2.z)])
edgenum += 1
return meshMatrix
def getVertEdgeCount():
bpy.ops.object.mode_set(mode='OBJECT')
vert_count = bpy.context.active_object.data.vertices
edge_count = bpy.context.active_object.data.edges
return (vert_count, edge_count)
def selectNewGeometry():
# potentially this is where i maybe go back to edit mode and select
# the newly added edges.
return
def initScript():
obj = bpy.context.active_object
meshMatrix = getMeshMatrix(obj)
(vert_count, edge_count) = getVertEdgeCount()
## force edit mode
bpy.ops.object.mode_set(mode='EDIT')
vSel = bpy.context.active_object.data.total_vert_sel
xv = len(vert_count)
xe = len(edge_count)
if len(meshMatrix) != 2:
print(str(len(meshMatrix)) +" select, make sure (only) 2 are selected")
else:
''' debug prints
print("begin--------------------")
print("the object has " + str(xv) + " verts")
print("currently " + str(vSel) + " verts are selected")
print("the object has " + str(xe) + " edges")
print("currently " + str(len(meshMatrix)) + " edges are selected")
print("--------------------end")
'''
if checkEdges(meshMatrix, obj) == None:
print("lines dont intersect")
else:
makeGeometry(checkEdges(meshMatrix, obj),meshMatrix)
runCleanUp()
# selectNewGeometry()
# runs the joint
initScript()
Sunday, 19 September 2010
blender 2.54 Mesh Tools 2D Boolean on Edges (part 2)
this i think represents the most complicated case over which the code should eventually be able to iterate.

it should manipulate the given geometry into this:

it should manipulate the given geometry into this:
blender 2.54 Mesh Tools 2D Boolean on Edges (part 1)
While doing typography design in blender i was faced repeatedly with some very basic but tedious and time consuming operations. Instead of waiting for the pre 2.5 CAD tools to be updated to 2.5x I started coding some of the prerequisites.
here is what i put into blender console to get the selected edges of the current
object. ( tho, after selecting the edges you must enter object mode with the object still selected for this script to be able to get the information )
this spits out vertex data like :
the previous code snippet needs commas to seperate the list. This is purposely coded verbosely with many print statements while i'm bug testing my logic :) optimizing will happen when i'm confident this catches undesirable anomalies.
here is what i put into blender console to get the selected edges of the current
object. ( tho, after selecting the edges you must enter object mode with the object still selected for this script to be able to get the information )
import sys
obj = bpy.context.active_object
edges = []
mesh = obj.data
verts = mesh.vertices
# this for loop is from the 3dview_spacebar_menu
for e in mesh.edges:
if e.select:
edges.append(e)
meshMatrix = []
edgenum = 0
for edge_to_test in edges:
p1 = verts[edge_to_test.vertices[0]].co
p2 = verts[edge_to_test.vertices[1]].co
meshMatrix.append([(p1.x,p1.y,p1.z),(p2.x,p2.y,p2.z)])
edgenum += 1
for edge_ in meshMatrix:
edge_
this spits out vertex data like :
[(0.0, 1.0, 1.0), (5.211963127749186e-08, 0.0, 1.0)]
[(5.211963127749186e-08, 0.0, 1.0), (5.211963127749186e-08, 0.0, 2.0)]
[(0.6621248126029968, 0.0, 2.0), (0.6621248126029968, 0.0, 1.0)]
[(0.6621248126029968, 0.0, 1.0), (0.6621248126029968, 1.637786626815796, 1.0)]
[(0.6621248126029968, 1.637786626815796, -0.04176926612854004), (0.6621248126029968, 1.637786626815796, 1.0)]
[(2.0378873348236084, 1.637786626815796, 1.0), (2.0378873348236084, 1.637786626815796, -0.04176926612854004)]
[(2.0378873348236084, 1.637786626815796, 1.0), (2.0378873348236084, 0.0, 1.0)]
the previous code snippet needs commas to seperate the list. This is purposely coded verbosely with many print statements while i'm bug testing my logic :) optimizing will happen when i'm confident this catches undesirable anomalies.
# test
import sys
import math
preStantonList = [
[(0.0, 1.0, 1.0), (5.211963127749186e-08, 0.0, 1.0)],
[(5.211963127749186e-08, 0.0, 1.0), (5.211963127749186e-08, 0.0, 2.0)],
[(0.6621248126029968, 0.0, 2.0), (0.6621248126029968, 0.0, 1.0)],
[(0.6621248126029968, 0.0, 1.0), (0.6621248126029968, 1.637786626815796, 1.0)],
[(0.6621248126029968, 1.637786626815796, -0.04176926612854004), (0.6621248126029968, 1.637786626815796, 1.0)],
[(2.0378873348236084, 1.637786626815796, 1.0), (2.0378873348236084, 1.637786626815796, -0.04176926612854004)],
[(2.0378873348236084, 1.637786626815796, 1.0), (2.0378873348236084, 0.0, 1.0)]
]
def calcDifference(val1, val2):
if val1 > val2: return val1-val2
if val2 > val1: return val2-val1
if val1 == val2: return 0.0
def isListHomogenous(vertList):
'''
function returns True if no anomalies are found, False if one vert exceeds epsilon
'''
def average(vertList):
sum = 0.0
for i in vertList:
sum += i
avsum = sum / len(vertList)
return avsum
def isEpsilon(val1, val2):
min = 0.0000001
if calcDifference(val1, val2) == 0.0: return True
if calcDifference(val1, val2) < min: return True
return False
def checkList(vertList):
print average(vertList)
for i in vertList:
if not isEpsilon(i, average(vertList)): return False
# reaches this stage, means the variation is probably minimal
return True
#my Boolean
GOODTOGO = False
GOODTOGO = checkList(vertList)
return GOODTOGO
def printEdgeList(eList):
(p1x, p1y, p1z) = (0.0, 0.0, 0.0)
(p2x, p2y, p2z) = (0.0, 0.0, 0.0)
planarX = 0.0
planarY = 0.0
planarZ = 0.0
edgenum = 0
vertXList = []
vertYList = []
vertZList = []
xPlaneVerts = []
yPlaneVerts = []
zPlaneVerts = []
def sumList(coordinate_list):
summing = 0.0
for i in coordinate_list:
summing += i
return summing
def VertsDeltaInPlane(axis, value1, value2):
min = 0.0000001
difference = calcDifference(value1, value2)
# gets dirty for larger numbers, but we aren't doing intergalatic travel.
if difference < min: difference = 0.0
print "difference in axis " + axis + " component of this edge is " + str(difference)
return difference
def printCoordinate(val_x,val_y,val_z):
print "| x = " + str(val_x),
print "| y = " + str(val_y),
print "| z = " + str(val_z)
for item in eList:
print "=========="
print "edge [" + str(edgenum) + "] .....(" + str(edgenum+1) + ")"
print "vertex 0",
p1x = item[0][0]
p1y = item[0][1]
p1z = item[0][2]
printCoordinate(p1x,p1y,p1z)
print "vertex 1",
p2x = item[1][0]
p2y = item[1][1]
p2z = item[1][2]
printCoordinate(p2x,p2y,p2z)
planarX = VertsDeltaInPlane("x", p1x, p2x)
planarY = VertsDeltaInPlane("y", p1y, p2y)
planarZ = VertsDeltaInPlane("z", p1z, p2z)
# collects all vertices
xComp = [p1x,p2x]
yComp = [p1y,p2y]
zComp = [p1z,p2z]
xPlaneVerts.extend(xComp)
yPlaneVerts.extend(yComp)
zPlaneVerts.extend(zComp)
vertXList.append(planarX)
vertYList.append(planarY)
vertZList.append(planarZ)
edgenum += 1
def myRound(value):
return ".7f" % value
print "-------conclusion---------"
# this statement checks if the the verts on each edge or on the same plane
# check here if the selection is usable or not.
if sumList(vertXList) == 0.0:
print "x", xPlaneVerts
if isListHomogenous(xPlaneVerts):
print "GOOD TO GO!",
print "the plane is X at " + str(round(p1x,6))
else:
print "same plane but not same elevation"
elif sumList(vertYList) == 0.0:
print "y", yPlaneVerts
if isListHomogenous(yPlaneVerts):
print "GOOD TO GO!",
print "the plane is Y at " + str(round(p1y,6))
else:
print "same plane but not same elevation"
elif sumList(vertZList) == 0.0:
print "z", zPlaneVerts
if isListHomogenous(zPlaneVerts):
print "GOOD TO GO!",
print "the plane is Z at " + str(round(p1z,6))
else:
print "same plane but not same elevation"
else:
print "not on the same plane, the script will not proceed"
printEdgeList(preStantonList)
Subscribe to:
Posts (Atom)