Script: Add Tricylinder Object

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):
http://img517.imageshack.us/img517/6172/threecircles02ex6.th.png

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()

It is eazily done in MOI3D
http://img209.imageshack.us/img209/7319/3cylrz8.th.jpg

Also in Blender can be done by cutting a cylinder diagonally couple of times and then rotating two copies of the resulting mesh.

Anyway!
Very nice script! :cool:

Thanks Syziph, looks very nice in MOI3D. It’s Windows only, isn’t it? Would love to try it, I’m on Linux.

I also did the object manually a few times, similar to what you described, but I got tired of it soon. So I wanted to have it at the click of a button. :slight_smile:

I know it’s nothing special, but it was a nice way for me to learn a bit scripting and how to duplicate a part of a mesh with all faces intact in Python.

Well modeling this shape in Blender is not so complex :slight_smile:
It took me 2:30 min to record this video.
http://rapidshare.com/files/67452421/threecylinder_modeling.avi
(avi, XVID, 2,2 KB)
(If you’re not familliar with Blender hot keys - run the video on slow motion and observe the key input in the yellow box)

Your script is a great example for learning Python, thanks!