IcoskaiPentagon Art Project
There is a local art show called "Something Red" that I enter on occasion here. The show's primary theme is to create something that highlights the color red, given the time of the year. I also do a lot, and I mean a LOT, of java coding, so I thought that I could marry both and produce my art. And I wanted it to be unique, and possibly self generating. The project would be created on either my laser or cnc machines.
Supplies
I had a good supply of wood to use, so I selected some Purpleheart, Bloodwood, River Birch, Maple and Wenge to be my main carriers , along with thin dyed red, yellow and black to be used as my inlays. I also needed a specialized canvas to hold my amulets, so I purchased some canvas from a local craft store, and I intended to reuse some Oak wood I had laying around for the frame. The amulets would be attached to the canvas by yellow wood glue.
Creating the Idea
I created a program about three years ago that allowed me to create various shapes for use with my box making work. It was modular enough, so adding functionality just required me to write a new "tab" of code to load into the program. So I started out thinking that the project could produce rings of objects around a center object.. Pretty simple I thought, no so simple to implement. My focus was heads down writing what I thought would take me a day to complete.
Four Days Later
Yeah, one day passed, two days, three days.. and finally, on the fourth day, I had something I could use. I discovered that I was too slow to manually try all the permutations to see how it looked. So I put buttons, spinners and number fields to control what I thought were the important aspects of what I wanted to get done. So, I can control the Initial size of the center image, and the number of rings to build. I could use circles or squares, though I opted for circles for this project. Future adaptations might add polygons. I chose to add a randomized button to change the size of the circles, and I also added functionality to change the minimum and maximum size of the circles. After a few iterations of the update button, I think I achieved what I was looking for.
The Code
It took a lot of code to get the fits right. The program calculates the number of objects that can fit on each ring, and then adjusts the sizes so that they just about touch each other. I had programmed in a boundary size, so we can control exactly how much room we do have. After each run, besides the visual display, I get a summary of the rings:
Ring: 1 Sine: 0.564497885257563 Degrees: 360.0 Steps: 5 Angle: 72.0 Direction: false Adjacent: 63.26704818276728 Opposite: 36.26704818276728 Total Arc: 360.0 Ring: 2 Sine: 0.32189647558115386 Degrees: 360.0 Steps: 9 Angle: 40.0 Direction: false Adjacent: 161.41349421935212 Opposite: 54.879397853817544 Total Arc: 360.0 Conflict: 15 Ring: 3 Ring: 3 Sine: 0.20572390785582753 Degrees: 360.0 Steps: 15 Angle: 24.0 Direction: false Adjacent: 273.8649137264769 Opposite: 56.572021653307246 Total Arc: 360.0 Ring: 4 Sine: 0.024166017683055083 Degrees: 359.9999999999993 Steps: 129 Angle: 2.7906976744186047 Direction: false Adjacent: 339.6472546912791 Opposite: 8.21031931149497 Total Arc: 360.0 Ring: 5 Sine: 0.1164363188456519 Degrees: 360.00000000000006 Steps: 26 Angle: 13.846153846153847 Direction: false Adjacent: 394.0539907835076 Opposite: 46.196416780733514 Total Arc: 360.0 Ring: 6 Sine: 0.12549315446599302 Degrees: 360.0 Steps: 24 Angle: 15.0 Direction: false Adjacent: 504.0033818800391 Opposite: 63.75297431579805 Total Arc: 360.0 Ring: 7 Sine: 0.11676699500588567 Degrees: 360.00000000000006 Steps: 26 Angle: 13.846153846153847 Direction: false Adjacent: 643.4019322967755 Opposite: 75.6455761009383 Total Arc: 360.0
This run ended up to be a 26 sided one (check ring 7 ) , the one I chose to build was a 25 sided one. All the angles and position factors was done with sine cosine, with trig used for distance and separation. A partial copy of the code I built for that portion is as follows:
// Repeat the circles here for the number of rings selected for (int i = 0; i < rings; i++) { if (reverseCheckbox.isSelected()) { direction = !direction; } // Check if want random sizes if (this.randomSizeCheckbox.isSelected()) { double randomNumber = Math.random(); double minRandom = Double.valueOf(this.minRandomSize.getText()); double maxRandom = Double.valueOf(this.maxRandomSize.getText()); secondRadius = randomNumber * (maxRandom - minRandom); } else { secondRadius = startRadius + increments; } // This now moves the X axis to where we want to put the circles initialLength += secondRadius; // Now let's see how long the actual side is double adjacentSide = initialLength; double compensation = adjustment * (i + 1); adjacentSide += compensation; // * we increased the leg.. but we should add this onto the points double adjustedRadius = secondRadius += adjustment; // calculate the sign SOH Opposite / hypontenuse double sine = secondRadius / Math.sqrt((adjacentSide * adjacentSide) + (adjustedRadius * adjustedRadius)); double result = 2 * Math.toDegrees(Math.asin(sine)); double degrees = 0.0; int steps = (int) (360 / result); double steppers = 360.0 / steps; // Create a list for the circles we create LinkedList<QuantumShapeInterface> circles = new LinkedList(); LinkedList<Object> svgPath = new LinkedList(); Double originalSecondRadius = secondRadius; // Do the circle of circles here while (degrees + steppers < 361.0) { double radians = Math.toRadians(degrees); double newX = Math.sin(radians) * adjacentSide; double newY = Math.cos(radians) * adjacentSide; //circle = new QuantumCircle(newX + baseStartX, newY + baseStartY, secondRadius); double modifier = .75; if (this.radioCircles.isSelected()) { circle = new QuantumCircle(newX + baseStartX, newY + baseStartY, secondRadius); } else { circle = new QuantumSquare(newX + baseStartX, newY + baseStartY, secondRadius); modifier = .6; }
Now that we had the rings done, we needed to jazz this up.
I spent another day doing some quick coding to allow me to add images to each medallion. SVG images are something I never worked with before, so of course, I buckled down to do some imports of that format. I knew this could get carried away, so I only did a minimal port to get the basics done. I added a few buttons to load images, scales them, and only place an image if the medallions were of a certain minimal size. Partial code for this area:
if (svgReader != null) { if (minSize <= secondRadius) { double dieg = direction ? 90 : 270; svgReader.computeScale((secondRadius * 2.0) * modifier); svgReader.setRotation(dieg - degrees); svgReader.setTranslationX(newX + baseStartX, newY + baseStartY); svgReader.parseSvg(); svgReader.moveObjects(svgPath); } } circle.setRotation(degrees); if (degrees == 0.0) { circle.setStrokeColor(Color.CYAN); } else { circle.setStrokeColor(Color.BLUE); } circles.add(circle); if (degrees > 0.0) { if (!circle.valid((QuantumShapeInterface) circles.getFirst())) { degrees = 0; circles = new LinkedList(); svgPath = new LinkedList(); secondRadius--; System.out.println("Conflict: " + steps + " Ring: " + (i + 1)); continue; } }
So I also decided to rotate the image as if it was going around the center point, to bring the title of this work to life.
Generating the Cuts
The program I wrote generates the code to send to the actual program that will do the burns. In this case, LightBurn. When I sent it over, I saw right away that I would be having some issues. It was far too complicated to burn at once. I would have to isolate the layers, and then generate templates to help me place the amulets. Well, that part was fairly simple. I first had to scale the work so that it fit the criteria of being no bigger than 20" x 20". This was very easy to do in LightBurn.
Template Creation
I used LightBurn to drop all the butterflies, and separated each ring, in essence, this created the templates. I used cardboard then to burn each template. The completed templates are shown on my shop floor. Now, I can center each template, then put the medallions on the canvas with precision. But I still had to create the medallions. I would have to do a lot of manual manipulation in LightBurn to arrange all the medallions, and inlays, to be efficient on use of wood. Well, back to coding ....
Writing a Packing Algorithm
My source wood is not of stock sizes. I have bits and pieces, and I was trying to use the most wood with the least waste of the expensive materials. Now, packing algorithms are the source of many a college thesis, and few have successfully completed the actual work. But, mathematically, I chose to try with a few simple ideas. One, I will have a fixed size medallion or butterfly to cut. Two, I needed a bit of a separation between parts for the laser beam. So, I generated up another "tab" for my code, and added on fields to handle the size of my wood, the number of objects I need to burn, the size of the medallions for that ring, spacing. The code then generates best fit, and I transferred the results to LightBurn. I repeated this for all the rings. The code wasn't all that difficult. But very tedious. A small sample:
double multiplier = this.radioCircles.isSelected() ? 0.8 : 1.0; double adjustment = 0.0; for (int index = 0; index < pc.maxObjects; index++) { if (pc.targetX + pc.startRadius > bsX + pc.objectWidth - pc.edgeBuffer) { if (radioTight.isSelected() || whichPack) { direction = !direction; } if (direction && this.radioCircles.isSelected()) { pc.targetX = bsX + (2 * pc.startRadius) + pc.edgeBuffer; } else { pc.targetX = bsX + (1 * pc.startRadius) + pc.edgeBuffer; } pc.targetY += (2 * pc.startRadius) * multiplier; } if (pc.targetY + pc.startRadius >= (bsY + pc.objectHeight)) { break; } counts++; if (!whichPack) { if (this.radioCircles.isSelected()) { circle = new QuantumCircle(pc.targetX, pc.targetY, pc.startRadius - pc.buffer); } else { circle = new QuantumSquare(pc.targetX, pc.targetY, pc.startRadius - pc.buffer); } circle.setStrokeColor(Color.GREEN); this.getCanvasCallback().appendShape(circle); } else { // Put a rectangle around the object circle = new QuantumSquare(pc.targetX, pc.targetY, pc.startRadius); circle.setStrokeColor(Color.PURPLE); this.getCanvasCallback().appendShape(circle); adjustment = 2.0; } if (svgReader != null) { if (whichPack) { svgReader.setStrokeOverride(Color.BROWN); } if (minSize <= pc.startRadius) { if (pc.scale == 0.0) { svgReader.computeScale(((pc.startRadius - pc.buffer) * 2.0) * pc.imageScale); pc.scale = svgReader.getScale(); } svgReader.setRotation(0); svgReader.setTranslationX(pc.targetX, pc.targetY); svgReader.parseSvg(); svgReader.moveObjects(canvasCallback); } } pc.targetX += (2 * pc.startRadius) + adjustment; } return counts;
I could now burn and assemble all my medallions and associated inlays.
Assembly
Rather anticlimactic, but the hardest part of assembly was creating the canvas. I used the laser to cut out the icosikaipentagon, cut all the 25 Oak braces on my table saw. I only used an angle on one side of each piece. Makes it easier, and the fit would be better. I had the laser draw the lines for me where to lay the wood, so the assembly was fast. Let it dry overnight. The next morning, I sprayed the top with spray adhesive, and put the canvas on. I used a J roller to make sure there were no bubbles. I then trimmed the canvas and wrapped each of the 25 sides. After that was completed, the canvas was coated with white Gesso. Gesso turns the brown canvas a brilliant white, and gives a "tooth" that the glue can grab on to. I used the outer template to mount my center medallion, making that the "Master" pin that all my templates would register to. Using the rest of the templates, starting at the outer ring, I laid in all my medallions. A bit of cleaning up the back, adding the hanging wire .. and done.
Summary
I am quite pleased with how the project turned out, and already wrote some new "tabs" to handle Archimedes Spirals and Mouse Catching spirals. I do have plenty of videos that I may compile and post later.