5
$\begingroup$

I’m trying to fill a mesh with a grid and I wrote following code:

import bpy

### Settings ###
thickness = 0.01
distance = 0.5
k_segmentname = "Suzanne"

# TODO: check names for cylinder
bpy.ops.object.select_all(action = 'DESELECT')

# get dimensions
(x,y,z) = bpy.data.objects[k_segmentname].dimensions


#
# create z - Cylinders
#

# create first z-Cylinder
xlocation = x / 2

bpy.ops.mesh.primitive_cylinder_add()
bpy.data.objects['Cylinder'].dimensions = (thickness,thickness,z)
bpy.data.objects['Cylinder'].location = (xlocation,0,0)


yrepeattimes = (y / distance)
ycount = 0

# create z-Cylinder-Row in y-Direction
while (ycount < yrepeattimes):
    ylocation = (distance * ycount) - y / 2

    bpy.ops.mesh.primitive_cylinder_add()
    bpy.data.objects['Cylinder.001'].dimensions = (thickness,thickness,z)
    bpy.data.objects['Cylinder.001'].location = (xlocation,ylocation,0)

    bpy.ops.object.select_all(action = 'DESELECT')

    bpy.context.scene.objects.active = bpy.data.objects['Cylinder']
    bpy.data.objects['Cylinder'].select = True
    bpy.data.objects['Cylinder.001'].select = True

    bpy.ops.object.join()

    ycount = ycount + 1

# create second z-Cylinder-Row in y-Direction
xlocation = x / 2 - distance
bpy.ops.object.select_all(action = 'DESELECT')

bpy.data.objects['Cylinder'].select = True
bpy.ops.object.duplicate()

bpy.data.objects['Cylinder.001'].location = (xlocation,0,0)

bpy.ops.object.select_all(action = 'DESELECT')

# duplicate second z-Cylinder-Row in y-Direction and join with first
xrepeattimes = (x / distance) - 2
xcount = 0

while (xcount < xrepeattimes):

    xlocation = (distance * xcount) - (x / 2) + distance
    bpy.ops.object.select_all(action = 'DESELECT')
    bpy.data.objects['Cylinder.001'].select = True
    bpy.ops.object.duplicate()
    bpy.data.objects['Cylinder.002'].location = (xlocation,0,0)

    bpy.ops.object.select_all(action = 'DESELECT')
    bpy.context.scene.objects.active = bpy.data.objects['Cylinder']
    bpy.data.objects['Cylinder'].select = True
    bpy.data.objects['Cylinder.002'].select = True

    bpy.ops.object.join()

    xcount = xcount + 1

# join z-Cylinders -> Cylinder
bpy.context.scene.objects.active = bpy.data.objects['Cylinder']
bpy.data.objects['Cylinder'].select = True
bpy.data.objects['Cylinder.001'].select = True
bpy.ops.object.join()


#
# create x - Cylinders
#

# x = z
# y = y
# z = -x


newZ = x
newX = z

x = newX
z = newZ * (-1)


# create first z-Cylinder
xlocation = x / 2

bpy.ops.mesh.primitive_cylinder_add()
bpy.data.objects['Cylinder.001'].dimensions = (thickness,thickness,z)
bpy.data.objects['Cylinder.001'].location = (xlocation,0,0)


yrepeattimes = (y / distance)
ycount = 0

# create z-Cylinder-Row in y-Direction
while (ycount < yrepeattimes):
    ylocation = (distance * ycount) - y / 2

    bpy.ops.mesh.primitive_cylinder_add()
    bpy.data.objects['Cylinder.002'].dimensions = (thickness,thickness,z)
    bpy.data.objects['Cylinder.002'].location = (xlocation,ylocation,0)

    bpy.ops.object.select_all(action = 'DESELECT')

    bpy.context.scene.objects.active = bpy.data.objects['Cylinder.001']
    bpy.data.objects['Cylinder.001'].select = True
    bpy.data.objects['Cylinder.002'].select = True

    bpy.ops.object.join()

    ycount = ycount + 1

# create second z-Cylinder-Row in y-Direction
xlocation = x / 2 - distance
bpy.ops.object.select_all(action = 'DESELECT')

bpy.data.objects['Cylinder.001'].select = True
bpy.ops.object.duplicate()

bpy.data.objects['Cylinder.002'].location = (xlocation,0,0)

bpy.ops.object.select_all(action = 'DESELECT')

# duplicate second z-Cylinder-Row in y-Direction and join with first
xrepeattimes = (x / distance) - 2
xcount = 0

while (xcount < xrepeattimes):

    xlocation = (distance * xcount) - (x / 2) + distance
    bpy.ops.object.select_all(action = 'DESELECT')
    bpy.data.objects['Cylinder.002'].select = True
    bpy.ops.object.duplicate()
    bpy.data.objects['Cylinder.003'].location = (xlocation,0,0)

    bpy.ops.object.select_all(action = 'DESELECT')
    bpy.context.scene.objects.active = bpy.data.objects['Cylinder.001']
    bpy.data.objects['Cylinder.001'].select = True
    bpy.data.objects['Cylinder.003'].select = True

    bpy.ops.object.join()

    xcount = xcount + 1

# join
bpy.context.scene.objects.active = bpy.data.objects['Cylinder.001']
bpy.data.objects['Cylinder.001'].select = True
bpy.data.objects['Cylinder.002'].select = True
bpy.ops.object.join()

# rotate
bpy.ops.transform.rotate(value =1.57, axis=(0,2,0))
bpy.data.objects['Cylinder.001'].location = (0,0,-x/2)


# join z-Cylinders -> Cylinder
bpy.context.scene.objects.active = bpy.data.objects['Cylinder']
bpy.data.objects['Cylinder'].select = True
bpy.data.objects['Cylinder.001'].select = True
bpy.ops.object.join()

#
# create y - Cylinders
#

# reset x,y,z
(x,y,z) = bpy.data.objects[k_segmentname].dimensions

# x = x
# y = z
# z = -y

newY = z
newZ = -y

y = newY
z = newZ


# create first z-Cylinder
xlocation = x / 2

bpy.ops.mesh.primitive_cylinder_add()
bpy.data.objects['Cylinder.001'].dimensions = (thickness,thickness,z)
bpy.data.objects['Cylinder.001'].location = (xlocation,0,0)


yrepeattimes = (y / distance)
ycount = 0

# create z-Cylinder-Row in y-Direction
while (ycount < yrepeattimes):
    ylocation = (distance * ycount) - y / 2

    bpy.ops.mesh.primitive_cylinder_add()
    bpy.data.objects['Cylinder.002'].dimensions = (thickness,thickness,z)
    bpy.data.objects['Cylinder.002'].location = (xlocation,ylocation,0)

    bpy.ops.object.select_all(action = 'DESELECT')

    bpy.context.scene.objects.active = bpy.data.objects['Cylinder.001']
    bpy.data.objects['Cylinder.001'].select = True
    bpy.data.objects['Cylinder.002'].select = True

    bpy.ops.object.join()

    ycount = ycount + 1

# create second z-Cylinder-Row in y-Direction
xlocation = x / 2 - distance
bpy.ops.object.select_all(action = 'DESELECT')

bpy.data.objects['Cylinder.001'].select = True
bpy.ops.object.duplicate()

bpy.data.objects['Cylinder.002'].location = (xlocation,0,0)

bpy.ops.object.select_all(action = 'DESELECT')

# duplicate second z-Cylinder-Row in y-Direction and join with first
xrepeattimes = (x / distance) - 2
xcount = 0

while (xcount < xrepeattimes):

    xlocation = (distance * xcount) - (x / 2) + distance
    bpy.ops.object.select_all(action = 'DESELECT')
    bpy.data.objects['Cylinder.002'].select = True
    bpy.ops.object.duplicate()
    bpy.data.objects['Cylinder.003'].location = (xlocation,0,0)

    bpy.ops.object.select_all(action = 'DESELECT')
    bpy.context.scene.objects.active = bpy.data.objects['Cylinder.001']
    bpy.data.objects['Cylinder.001'].select = True
    bpy.data.objects['Cylinder.003'].select = True

    bpy.ops.object.join()

    xcount = xcount + 1

# join
bpy.context.scene.objects.active = bpy.data.objects['Cylinder.001']
bpy.data.objects['Cylinder.001'].select = True
bpy.data.objects['Cylinder.002'].select = True
bpy.ops.object.join()

# rotate
bpy.ops.transform.rotate(value =1.57, axis=(2,0,0))


# join z-Cylinders -> Cylinder
bpy.context.scene.objects.active = bpy.data.objects['Cylinder']
bpy.data.objects['Cylinder'].select = True
bpy.data.objects['Cylinder.001'].select = True
bpy.ops.object.join()


#
# Boolean-Modifier
#

bpy.context.scene.objects.active = bpy.data.objects['Cylinder']
bpy.ops.object.modifier_add(type='BOOLEAN')
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects['Suzanne']
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT'
bpy.ops.object.modifier_apply(apply_as='DATA')

Basically I’m creating Cylinders for all dimensions and rotate them till they fit. Afterwards I want to use the boolean-modifier to delete all over-standing parts of this grid.

before boolean modifier after modifier after modifier

Unfortunately I have two problems: First, as you can see on the pictures above, the Boolean Modifier deletes too much of the grid and I don’t know how to fix this. Second the whole process takes much time and maybe there is a better and smarter way to solve my problem?

$\endgroup$
  • $\begingroup$ This question relies on two parts being solved, independently. 1) Create a good topological Grid mesh 2) use a scripted Boolean to intersect Suzanne with the Grid. The latter (2) will almost always work correctly if the former (1) is a topologically sound grid. $\endgroup$ – zeffii Dec 23 '15 at 12:30
  • $\begingroup$ Please reduce your question to the most important sub question. ie: How to create a good Grid Mesh using Cylinders. $\endgroup$ – zeffii Dec 23 '15 at 12:34
2
$\begingroup$

This script will generate an inner grid using the SKIN modifier:

enter image description here

It generates an edge wireframe lattice from the object's bounding box, then adds a skin modifier to that wireframe to give it depth. Currently it uses a skin modifier's X and Y radius as 0.15.

You can set the number of divisions along the X, Y and Z axes.

Since it uses the skin modifier, it generates a nice manifold mesh. I commented out the last line that applies the skin and boolean modifiers, but you can uncomment it if you want the script to give you a final modifier free mesh.

import bpy, bmesh
import numpy as np
from mathutils import Vector

C = bpy.context
S = C.scene
O = S.objects

minmax = lambda l: ( min(l), max(l) )
ranges = lambda bb: [ minmax( [ getattr( b, axis ) for b in bb ] ) for axis in 'xyz' ]

def generate_regular_points( bb, count ):
    ''' Generate a 3D matrix of points with regular intervals.
        Creates the points within the provided bounding box (bb).
        The count is an XYZ vector specifying the num of points in each axis.
    '''
    bbRanges   = ranges( bb )
    dimensions = Vector( [ range[1] - range[0] for range in bbRanges ] )
    intervals  = Vector( [ d / (c-1) for d,c in zip( dimensions, count ) ] )

    c      = 0
    points = []
    edges  = []
    for i in range( int( count.x ) ):
        for j in range( int( count.y ) ):
            for k in range( int( count.z ) ):
                points.append([ 
                    bbRanges[0][0] + i * intervals.x, 
                    bbRanges[1][0] + j * intervals.y, 
                    bbRanges[2][0] + k * intervals.z 
                ])

                if k < count.z -1:
                    edges.append( ( c, c + 1) )
                if j < count.y -1:
                    edges.append( ( c, int( c + count.z ) ) )
                if i < count.x -1:
                    edges.append( ( c, int( c + count.y * count.z ) ) )

                c += 1

    return points, edges

def mesh_from_points( points, edgeList, name = "Points" ):
    bm = bmesh.new()
    for p in points: bm.verts.new( Vector(p) )

    bm.verts.ensure_lookup_table()

    for e in edgeList:
        bm.edges.new( [ bm.verts[i] for i in e ] )

    m = bpy.data.meshes.new( name )
    bm.to_mesh( m )

    o = bpy.data.objects.new( name, m )
    O.link( o )

    return o 

obj = O[ C.object.name ]
bb = [ Vector( bb[:] ) for bb in obj.bound_box ]

xRange, yRange, zRange = ranges( bb )

count = Vector(( 3, 4, 5 ))
points = generate_regular_points( bb, count )

points, edgeList = np.array( points )

o = mesh_from_points( points, edgeList, "FILL" )

# Add Skin Modifier
o.modifiers.new( "Skin", type = "SKIN" )

for i in range( len( o.data.vertices ) ):
    o.data.skin_vertices[0].data[ i ].radius = [ 0.15 for d in o.dimensions[:2] ]

mod        = o.modifiers.new( 'Boolean', 'BOOLEAN' )
mod.object = obj

# Apply modifiers
#o.data = o.to_mesh( S, True, 'RENDER' )

This solution is fairly simple but will be really slow to calculate with with large subdivisions (the Skin Modifier is notoriously slow).

$\endgroup$
1
$\begingroup$

I created a faster and cleaner code which does not produce a non manifold grid:

import bpy

scene = bpy.context.scene

### Settings ###
thickness = 0.03
distance = 0.5
k_segmentname = "Torus"


# get dimensions & location
objRec = bpy.data.objects[k_segmentname]
(x,y,z) = objRec.dimensions
(xl,yl,zl) = objRec.location

### Helper ###

def joinObjects(obs):
    bpy.ops.object.select_all( action = 'DESELECT' )
    for o in obs:
        o.select = True
    scene.objects.active = obs[0]
    bpy.ops.object.join()


def booleanMod(operation, objA, objB):

    boMoUn = objA.modifiers.new('boUn', 'BOOLEAN')
    boMoUn.object = objB

    if operation == 0:
        boMoUn.operation = 'DIFFERENCE'
    if operation == 1:
        boMoUn.operation = 'UNION'
    if operation == 2:
        boMoUn.operation = 'INTERSECT'

    scene.objects.active = objA
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier='boUn')


def createCube(name, size, strength, location):
    cube = bpy.ops.mesh.primitive_cube_add(radius=size/2, location=location)
    cube = bpy.context.active_object
    cube.name = name

    bpy.ops.object.mode_set(mode = 'OBJECT')
    bpy.ops.object.select_all( action = 'DESELECT' )

    cube_inner_x = bpy.ops.mesh.primitive_cube_add(radius=((size/2)-strength), location=location)
    cube_inner_x = bpy.context.active_object
    cube_inner_x.dimensions[0] = size +2
    booleanMod(0,cube,cube_inner_x)
    cube_inner_x.select = True
    bpy.ops.object.delete()

    cube_inner_y = bpy.ops.mesh.primitive_cube_add(radius=((size/2)-strength), location=location)
    cube_inner_y = bpy.context.active_object
    cube_inner_y.dimensions[1] = size +2
    booleanMod(0,cube,cube_inner_y)
    cube_inner_y.select = True
    bpy.ops.object.delete()

    cube_inner_z = bpy.ops.mesh.primitive_cube_add(radius=((size/2)-strength), location=location)
    cube_inner_z = bpy.context.active_object
    cube_inner_z.dimensions[2] = size +2
    booleanMod(0,cube,cube_inner_z)
    cube_inner_z.select = True
    bpy.ops.object.delete()

    return cube


sLoc_x = xl - (x/2)
sLoc_y = yl - (y/2)
sLoc_z = zl - (z/2)
cube = createCube('Grid', distance, (thickness/2), (sLoc_x,sLoc_y,sLoc_z))

arrayMod = cube.modifiers.new('arrayX', 'ARRAY')
arrayMod.use_merge_vertices = True
arrayMod.use_relative_offset = True
arrayMod.relative_offset_displace[0] = 1
arrayMod.relative_offset_displace[1] = 0
arrayMod.relative_offset_displace[2] = 0
arrayMod.count = x/distance + 2
scene.objects.active = cube
bpy.ops.object.modifier_apply(apply_as='DATA', modifier='arrayX')

arrayMod = cube.modifiers.new('arrayY', 'ARRAY')
arrayMod.use_merge_vertices = True
arrayMod.use_relative_offset = True
arrayMod.relative_offset_displace[0] = 0
arrayMod.relative_offset_displace[1] = 1
arrayMod.relative_offset_displace[2] = 0
arrayMod.count = y/distance + 2
scene.objects.active = cube
bpy.ops.object.modifier_apply(apply_as='DATA', modifier='arrayY')

arrayMod = cube.modifiers.new('arrayZ', 'ARRAY')
arrayMod.use_merge_vertices = True
arrayMod.use_relative_offset = True
arrayMod.relative_offset_displace[0] = 0
arrayMod.relative_offset_displace[1] = 0
arrayMod.relative_offset_displace[2] = 1
arrayMod.count = z/distance + 2
scene.objects.active = cube
bpy.ops.object.modifier_apply(apply_as='DATA', modifier='arrayZ')

booleanMod(2, cube, objRec)

unfortunately the boolean modifier won't work at a small diameter of the columns (here >= 0.02). Any idea how to fix that?

enter image description here enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.