Clay 3D Printing Grasshopper Plugin Extension -- CoilCAM + FFT
by jennihutson in Workshop > 3D Printing
399 Views, 3 Favorites, 0 Comments
Clay 3D Printing Grasshopper Plugin Extension -- CoilCAM + FFT
I have been using CoilCAM, a visual scripting plug-in for Grasshopper, to create 3D printed clay vessels. I decided to extend the CoilCAM plugin to better align with my preferred way of designing, which includes manually drawing forms before using computational design software. I added two objects to the CoilCAM plugin which work with the larger ecosystem, one which interprets drawn curves in Rhino, and one which allows audio data to be brought into CoilCAM to enable data-driven design. In the end, I printed several vessels using my new CoilCAM extension and experimented with one method for using audio data for clay surface decoration.
My additions are now available to be used as part of CoilCAM and more info can be found in the documentation.
Supplies
Rhino + Grasshopper
CoilCAM Plug-in
Clay 3D printer
Python 2.7 instance on machine with NumPy and SciPy libraries installed
Grasshopper Setup
I first setup my development environment by forking the CoilCAM repo and adding the component objects to Grasshopper. CoilCAM takes inspiration from audio synthesis techniques which build and analyze sounds using common wave functions, I thought I could leverage this paradigm and use the Fourier transform to bring arbitrary waveforms into CoilCAM. In order to do so, I wanted to use the Python libraries NumPy and SciPy. Without modification, Python components in Grasshopper don't allow for arbitrary libraries to be imported. In order to use a Python instance on my machine instead, I used the GH Python Remote component by Digital Structures. This component has to be present in the Grasshopper file that uses these extra libraries, so I made a user object in the Setup category of CoilCAM that has the list of necessary libraries pre-populated. Users of CoilCAM can input the path to their Python instance to the new object.
Figuring Out How FFTs Work
The Fourier Transform is an algorithm which takes an arbitrary function and returns the frequencies present in the function, which can be represented as sine waves. The Fast Fourier Transform is a less complex implementation of the Fourier transform, for which there exists pre-written functions in libraries in Python and other programming languages.I used the NumPy library's FFT function.
Taking the FFT of an audio file returns an array of real and imaginary numbers which can be mapped to the functions of sine waves, which sum together to construct a waveform. This can be done automatically using the Inverse Fast Fourier Transform. However, I needed to compute the IFFT for each point in the nbPoints variable which is used for every CoilCAM object. I reconstructed the sum of sin waves without using the IFFT function at each step we needed, dictated by the nbPoints variable.
The math was as follows:
I used NumPy's fft.freq function to get all the component frequencies present in my waveform.
I used NumPy's fft function to get the amplitude array for the signal, i.e.
magnitudes = abs(np.fft.fft(wavedate))/(sampleNumber/2)
which gave me the magnitude of each signal.
Then for each point in my nbPoints, I could get the sin wave functions with the correct frequencies multiplied by the magnitude at each point:
sinArr = np.sin(frequencies * 2 * math.pi) * magnitudes
This has to be scaled for each point in the total number of points, so the first point is at the beginning of the signal and the last one at the end, even if the number of points in nbPoints is smaller than the original number of samples:
sinArr = sinArr * pointI * (sampleNumber/nbPoints)
Finally, each sin array can be summed for each point to add up to where the wave should be at the point along the total length:
np.sum(sinArr)
Developing the CurveInterp Object
I figured out the math above working with input curves from Rhino, starting with a simple sin wave. This is what I used for the AudioInterp object. However, it became apparent that for drawn curves in Rhino that it was more efficient to use Rhino geometry functions rather than NumPy functions. I used rs.DivideCurve to divide my curve into points and then interpolated between them at the appropriate number of points from nbPoints.
I also translated each Rhino curve input to the origin so it could be drawn anywhere, and scaled the curve to the desired nbPoints variable, as well as added a flip input parameter to my new object. The flip parameter, when set to true, flips the drawn curve around the vertical axis so the user can use their drawn curve in either direction regardless of orientation.
Developing the AudioInterp Object
For both objects, I needed a method to sample a curve into points. For a curve in Rhino, I used the rs.DivideCurve method. For an audio file, I used the scipy scipy.io.wavfile.read method.
It was easiest to have scipy resample my wav file to the number of points (nbPoints) variable which is sent to every CoilCAM object with the scipy.signal.resample method.
I also introduced an amplitude parameter as a multiplication factor on the output, as it is otherwise normalized between -1 to 1.
I started by using an audio file of a sin wave (A440) to debug my object, and used the technique outlined in the FFT step to analyze it. After, I tested with other wav files.
I added both objects to the CoilCAM plugin, and they are available to be used now.
Selecting and Editing Audio Clips
Now that I had completed software development, I could make clay works with my new objects! I wanted to create a series of vessels which used audio that was personally significant to me. For this, I used audio from voicemails my family had left me over the years, saying "I love you" in their own ways.
The iPhone has a built in feature to save voicemails as audio files, which I used, and then opened each audio file in my digital audio workstation of choice, Reaper. I then trimmed each voicemail down to just a few words, as I knew the shorter the audio, the more accurate my limited sampling would be (sample number dictated by the nbPoints variable).
I then exported the trimmed audio as a wav file to my computer.
Designing Clay Vessels
See a sped up (8x) video which is reflective of my design method using Rhino + Grasshopper: https://drive.google.com/file/d/1sgTx3p5IjHL83HBczgCcyA9dyYqSkxtF/view?usp=share_link
For each vessel, I drew the curvature I wanted in Rhino and fed the curve as input to the new CurveInterp object. I then connected the output to the scaleShapingParameter input on the ToolPath Unit Generator CoilCAM object to scale the radius of each horizontal layer of the pot. I tested and refined the shape in Rhino, drawing new curves and editing until I was satisfied.
I connected an audio file as input to the new AudioInterp object and connected the output to the rotateShapingParameter input on the ToolPath Unit Generator CoilCAM object. Since the toolpath of a 3D printer is a polygon, if the number of points in each layer is small enough, even a circular design will have sharper edges. If layers are rotated on top of one another and no longer perfectly align, we can get some overhang of each layer which can create an interesting texture on the surface of the pot. I changed the amount of rotation by the data in the audio file, which led to higher volume sections having more rotation, while quieter sections had less rotation and less texture.
While I connected a CurveInterp object to scale and AudioInterp to rotation in each of my designs, they can be connected to any parameters and chained together with any other CoilCAM objects.
Clay 3D Printing: Experimenting With Surface Decoration
All vessels were made with the curveInterp object dictating the shape of the vessel (scale) and the audioInterp object dictating the rotation of the vessel layers in some capacity.
I see this process as a first experimentation of what these objects can produce, as both curveInterp and audioInterp can send data to any Grasshopper object which accepts values, and don’t need to be limited to scale/rotation.
The first vessel (first image) has just a rotation applied from audio data, which resulted in very subtle surface decoration.
For the next two, I aimed to make the surface decorations more pronounced by adding sine waves and square waves to the scale of the vessels. I liked the effects this gave, but the vessels tended to collapse because the layers didn’t overlap sufficiently.
Clay 3D Printing: Final Print
Finally, working at a smaller scale, using the hairdryer as the piece printed, and decreasing the amplitude at which the layers undulated, I was able to get a vessel with surface decoration dictated by an audio file, with the profile coming from a curve draw in Rhino. All three pieces are made from different voicemails my family have left me over the years.
I hope to explore more ways to use these objects with CoilCAM in the future, especially investigating other methods of surface decoration. I would also like to bring other forms of media into CoilCAM, like CSV files and video.