Skip to content

Instantly share code, notes, and snippets.

@milasudril
Last active January 31, 2017 17:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milasudril/960f5c2c4c3735f3fdea566cfdc003d9 to your computer and use it in GitHub Desktop.
Save milasudril/960f5c2c4c3735f3fdea566cfdc003d9 to your computer and use it in GitHub Desktop.
Projected convex hull
# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Contributor(s): Torbjörn Ratshman.
#
# ***** END GPL LICENSE BLOCK *****
#
bl_info = {'name': 'Projected convhull', 'category': 'Object'}
import bpy
import bmesh
import mathutils
import pdb
class ProjectedConvhull(bpy.types.Operator):
bl_idname='object.create_proj_convhull'
bl_label='Create projected convex hull'
bl_options={'REGISTER','UNDO'}
plane_size_low_x=bpy.props.FloatProperty(name='Lower plane x radius',default=1,min=1e-6,max=1e6)
plane_size_low_y=bpy.props.FloatProperty(name='Lower plane y radius',default=1,min=1e-6,max=1e6)
plane_size_high_x=bpy.props.FloatProperty(name='Higher plane x radius',default=1,min=1e-6,max=1e6)
plane_size_high_y=bpy.props.FloatProperty(name='Higher plane y radius',default=1,min=1e-6,max=1e6)
height=bpy.props.FloatProperty(name='Column height',default=1,min=1e-6,max=1e6)
n_planes=bpy.props.IntProperty(name='Number of cuts',default=10,min=2,max=100)
def execute(self,context):
pos_init=context.scene.cursor_location
dz=(self.height-pos_init.z)/self.n_planes
dsx=(self.plane_size_high_x - self.plane_size_low_x)/self.n_planes
dyx=(self.plane_size_high_y - self.plane_size_low_y)/self.n_planes
obj_sel=context.object
objs=[]
for k in range(0,self.n_planes):
#Add plane
pos=(pos_init.x,pos_init.y,pos_init.z + k*dz)
bpy.ops.mesh.primitive_plane_add(location=pos,enter_editmode=True)
bpy.ops.transform.resize(value=(self.plane_size_low_x + k*dsx,self.plane_size_low_x + k*dsx, 1.0))
#Intersect with selected object
bpy.ops.object.mode_set(mode='OBJECT')
modifier=context.object.modifiers.new('Bool','BOOLEAN')
modifier.operation='INTERSECT'
modifier.object=obj_sel
bpy.ops.object.modifier_apply(apply_as='DATA',modifier='Bool')
#Compute 2d convex hull
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={'value':(0,0,1)})
mesh=bmesh.from_edit_mesh(context.object.data)
for v in mesh.verts:
v.select = True
bpy.ops.mesh.convex_hull(join_triangles=False,use_existing_faces=False)
bpy.ops.mesh.select_all(action='DESELECT')
for v in mesh.verts:
if v.co.z > 0.5:
v.select = True
bpy.ops.mesh.delete()
for v in mesh.verts:
for f in mesh.faces:
if mathutils.geometry.intersect_point_tri_2d(v.co,f.verts[0].co,f.verts[1].co,f.verts[2].co) \
and v!=f.verts[0] and v!=f.verts[1] and v!=f.verts[2]:
v.select=True
bpy.ops.mesh.delete()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.edge_face_add()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
#Add result to list of objexts
objs.append(context.object)
#Join planes
for obj in objs:
obj.select=True
bpy.ops.object.join()
#Apply any transformation
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.transform_apply(location=True)
#Remove any double
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles()
#Bridge edge loops
bpy.ops.mesh.bridge_edge_loops()
#Remove interior faces
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_interior_faces()
bpy.ops.mesh.delete(type='FACE')
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
@classmethod
def poll(cls, context):
if context.object is not None:
return context.object.type=='MESH'
return 0
def register():
bpy.utils.register_class(ProjectedConvhull)
bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
bpy.utils.VIEW32_MT_object.remove(menu_func)
bpy.utils.unregister_class(ProjectedConvhull)
def menu_func(self, context):
self.layout.operator(ProjectedConvhull.bl_idname)
if __name__=='__main__':
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment