Skip to content

Instantly share code, notes, and snippets.

@megafaunasoft
Created March 10, 2013 19:57
Show Gist options
  • Save megafaunasoft/5130161 to your computer and use it in GitHub Desktop.
Save megafaunasoft/5130161 to your computer and use it in GitHub Desktop.
Blender cup with rings, Python script
import bpy, math
deg2rad = 2*math.pi/360.0
scn = bpy.context.scene
scn.cursor_location = (0,0,0)
OVERALL_SCALE = 1/8.0
cgap = 0.525 * OVERALL_SCALE
#--------------------------------------------------------------
# Delete everything for starting over
#
bpy.ops.object.select_by_type(type="EMPTY")
bpy.ops.object.delete()
bpy.ops.object.select_by_type(type="MESH")
bpy.ops.object.delete()
bpy.ops.object.select_by_type(type="FONT")
bpy.ops.object.delete()
bpy.ops.object.select_by_type(type="CURVE")
bpy.ops.object.delete()
# add a font to the list for use -- stays in the list...
bpy.ops.font.open(filepath="/Library/Fonts/Georgia.ttf")
#----------------------------------------------------------
# File curve_types.py
#----------------------------------------------------------
import bpy
from math import sin, pi
'''
def make_half_torus(majorr, minorx, minory, run, y_off):
"""DEFUNCT! Problems with inside vs outside so replaced
with a mesh i make by hand. Definitely solvable though.
Make half torus -- white plastic"""
cu = bpy.data.curves.new("htorus", 'CURVE')
ob = bpy.data.objects.new("htorus", cu)
ob.location = (0,0,0)
bpy.context.scene.objects.link(ob)
spline = cu.splines.new('POLY')
cu.dimensions = '3D'
x1 = majorr-minorx
x2 = majorr+minorx
y1 = y_off-minory
y2 = y_off+minory
coords = [(0,majorr-minorx,y_off-minory), (0,majorr+minorx,y_off-minory)]
STEPS = 100
for i in range(1,STEPS+1):
coords.append((0,
majorr+minorx + i*(float(run)/STEPS),
y_off-minory + ((2.0*i/STEPS)*minory)))
# finally cap it
coords.append((0,majorr-minorx+run,y_off+minory))
coords.append((0,majorr-minorx,y_off-minory))
spline.points.add(len(coords)-1)
for n in range(len(coords)):
spline.points[n].co = (coords[n][0], coords[n][1], coords[n][2], 1)
# Add empty object to rotate around
bpy.ops.object.add(type="EMPTY", location=(0,0,0),)
empty = bpy.context.object
scn.objects.active = ob
mod = ob.modifiers.new("mod_screw", "SCREW")
mod.object = empty
mod.angle = math.pi
mod.steps = 128
mod.render_steps = 64
scn.update()
return ob
'''
def make_alphabet(half, radius, extrude, y_off):
if half==1:
letters = "ABCDEFGHIJKLM"
TOTAL_DEGREES = 360
elif half==2:
letters = "NOPQRSTUVWXYZ"
TOTAL_DEGREES = 360
elif half==3:
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
TOTAL_DEGREES = 360
elif half==4:
letters = "ABCD"
TOTAL_DEGREES = 360
textobjs = {}
for i, ch in enumerate(letters):
# Add half a rotation
i = (i * (TOTAL_DEGREES / (len(letters)))) + (TOTAL_DEGREES / (2*(len(letters))))
theta = i*deg2rad
#----------------------------------------
# Text object
#
bpy.ops.object.text_add(location=(0,0,0))
textobjs[ch] = bpy.context.object
textobjs[ch].name = ch+"_mytext"
tcu = textobjs[ch].data
tcu.name = ch+"_data"
tcu.body = ch
tcu.font = bpy.data.fonts[len(bpy.data.fonts)-1]
tcu.size = 0.8
tcu.extrude = extrude
# ESSENTIAL FOR BOUNDING BOX TO WORK!!!
scn.update()
# Centering
bpy.ops.transform.translate( value=((textobjs[ch].bound_box[0][0] - textobjs[ch].bound_box[7][0])/2,
0,
(textobjs[ch].bound_box[0][2] - textobjs[ch].bound_box[7][2])/2))
#-------------------------------------------------
# Little circle
#
print(textobjs[ch].bound_box[0][0],textobjs[ch].bound_box[7][0])
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
scn.update()
bpy.ops.transform.rotate(axis=(1,0,0), value=math.pi/2 )
print(dir(bpy.ops.object))
# Converting font/text object to mesh is necessary
# for apply rotation to work!
# You must apply a rotation in order to do any new rotations
bpy.ops.object.convert(target='MESH', keep_original=False)
bpy.ops.object.transform_apply(rotation=True)
#------------------------------------------------
# Big circle
#
bpy.ops.transform.translate(value=((0,radius,y_off)))
# ESSENTIAL FOR BIG ROTATION TO WORK
scn.cursor_location = (0,0,0)
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
scn.update()
# bpy.ops.transform requires textobj[ch].select=True
#bpy.ops.transform.rotate(axis=(0,0,1),value=[theta])
textobjs[ch].rotation_euler.rotate_axis("Z",theta)
# I didn't know how to treat this group as one object
#alphabet_group = bpy.data.groups.new("alphabet_group")
#for textobj in textobjs.values():
# alphabet_group.objects.link(textobj)
return textobjs
#-------------------------------------------------------------------------
# Cup
#
def make_cup(base_w, run, indent):
"""Make the cup -- black ceramic material"""
cu = bpy.data.curves.new("cup_curve", 'CURVE')
ob = bpy.data.objects.new("cup_object", cu)
ob.location = (0,0,0)
ob.show_name = False # text on the blender window
# Nothing shows up without linking it to the scene -- why?
bpy.context.scene.objects.link(ob)
# Create spline
spline = cu.splines.new('POLY')
cu.dimensions = '3D'
base_h = 3*OVERALL_SCALE #0.375 == 3mm
lip = 0.6
# http://www.shapeways.com/design-rules/ceramics
round = 2*OVERALL_SCALE
coords = [(0,0,0), (0,base_w-round,0), (0,base_w+round*run,round), (0,base_w+1*run,1),
(0,base_w+1*run-indent,1),(0,base_w+2*run-indent,2), (0,base_w+2*run,2),(0,base_w+3*run,3),
(0,base_w+3*run-indent,3),(0,base_w+4*run-indent,4), (0,base_w+4*run,4),(0,base_w+5*run,5),
(0,base_w+5*run-indent,5),(0,base_w+6*run-indent,6), (0,base_w+6*run,6),(0,base_w+6*run,7),
# turn at the top -- including rounding
(0,base_w+6*run-.09,7+round*.85),(0,base_w+6*run-.18,7+round),(0,base_w+6*run-.27,7+round*.85),
(0,base_w+7*run-lip,7),
# back down the other side -- including rounding
(0,base_w-lip + run*(base_h+.3),base_h+.3), (0,base_w-lip-.3 + run*(base_h),base_h),
(0,0,base_h)
]
spline.points.add(len(coords)-1)
for n in range(len(coords)):
spline.points[n].co = (coords[n][0], coords[n][1], coords[n][2], 1)
# Add empty object to rotate around
bpy.ops.object.add(type="EMPTY", location=(0,0,0),)
empty = bpy.context.object
# Run screw modifier on ob
scn.objects.active = ob
mod = ob.modifiers.new("mod_screw", "SCREW")
mod.object = empty
mod.angle = 2*math.pi
mod.steps = 256
mod.render_steps = 256
return ob
#--------------------------------------------------------------------------
# Mesh
#
def rotation_matrix(axis,theta):
axdot = math.sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2])
axis = (axis[0]/axdot,
axis[1]/axdot,
axis[2]/axdot)
a = math.cos(theta/2)
b = -axis[0]*math.sin(theta/2)
c = -axis[1]*math.sin(theta/2)
d = -axis[2]*math.sin(theta/2)
return [[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
[2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
[2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]]
def make_mesh(name, DEGREES, majorr, minorx, minory, run, y_off):
"""Make a band
"""
verts = []
edges = []
faces = []
nrot = 64 #26
axis = (0,0,1)
# Only use the minimum number of vertices necessary
# Otherwise, problems with inside and outside the mesh
spline = ((0, majorr-minorx+cgap, y_off-minory+cgap),
(0, majorr+minorx, y_off-minory+cgap),
(0, majorr+minorx+run, y_off+minory-cgap),
(0, majorr-minorx+cgap+run, y_off+minory-cgap),)
#(0, majorr-minorx, y_off-minory))
rotation_offset = (DEGREES/(2*nrot)) * deg2rad
if DEGREES < 360:
real_nrot = nrot + 1
else:
real_nrot = nrot
for n in range(real_nrot):
angle = -(rotation_offset) + (n * (DEGREES/(nrot)))
theta = angle * deg2rad
rmat = rotation_matrix(axis, theta)
for point in spline:
new_point = (rmat[0][0]*point[0] + rmat[0][1]*point[1] + rmat[0][2]*point[2],
rmat[1][0]*point[0] + rmat[1][1]*point[1] + rmat[1][2]*point[2],
rmat[2][0]*point[0] + rmat[2][1]*point[1] + rmat[2][2]*point[2])
verts.append(new_point)
if DEGREES < 360 and n == 0:
faces.append((0,1,2,3))
my_offset = n*len(spline)
if DEGREES == 360:
next_offset = ((n+1)*len(spline)) % (nrot*len(spline))
else:
next_offset = ((n+1)*len(spline))
if n == real_nrot-1:
continue
for i in range(len(spline)):
my_i = i + my_offset
my_i2 = (i+1)%len(spline) + my_offset
next_i = i + next_offset
next_i2 = (i+1)%len(spline) + next_offset
faces.append((my_i, my_i2, next_i2, next_i))
if DEGREES < 360:
faces.append((len(verts)-1,len(verts)-2,len(verts)-3,len(verts)-4))
me = bpy.data.meshes.new(name+"Mesh")
ob = bpy.data.objects.new(name, me)
ob.location = (0,0,0)
ob.show_name = False
# Link object to scene
bpy.context.scene.objects.link(ob)
# Create mesh from given verts, edges, faces. Either edges or
# faces should be [], or you ask for problems
me.from_pydata(verts, edges, faces)
# Update mesh with new data
me.update(calc_edges=True)
return ob
#----------------------------------------------------------------------------
# Diff
#
def do_diff(tor, textobjs, gap=None):
for textobj in textobjs.values():
scn.objects.active = tor
mod = tor.modifiers.new("mod_diff", "BOOLEAN")
mod.operation = "DIFFERENCE"
mod.object = textobj
bpy.ops.object.modifier_apply(apply_as="DATA", modifier="mod_diff")
scn.objects.unlink(textobj)
# This should also work
#textobj.select = True
#bpy.ops.object.delete()
if gap:
scn.objects.active = tor
mod = tor.modifiers.new("mod_diff", "BOOLEAN")
mod.operation = "DIFFERENCE"
mod.object = gap
bpy.ops.object.modifier_apply(apply_as="DATA", modifier="mod_diff")
def make_gap():
me = bpy.data.meshes.new("gapMesh")
ob = bpy.data.objects.new("AAA_gap", me)
ob.location = (0,0,0)
# It's necessary to link object to scene
bpy.context.scene.objects.link(ob)
#-----------------------------------------
# 0.5mm wide gap -- hopefully avoid fusing
#
Xh = 0.25 * OVERALL_SCALE
Y = -4
Z = 6
verts = ((-Xh,Y,0),(Xh,Y,0),(Xh,0,0),(-Xh,0,0),
(-Xh,0,Z),(Xh,0,Z),(Xh,Y,Z),(-Xh,Y,Z))
faces = ((0,3,2,1),
(7,6,5,4),
(0,1,6,7),(2,3,4,5),
(1,2,5,6),(7,4,3,0))
me.from_pydata(verts, [], faces)
# Update mesh with new data
me.update(calc_edges=True)
return ob
if __name__ == "__main__":
# 8.6 units wide, 7 units high ->
# An espresso cup should be 2.25" by 2.25" = 57mm
# That would be a scale of 8.14
PRINTING = False
DRAW_TEXT = True
base_w = 2.5
run = 0.2
indent = 0.3
minorx = indent/2
minory = 0.5
cup = make_cup(base_w, run, indent)
#--------------------------------------------------------------------------------
# Gap maker -- has to be in mm
# Cube is 2x2x2 so the gap = 0.5mm
#
gap = make_gap()
#--------------------------------------------------------------------------------
# Bands
#
y_off = 1.5
majorr = base_w + 1*run - indent/2
t1 = make_mesh(name="one", DEGREES=360,
majorr=majorr, minorx=minorx, minory=minory,
run=run, y_off=y_off)
textr = majorr+indent
texte = indent*.75
texty = y_off - 0.25
if DRAW_TEXT:
textobjs1 = make_alphabet(half=3, radius=-textr, extrude=texte, y_off=texty)
else:
textobjs1 = {}
do_diff(t1, textobjs1, gap=gap)
# FINAL MOVE FOR PRINTING
if PRINTING:
t1.location = (6,0,-1.5+minory-cgap)
y_off = 3.5
majorr = base_w + 3*run - indent/2
t2 = make_mesh(name="two", DEGREES=360,
majorr=majorr, minorx=minorx, minory=minory,
run=run, y_off=y_off)
textr = majorr+indent
texte = indent*.7
texty = y_off - 0.25
if DRAW_TEXT:
textobjs2 = make_alphabet(half=3, radius=-textr, extrude=texte, y_off=texty)
else:
textobjs2 = {}
do_diff(t2, textobjs2, gap=gap)
# FINAL MOVE FOR PRINTING
if PRINTING:
t2.location = (6,7,-3.5+minory-cgap)
y_off = 5.5
majorr = base_w + 5*run - indent/2
t3 = make_mesh(name="three", DEGREES=360,
majorr=majorr, minorx=minorx, minory=minory,
run=run, y_off=y_off)
textr = majorr+indent
texte = indent*.65
texty = y_off - 0.25
if DRAW_TEXT:
textobjs3 = make_alphabet(half=3, radius=-textr, extrude=texte, y_off=texty)
else:
textobjs3 = {}
do_diff(t3, textobjs3, gap=gap)
# FINAL MOVE FOR PRINTING
if PRINTING:
t3.location = (6,-7,-5.5+minory-cgap)
# delete gap-maker
scn.objects.unlink(gap)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment