Hi everybody,

some days ago I had a chat with a family member about objects and their projections in lower dimensions. For example, a cylinder in 3D can look either like a circle or a rectangle in 2D when projected along the main axes. So we wondered if there was an object which 2D projections look like a circle along all three axes, but which isn’t a sphere. We came up with an intersection of three cylinders of equal radius along the main axes. Turns out such an object is called Tricylinder.

Intersecting three cylinders with the boolean function in Blender leaves the resulting mesh a bit messy, so I though I’ll try to generate such an object a bit cleaner with Python. This is my first script of this kind, I learned quite a bit, it 's a fun object, so I thought maybe I should share it.

It’s found in the “Add” menu of the scripts window. If you want to have it showing up in the Add Mesh menu, change Line 5 from “Group: ‘Add’” to “Group: ‘AddMesh’”. I didn’t put it in by default, because I don’t want to clutter people’s menu.

Here’s an image (rendered with Yaf(a)Ray, click to enlarge):

And here’s the script:

```
#!BPY
"""
Name: 'Tricylinder'
Blender: 245
Group: 'Add'
Tooltip: 'Add a Tricylinder object'
"""
__author__ = 'Sanne'
__version__ = '0.1'
__bpydoc__ = """\
A Tricylinder is a Steinmetz Solid, an intersection of three cylinders of equal radii intersecting at right angles.
"""
import Blender
from Blender import Scene, Object, Mesh
from math import cos, sin, radians
def makeSegment(me, SUBDIVISION, RADIUS):
"""Generate 1/24th segment of the tricylinder """
verts = []
faces = []
alpha = 45.0/(SUBDIVISION + 1.0)
v1 = 0
for n in range(SUBDIVISION + 1):
# not needed, keep for reference
#a1 = sin(n * radians(alpha))
#b1 = cos(n * radians(alpha))
a2 = sin((n + 1) * radians(alpha)) * RADIUS
b2 = cos((n + 1) * radians(alpha)) * RADIUS
if n > 0:
# not needed, keep for reference
#verts.append([a1, a1, b1])
#verts.append([a1, -a1, b1])
verts.append([a2, -a2, b2])
verts.append([a2, a2, b2])
v2 = v1 + 1
v3 = v1 + 2
v4 = v1 + 3
faces.append([v2, v1, v3, v4])
v1 += 2
else:
verts.append([0, 0, RADIUS] )
verts.append([a2, -a2, b2])
verts.append([a2, a2, b2])
v2 = v1 + 1
v3 = v1 + 2
faces.append([v1, v2, v3])
v1 += 1
me.verts.extend(verts)
me.faces.extend(faces)
def makeTop(scn, ob):
"""
Generate the top side (1/6th) of the tricylinder out of four segments
The newly generated tricylinder segment is the currently selected onject.
The for loop generates three copies of this segment, rotates and joins them,
thusly building the top of the tricylinder.
"""
oblist = []
for i in range(3):
Object.Duplicate(mesh=1)
tmpob = Object.GetSelected()[0]
tmpob.RotZ += radians(90)
oblist.append(tmpob)
# join temporary objects with the original segment
# and delete the temporary objects from the scene
ob.join(oblist)
for item in oblist:
scn.unlink(item)
ob.select(1)
def makeBody(scn, ob):
"""Generate the whole body of the tricylinder out of the top side"""
# rotate top 180 degrees around x
Object.Duplicate(mesh=1)
tmpob = Object.GetSelected()[0]
tmpob.RotX += radians(180)
ob.join([tmpob]) # newob1
scn.unlink(tmpob)
ob.select(1)
# rotate joined object 90 degrees around x
Object.Duplicate(mesh=1)
tmpob = Object.GetSelected()[0]
tmpob.RotX += radians(90)
# rotate last tmpob 90 degrees around Z
Object.Duplicate(mesh=1)
tmpob2 = Object.GetSelected()[0]
tmpob2.RotZ += radians(90)
# join all objects and clean up
ob.join([tmpob, tmpob2])
scn.unlink(tmpob)
scn.unlink(tmpob2)
ob.select(1)
def addTricylinder(SUBDIVISION, RADIUS):
"""Generate the whole mesh"""
limit = 0.001
scn = Scene.GetCurrent()
scn.objects.selected = []
me = Mesh.New("Tricylinder")
makeSegment(me, SUBDIVISION, RADIUS)
ob = scn.objects.new(me, 'Tricylinder')
makeTop(scn, ob)
me.remDoubles(limit)
makeBody(scn, ob)
me.remDoubles(limit)
Blender.Redraw()
def main():
"""GUI function"""
PREF_SUBDIVISION = Blender.Draw.Create(3)
PREF_RADIUS = Blender.Draw.Create(1.0)
# list for popup content
block = []
# inputs: title, button, min/max values, tooltip
block.append(("Subdivision: ", PREF_SUBDIVISION, 0, 50, "Subdivision of 1/24th tricylinder segment"))
block.append(("Radius: ", PREF_RADIUS, 0.01, 100, "Radius of intersecting cylinders"))
if not Blender.Draw.PupBlock("Add Tricylinder",block):
return
addTricylinder(PREF_SUBDIVISION.val, PREF_RADIUS.val)
# ---------- main program --------------------
main()
```