# Using python to create cuboid with a hole in the centre

I wish to create a basic rectangle with a hole in it to the scene upon running the python script. I understand how to create a simple cuboid - however I can't find an easy way of creating one with a rectangular hole through the centre short of splitting it up into four rectangles however I want it to be one structure.

My code for the cuboid so far is:

import bpy

verts = [(0,0,0),(0,5,0),(1,5,0),(1,0,0),(0,0,5),(0,5,5),(1,5,5),(1,0,5)]
faces = [(0,1,2,3), (4,5,6,7), (0,4,5,1), (1,5,6,2), (2,6,7,3), (3,7,4,0)]

grat1mesh = bpy.data.meshes.new("grat1")
grat1obj = bpy.data.objects.new("grat1", grat1mesh)
grat1obj.location = bpy.context.scene.cursor_location
grat1mesh.from_pydata(verts,[],faces)
grat1mesh.update(calc_edges=True)


Any help is appreciated, thanks.

Using from_pydata

Make an inner and outer square of vertices for top and bottom. Fill top with 4 trapezium faces. All other face indexes can be calculated from the top faces by adding appropriate index offset to them.

import bpy
from mathutils import Vector
context = bpy.context

scene = context.scene
mesh = bpy.data.meshes.new("mesh")
# 2d square
def xysquare2d(z=0, scale=1):
r = [-scale, scale]
return [Vector((x, y, z)) for x in r for y in r]

verts = []
# top verts
verts.extend(xysquare2d(z=2, scale=2))
verts.extend(xysquare2d(z=2))
# bottom verts
verts.extend(xysquare2d(z=-2))
verts.extend(xysquare2d(z=-2, scale=2))
top_faces = [(0, 1, 5, 4),
(1, 3, 7, 5),
(3, 2, 6, 7),
(2, 0, 4, 6)]
# make all the other faces from top faces
bottom_faces = [(i + 8, j + 8, k + 8, l + 8) for i, j, k, l in top_faces]
outer_faces =  [(i, j, k + 8, l + 8) for i, j, k, l in top_faces]
inner_faces =  [(i + 8, j + 8, k, l) for i, j, k, l in top_faces]
faces = top_faces  + bottom_faces + outer_faces + inner_faces

mesh.from_pydata(verts, [], faces)
obj = bpy.data.objects.new("obj", mesh)


Can create pydata from a mesh.
Make the model, then run this script on the mesh.

print("verts = [")
for v in mesh.vertices:
print("\t", [axis for axis in v.co], ",")
print("\t]")
print("faces = [")
for f in mesh.polygons:
print("\t", [i for i in f.vertices], ",")
print("\t]")


Which when run on mesh created above results in

verts = [
[-2.0, -2.0, 2.0] ,
[-2.0, 2.0, 2.0] ,
[2.0, -2.0, 2.0] ,
....
[2.0, 2.0, -2.0] ,
]
faces = [
[0, 1, 5, 4] ,
[1, 3, 7, 5] ,
....
[10, 8, 4, 6] ,
]


Using boolean modifier

If you don't mind ngons, can use boolean difference, append this script to your original

# copy the mesh
grat2obj = grat1obj.copy()
grat2obj.data = grat1mesh.copy()
# move it up 1, 1
grat2obj.location += Vector((-0.5, 1.25, 1.25))
# scale it
grat2obj.scale = (2, 0.5, 0.5)

bm = grat1obj.modifiers.new("Chop", 'BOOLEAN')
bm.object = grat2obj
bm.operation = 'DIFFERENCE'
# apply the modifier
scene.objects.active = grat1obj
bpy.ops.object.modifier_apply(modifier=bm.name)
# remove chopper


Using bmesh

Add a plane, inset it, remove middle face, solidify.

import bpy
import bmesh
mesh = bpy.context.object.data
bm = bmesh.new()
bmesh.ops.create_grid(bm,
x_segments=2,
y_segments=2,
size=1)

bmesh.ops.inset_individual(bm, faces=bm.faces, thickness=0.3)
# take out middle
[bm.faces.remove(f) for f in bm.faces if all(not e.is_boundary for e in f.edges)]
# extrude
bmesh.ops.solidify(bm, geom=bm.faces, thickness=1)
bm.to_mesh(mesh)
mesh.update()

• Thanks a bunch that was the best explanation I could've wanted. Thought it might've been slightly easier than this, however! – CooperCape Sep 27 '17 at 18:54
• it's late, I'll get around to bmesh later. With bmesh you don't have to keep tabs of the vert indices. Possibly easiest to add two prim cubes, remove tops, then bridge. Or 4 traps and extrude. Is there any particular reason you want to use from_pydata. – batFINGER Sep 27 '17 at 19:01
• Not at all. I'm sure I can work around it if there's an easier solution. Just seemed easier at the time to plot out a basic cube that way - and as such I thought a basic cube with a hole would be similar. – CooperCape Sep 27 '17 at 19:05