# Efficient way to delete all points below z = 0 on grid via Python script

I am trying to create a bowl in Blender by using the Python scipt. My method is as follows:

1. Clear cube in new file
2. Create UV sphere via Add --> Mesh --> UV Sphere
3. Tab (enter edit mode)
4. a' (unselect all points)
5. Highlight all vertices below the XY plane (Z < 0), and delete them.

Is there an efficient way to carry out step 5 via python scripting? Doing so in Blender and watching the Python script window doesn't help, since the selection is manual.

• you may loop through vertices and select them based on thier Z component; or use the view3d.select_border() but you'll need to have the correct view centred – Chebhou Sep 2 '15 at 23:51
• Hmm ... if you're deleting points below z=0 isn't that a cap, not a bowl? :) – ComputerScientist May 4 at 18:18

This small script loops through the vertices in the active object and deletes all the vertices that are below 0 on the local Z axis. The local axis starts at the object origin.

Just paste this in to the text editor in blender and with your UV sphere selected press the Run button.

import bpy

vert = bpy.context.object.data.vertices

bpy.ops.object.mode_set(mode='OBJECT')

for v in vert:
if v.co[2] < 0:
v.select = True

bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.mesh.delete(type='VERT')
bpy.ops.object.mode_set(mode = 'OBJECT')

• @A.Pung If David solved your issue please accept the answer for future visitors. Might be a good idea to start with the tour: blender.stackexchange.com/tour. Thanks. – p2or Sep 4 '15 at 12:19

Bmesh module solution for both, object and edit mode. Turn global_z_plane on for global Z < 0:

import bpy
import bmesh

ob = bpy.context.object
assert ob.type == "MESH"
mat = ob.matrix_world
me = ob.data

global_z_plane = False

if me.is_editmode:
bm = bmesh.from_edit_mesh(me)
else:
bm = bmesh.new()
bm.from_mesh(me)

for v in bm.verts:
co = mat * v.co if global_z_plane else v.co
if co.z < 0:
bm.verts.remove(v)

if bm.is_wrapped:
bmesh.update_edit_mesh(me)
else:
bm.to_mesh(me)
me.update()


[Using Blender 2.82a]

Here is an alternative view on David's excellent answer which can be done entirely on the command line. I found that when using his answer, my object was selected by default, so all vertices were selected (and thus the for loop was meaningless). I was unable to replicate his result by inverting the if condition to set vertices to be False. The reason? De-selecting an already selected vertex does not work.

Suppose I have the following script in the command line, called test.py:

import bpy

def clear_scene():
"""Clear existing objects in scene."""
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

def _debug(vert):
n_selected = len([v for v in vert if v.select])
n_deselect = len([v for v in vert if not v.select])
print('\nlen vertices = {}, {} selected, {} not selected'.format(
len(vert), n_selected, n_deselect))

def make_cap(radius=1.0, x=0.0, y=0.0, z=0.0, z_thresh=0.0):
vert = bpy.context.object.data.vertices
_debug(vert)
for v in vert:
if v.co[2] >= z_thresh:
v.select = False
_debug(vert)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete(type='VERT')
bpy.ops.object.mode_set(mode='OBJECT')

if __name__ == '__main__':
clear_scene()
make_cap()


Running blender -P test.py on the command line will produce this output for the prints:

len vertices = 482, 482 selected, 0 not selected
len vertices = 482, 225 selected, 257 not selected


Notice how the vertices of the sphere are already selected by default. However, even though de-selecting (via v.select=False) will work in the sense that the number of selected vertices is as expected (225 out of 482), the resulting Blender scene will not show anything. The sphere is still there, it's just not visible.

The only way I have been able to successfully get this working is by following this answer to go to edit mode and then de-select everything. Here's the Minimal Working Example script:

import bpy

def clear_scene():
"""Clear existing objects in scene."""
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

def _debug(vert):
n_selected = len([v for v in vert if v.select])
n_deselect = len([v for v in vert if not v.select])
print('\nlen vertices = {}, {} selected, {} not selected'.format(
len(vert), n_selected, n_deselect))

def make_cap(radius=1.0, x=0.0, y=0.0, z=0.0, z_thresh=0.0):
bpy.ops.object.mode_set(mode="EDIT") #Activating Edit mode
bpy.ops.mesh.select_all(action='DESELECT') #Deselecting all
bpy.ops.object.mode_set(mode="OBJECT") #Going back to Object mode
vert = bpy.context.object.data.vertices
_debug(vert)
for v in vert:
if v.co[2] < z_thresh:
v.select = True
_debug(vert)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete(type='VERT')
bpy.ops.object.mode_set(mode='OBJECT')

if __name__ == '__main__':
clear_scene()
make_cap()


Running blender -P test.py on the command line will produce this output for the prints:

len vertices = 482, 0 selected, 482 not selected
len vertices = 482, 225 selected, 257 not selected


But more importantly, here's what the scene looks like;

As desired!