You could import the STL file. With HyperFun you can create it by yourself. Here is a little tutorial how to use it:

First install

the polygonizer tool. Then create a text file dome.hf with this content:

Code:

my_model(x[3], a[1])
{
-- array declarations
array center[3];
array rotated[3];
-- constants
pi = 3.14159;
deg2rad = pi/180;
-- create a cylinder
center = [0, 0, 0];
cylinder1 = hfCylinderX(x, center, 2);
-- create a rotated coordinate system
rotated[1] = x[1];
rotated[2] = x[2];
rotated[3] = x[3];
angle = 32;
dummy = hfRotate3DZ(rotated, deg2rad * angle);
-- another cylinder, now with the rotated coordinates
cylinder2 = hfCylinderX(rotated, center, 2);
-- return constructive solid union of both cylinder
my_model = cylinder1 | cylinder2;
}

Then open the command prompt and call the hfp program like this:

Code:

hfp dome.hf -w 400 -b 10

and you'll see a window where you can rotate and zoom (left and right mouse buttons while moving) the 3D model, which looks like this:

The language is some kind of very restricted Basic, like Visual Basic 6 without returns and other limitations, e.g. you can't ignore return values, there are only floats and arrays of floats, assigning and initializing values is a pain if you know C++ or Python and the only control structures are loops and ifs, but at least this is sufficient for creating some nice models.

The concept of HyperFun is to call a function, my_model in the example, which is the default for the polygonizer tool, for all (x, y, z) points in the bounding box (specified by the "-b" parameter, which is interpreted as (-10,-10,-10)-(10,10,10), if you specify only "-b 10"). If the function returns a value >0, the point is inside the object, if it is 0, the point is on the surface and if it is < 0, the point is outside of the object. The x, y and z coordinates are provided with the x-array.

This is the same in 3D like I've implemented some time ago in 2D (see

this Lisp sample) and sometimes it is called functional geometry. This is unusual at the beginning, because you don't describe an object, but you write a function and this function is called from the framwork with lots of points to produce the geometry. You have to think backward, like with function composition in mathematics or matrix multiplication (as you see in the example: for a rotated cylinder you don't create a cylinder and then rotate it, but you rotate the world and then return the points of the cylinder for this rotated coordinate system), but it is a very powerful concept. Sometimes this is reminiscent of functional programming like used in Haskell.

The line "cylinder1 = hfCylinderX(x, center, 2);" uses a predefined function (see

this page for a reference of all available built-in functions), which returns a value <0, 0 or >0 for a given point. hfRotate3DZ rotates a vector about the z-axis. Finally the function returns the value of the union of both cylinders with the line "my_model = cylinder1 | cylinder2;".

But the cylinders are infinite long, so we need to intersect it with a cube. You can define your own functions, which can be called from the start function and with the a-parameter you can pass additional parameters. So it is possible to write a general purpose line function and draw a wireframe cube. For nice joints we add a sphere at the ends of the line. There are no functions with arbitrary arguments and no global variables, so it looks a bit obfuscates and redundant:

Code:

line(x[3], a[6])
{
-- array declarations
array p[3];
array rotated[3];
-- constants
pi = 3.14159;
deg2rad = pi/180;
diameter = 2;
radius = diameter / 2;
-- extract line start and end coordinates
xs = a[1];
ys = a[2];
zs = a[3];
xe = a[4];
ye = a[5];
ze = a[6];
-- extract current point
xc = x[1] - xs;
yc = x[2] - ys;
zc = x[3] - zs;
-- line length
length = sqrt((xe-xs)^2+(ye-ys)^2+(ze-zs)^2);
-- distance from origin
length0 = sqrt(xs^2+ys^2+zs^2);
-- the following code is based on an article by Stan Melax in Game Programming Gems,
-- see this book: http://books.google.de/books?id=hiBFUv_FT0wC&pg=PA214
-- first calculate a quarternion, which represents the rotation between v0 and v1
-- the target vector
v0x = xe - xs;
v0y = ye - ys;
v0z = ze - zs;
-- normalize target vector
v0x = v0x / length;
v0y = v0y / length;
v0z = v0z / length;
-- the normalized cylinder vector
v1x = 1;
v1y = 0;
v1z = 0;
-- cross product of v0 and v1
cx = v0y*v1z - v0z*v1y;
cy = v0z*v1x - v0x*v1z;
cz = v0x*v1y - v0y*v1x;
-- dot product of v0 and v1
d = v0x * v1x + v0y * v1y + v0z * v1z;
-- special case: if the dot product is near -1, v0 and v1 are pointing in opposite directions and we can simply inverse the x-axis
if (d < 1e-5 - 1) then
xc = -xc;
else
-- calculate quarternion
sc = sqrt((1+d) * 2);
qx = cx / sc;
qy = cy / sc;
qz = cz / sc;
qw = sc / 2;
-- rotate coordinate system with quaternion
rw = -qx*xc-qy*yc-qz*zc;
rx = qw*xc+qy*zc-qz*yc;
ry = qw*yc+qz*xc-qx*zc;
rz = qw*zc+qx*yc-qy*xc;
xc = -rw*qx+rx*qw-ry*qz+rz*qy;
yc = -rw*qy+ry*qw-rz*qx+rx*qz;
zc = -rw*qz+rz*qw-rx*qy+ry*qx;
endif;
-- initialize rotated vector, translated by start point
rotated[1] = xc;
rotated[2] = yc;
rotated[3] = zc;
-- create a cylinder
p = [0, 0, 0];
result = hfCylinderX(rotated, p, radius);
-- intersect with a cube
p[2] = -radius;
p[3] = -radius;
result = result & hfBlock(rotated, p, length, diameter, diameter);
-- add spheres at both line ends
p = [0, 0, 0];
result = result | hfSphere(rotated, p, radius);
p[1] = length;
result = result | hfSphere(rotated, p, radius);
-- return result
line = result;
}
my_model(x[3], a[1])
{
-- array declarations
array coords[6];
result = -1;
-- pyramid base
coords = [ -5, -5, 0, 5, -5, 0 ]; result = result | line(x, coords);
coords = [ 5, -5, 0, 5, 5, 0 ]; result = result | line(x, coords);
coords = [ 5, 5, 0, -5, 5, 0 ]; result = result | line(x, coords);
coords = [ -5, 5, 0, -5, -5, 0 ]; result = result | line(x, coords);
-- sides
coords = [ -5, -5, 0, 0, 0, 7 ]; result = result | line(x, coords);
coords = [ 5, -5, 0, 0, 0, 7 ]; result = result | line(x, coords);
coords = [ 5, 5, 0, 0, 0, 7 ]; result = result | line(x, coords);
coords = [ -5, 5, 0, 0, 0, 7 ]; result = result | line(x, coords);
my_model = result;
}

This needs more sampling points, so render it with the additonal "-g" parameter:

Code:

hfp dome.hf -w 400 -b 10 -g 80

The result:

Finally we can create the dome. The algorithm is based on

this article. But the code needs ages to compute the polygones and then it is still not very detailed. And the code looks very ugly in the HyperFun language. This is not the fault of functional programming, e.g.

in this example you can see a nice Haskell program written by me to draw some functional graphics.

Ok, back to more imperative languages. Now the same with Python in Blender. With a little framework I can write this:

Code:

# create platonic solids
solidRadius = 2
coords, faces = cubeData()
coords = scaleArrayToLength(coords, solidRadius)
coords = translateArray(coords, [ -5, 0, 0 ])
addMeshToBlender(coords, faces)
coords, faces = octahedronData()
coords = scaleArrayToLength(coords, solidRadius)
coords = translateArray(coords, [ 0, 0, 0 ])
addMeshToBlender(coords, faces)
coords, faces = dodecahedronData()
coords = scaleArrayToLength(coords, solidRadius)
coords = translateArray(coords, [ 5, 0, 0 ])
addMeshToBlender(coords, faces)
coords, faces = icosahedronData()
coords = scaleArrayToLength(coords, solidRadius)
coords = translateArray(coords, [ 0, 5, 0 ])
addMeshToBlender(coords, faces)
coords, faces = tetrahedronData()
coords = scaleArrayToLength(coords, solidRadius)
coords = translateArray(coords, [ 5, 5, 0 ])
addMeshToBlender(coords, faces)

and I'll get this objects:

The wireframe is just some cylinders and spheres for the joints:

The geodesic dome is an icosphere, where half of the sphere is deleted. This can be coded with my framework like this:

Code:

domeRadius = 7
domeSubdivisions = 2
cylinderRadius = 0.1
sphereSubdivisions = 2
steps = 50
coords, faces = createIcosphere(domeRadius, domeSubdivisions)
coords, faces = half(coords, faces, -domeRadius / 10)
createWireframeMesh(coords, faces, cylinderRadius, steps, sphereSubdivisions)

The result:

I didn't use classes and operator overloading, so it should be very easy to port it to any other 3D scripting language, e.g. a 3ds script.

Here is the full source code, including the general framework functions. As you can see, it is much easier to implement this in Python than with HyperFun. Note: The rendering works, but there are still some problems with normals and manifolds, when I try to upload it to Shapeways, I'll analyze it.