In this assignment you will write Python code to generate an STL file
for a rubber duck, which you will then 3D print. The duck is
described using a set of nine parametric equations for ellipsoids that
generate a beak, eyes, head, neck, body, wings, and tail. Your code
will create a triangular mesh to approximate the surface of each
ellipsoid.
If we were to render all nine meshes as surfaces, we would have
something that looks like a duck, but it would not be a valid
description of a 3D object because the ellipsoid meshes overlap each
other rather than being knit together to form a single continuous
surface. In practice, most slicing programs can deal with this
problem, so you could get away with such an ugly hack, but we're going
to do better: we're going to use the CSG (Constructive Solid Geometry)
package to unify the meshes.
The 2D Case: Approximating An Ellipse
Let's start with a simpler 2D case of an ellipse centered on the
origin whose major and minor axes are aligned with the coordinate
axes. Such an ellipse can be described by two parametric equations
for the x and y coordinates, where the parameter θ
varies from 0 to 2π:
x(θ) = A cos(θ) y(θ) = B sin(θ)
The constants A and B determine the shape of the ellipse: they are the
lengths of the semi-major and semi-minor axes. If A is larger than B
then A is the semi-major axis, otherwise A is the semi-minor axis. If
A equals B, then the shape is a circle and A is the radius. If we
want to move the ellipse off of the origin we can add in offset terms
x0 and y0:
x(θ) = x0 + A cos(θ) y(θ) = y0 + B sin(θ)
If we want to tilt the ellipse so its major and minor axes are not
aligned with the coordinate axes, we could introduce additional terms
in the equations, but this won't be necessary for our purposes.
To draw the ellipse, we use the equations to generate a set of points
(xi,yi) by stepping θ from 0
up to 2π. We can then draw lines to connect adjacent points. Here
is an ellipse defined by A=2 and B=1:
Since our goal is to generate a triangular mesh, we will instead
use the points to generate a set of triangles. Each triangle will
have one point at the center of the ellipse; the other two points will
approximate the edge:
By using a smaller step size dθ we can generate more points and
get a finer approximation to the true curve:
Here is sample
code ellipse_example.py to generate
the ellipse and write out the STL
file ellipse.stl. This code relies on
the stlwrite package. Download that file
and put it in the same folder as ellipse_example.py
After writing out the triangles as an STL file we can examine it in
MeshLab. Play with the mode buttons at the top of the MeshLab window
to view the triangulation with or without shading.
The 3D Case
The higher-dimensional generalization of an ellipse is called an
ellipsoid. In 3D an ellipsoid can resemble a football. There are
three parameters governing the lengths of the axes, which we'll denote
A, B, and C. We'll be using spherical coordinates for our parametric
equations, so instead of a single angle θ there are two angles,
θ and φ. θ ranges from 0 to 2π as before, while
φ ranges from -π/2 to +π/2. You can think of these
parameters as longitude and lattitude, with φ=+π/2 being the
north pole and φ=-π/2 being the south pole.
The 3D parametric equations for an axis-aligned ellipsoid are:
x(θ, φ) = x0 + A cos(θ) cos(φ) y(θ, φ) = y0 + B sin(θ) cos(φ) z(φ) = z0 + C sin(φ)
For any given φ value, the steps of θ trace out an ellipse
(a parallel of lattitude) parallel to the x-y plane. For the next
lattitude value, φ+dφ, there is another ellipse just below it.
If we pick two adjacent points on the first ellipse, and the
corresponding two adjacent points on the second ellipse, we'll have a
quadrilateral surface patch, as in the diagram above. Any
quadrilateral can be converted to two triangles by connecting two
diagonally opposite points.
The only exceptions are at the north and south poles, where
φ = ±π/2, so cos(φ) is zero, so x =
x0, y = y0, z =
z0 ± C, and the ellipse becomes a point. In
these two special cases our surface patches connecting level φ
with level φ+dφ should be triangles instead of
quadrilaterals.
Given this explanation, write a Python function get_ellipsoid that
takes parameters A, B, C, x0, y0,
and z0 as input and returns a list of triangles.
You will need to use a nested for loop to iterate over values of
θ and φ.
To get you started, we've supplied a
file duck.py. It requires the csg package.
Download the file csg.zip and extract the
contents. Make sure that the csg folder is in the same location as
your duck.py file. Also place the
file stlwrite.py in that location.
Test your function by writing out the triangles for one ellipsoid as
an STL file and viewing the file in MeshLab.
Note: the order of vertices should be counter-clockwise to indicate
which side of the patch is the exterior surface of the object. If you
get this order wrong, Meshlab will render the patch as flat black
instead of shaded.
Common programming error: when testing two points to see if they're
identical, you cannot rely on p1 == p2 due to floating
point roundoff error. Instead you need to do something
like abs(p1-p2) < 1e-5.
Describing the Duck
Here are the parameter values for the nine ellipsoids that describe the duck:
Part
A
B
C
x0
y0
z0
beak
0.4
1
0.2
0
-0.3 π
0.5 π
head
1
1.2
1
0
0
0.5 π
left eye
0.15
0.2
0.15
0.23 π
-0.15 π
0.62 π
right eye
0.15
0.2
0.15
-0.23 π
-0.15 π
0.62 π
neck
0.75
0.75
1
0
0.5
0.5
body
2
3
1.2
0
0.75 π
0
left wing
0.2
1.4
0.6
0.56 π
0.8 π
0
right wing
0.2
1.4
0.6
-0.56 π
0.8 π
0
tail
0.8
1.4
0.2
0
1.4 π
0.1 π
You should write all nine ellipsoids into the same STL file. We've
provided a function write_simple_duck() that does this. The result
should look something like this:
Customize Your Duck
Modify the parameters a bit to customize your duck. You can change
the shape of existing body parts, and even add new body parts such as
a hat.
Examine your simple_duck.stl file in MeshLab to make sure that the
triangularization is correct. Look for holes in the mesh (missing
triangles), or triangles that are all black, indicating that the
surface normal is pointing in the wrong direction because the vertices
are listed in the wrong order. You will need to fix these problems
before proceeding to the next step.
Generating a Proper Duck Mesh
To generate a proper duck we must take the surface union of all the
ellipses to get a single continuous surface. We've provided a
function write_good_duck() to do that.
Placing Your Duck on a Pedestal
We're going to use SolidWorks to place the duck on a pedestal that you
design, with your initials cut into it.
Updated: In SolidWorks, go to File > Open, set the file type to be
"Mesh files", and select your STL file. Before clicking OK, click on
the Options button and make sure that "Import as" setting is Graphics
(the default), not Solid Body or Surface. Then click OK.
Create an extruded base as the pedestal. Since your duck has a curved
bottom, you should have the base extend up slightly into the bottom of
the duck so that the two bodies are connected and the duck is properly
supported. Cut your initials into the base; the letter height should
be 15 mm.
Scale your duck so that the max dimension (including the pedestal) is
roughly 3 inches. The exact scale is up to you. Remember to scale about
the origin, the same as for the molecule.
Save your SolidWorks part as a new STL file.
Print Your Duck
Print out your duck on the IDeATe 3D printer. Be sure to include
"15-294" and your Andrew id in the comments when you submit your
print. If Skylab isn't working for you, email your STL file to
help@ideate.cmu.edu and mention 15-294 and your Andrew id.
Note: if you get a message from Skylab saying "error getting build file",
load your STL file into MeshLab and selecting "analyze", "inspector", and "auto repair".
Let it correct any errors it finds. Then
save the STL file and send the corrected version to Skylab.
What to Hand In
Collect the following into a zip file and submit via AutoLab:
Your Python source code.
The STL file that is the output of your Python code.
Your SolidWorks part file and the STL you generated from that.
A screen capture showing a rendering of your generated STL file.
You can use Meshlab for the rendering, or you can use some other
program that displays STL files.
Also, once we send you your part, post a photograph of your 3D printed
customized duck to Piazza.
Dave Touretzky
Last modified: Sat Feb 13 14:34:11 EST 2016