Description
This animation uses a 12-12-3 Archimedean tiling, with circles as vertices. Three distinct arrays are constructed and used to rotate symmetrical sub-groupings of the tiling's vertices (3,6,18) during the animation. Because I tend to convert many of my 2D animations into 3D ones and because Processing's PVector class is irritatingly mutable, I use the 3-space classes from Apache Commons library for vectors and rotations.
This animation is dedicated to Corina Marinescu for her impressive contributions on G+ and because she's a wonderful inimitable character:
+CorinaMarinescu
If you enjoy this animation, are interested in seeing others or would like to ask any questions, please visit me on G+:
+SeanWalker67
This animation is dedicated to Corina Marinescu for her impressive contributions on G+ and because she's a wonderful inimitable character:
+CorinaMarinescu
If you enjoy this animation, are interested in seeing others or would like to ask any questions, please visit me on G+:
+SeanWalker67
What is this all about?
Processing is an open source programming language.
As you can see here, it is embeddable on a website by using Processing.js.
Check out also the Google+ Page +Processing and their Google+ Community.
As you can see here, it is embeddable on a website by using Processing.js.
Check out also the Google+ Page +Processing and their Google+ Community.
Show Code Hide Code
import java.util.*; import processing.core.*; import processing.opengl.*; import org.apache.commons.math3.geometry.*; import org.apache.commons.math3.geometry.euclidean.threed.*; // ------------------------------------------------------------------------------------------------ // 12 circle based tiling pattern based Processing Sketch - dedicated to Corina Marinescu // Jan 1, 2016 // - Developed using Processing 3.0, but in Eclipse // - Two class files including this one and Circle.java // - Uses the Apache Math Common library for Vector3D and Rotation classes // ------------------------------------------------------------------------------------------------- public class MyProcessingSketch extends PApplet { final static double framesPerCycle = 160; Vector3D eye = new Vector3D(100, -500, 100); final int circUnitPatternNum = 3; final int rows = 12; final int cols = 28; float radius = 30; float radFract = 0.7f; int circNumPerTrio = 3; int circNumPerSix = 6; int threesPerEighteen = 6; final int eighteensRows = (rows-1)/2; final int eighteensCols = cols/3; // Component angles and vectors for the 12 point circles of the tiling pattern double compAngles[] = {(11d/12d)*TWO_PI, (10d/12d)*TWO_PI, (9d/12d)*TWO_PI, (8d/12d)*TWO_PI, (7d/12d)*TWO_PI, (6d/12d)*TWO_PI, (5d/12d)*TWO_PI, (4d/12d)*TWO_PI, (3d/12d)*TWO_PI, (2d/12d)*TWO_PI, (1d/12d)*TWO_PI, (0d/12d)*TWO_PI}; Vector3D vComps[] = { new Vector3D(Math.cos(compAngles[0]), Math.sin(compAngles[0]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[1]), Math.sin(compAngles[1]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[2]), Math.sin(compAngles[2]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[3]), Math.sin(compAngles[3]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[4]), Math.sin(compAngles[4]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[5]), Math.sin(compAngles[5]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[6]), Math.sin(compAngles[6]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[7]), Math.sin(compAngles[7]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[8]), Math.sin(compAngles[8]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[9]), Math.sin(compAngles[9]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[10]), Math.sin(compAngles[10]), 0).scalarMultiply(radius), new Vector3D(Math.cos(compAngles[11]), Math.sin(compAngles[11]), 0).scalarMultiply(radius)}; // arrays for grouping the subsets of the tiling that are rotated: trio circle is prime, the other two are derived with the 'group' methods Circle trioCircles[][][] = new Circle[cols][rows][circUnitPatternNum]; Circle eighteensCircles[][][][] = new Circle[cols/3][rows/2][threesPerEighteen][]; Circle sixCircles[][][] = new Circle[cols/2-2][rows-2][circNumPerSix]; // vectors offsets for generating the tiling pattern - based on 3 circle groups (trios) of the circle Vector3D vertUnitDisp[][] = new Vector3D[2][2]; Vector3D horUnitDisp[][] = new Vector3D[2][2]; public static void main(String args[]) { PApplet.main(new String[] { "--present", "MyProcessingSketch" }); } public void settings() { // In Processing 3.0 it's necessary to set the graphics mode in the settings() method outside of the Processing env size(800, 600, "processing.opengl.PGraphics2D"); } public void setup() { vertUnitDisp[0][0] = vComps[1].add(vComps[2]).add(vComps[3]); vertUnitDisp[0][1] = vComps[1].add(vComps[2]).add(vComps[3]).add(vComps[4]).add(vComps[3]).add(vComps[2]).add(vComps[1]).add(vComps[0]); vertUnitDisp[1][0] = vComps[1].add(vComps[2]).add(vComps[3]); vertUnitDisp[1][1] = vComps[1].add(vComps[2]).add(vComps[3]).add(vComps[4]).add(vComps[3]).add(vComps[2]).add(vComps[1]).add(vComps[0]); horUnitDisp[0][0] = vComps[11].add(vComps[10]); horUnitDisp[0][1] = vComps[11].add(vComps[10]).add(vComps[11]).add(vComps[0]); horUnitDisp[1][0] = vComps[11].add(vComps[0]); horUnitDisp[1][1] = vComps[11].add(vComps[0]).add(vComps[11]).add(vComps[10]); } public void draw() { // cycle (0,1] is the clock of the animation - 0 represents the start (or first frame) and 1 the final frame float cycle = (float)((frameCount-1)/framesPerCycle); // always draw a frame buffer - more flexible and retains alpha channel values unlike view PGraphics pg = createGraphics(width,height,P3D); pg.beginDraw(); // Make the center of the view window the center of pixel plane pg.translate(0.5f*width, 0.5f*height, 0); pg.background(0); // Construct the tiling pattern within the trioCircles[][][] circles array constructTiling(new Vector3D(0,0,0)); // Derive the array of sub-grouping with 18 circles groupEighteens(); // Derive the array of sub-groupings with 6 circles groupSixes(); // This animation sequence is devided into 6 segments float segs = 6; // Animation segmentation logic if(cycle <= 1f/segs) { // Rotate 6 circle sub-groups float cycle2 = smootherStep(0,1,map(cycle, 0f, 1f/segs, 0f, 1f)); rotateSixes(cycle2); } else if(cycle <= 2f/segs) { // Rotate 3 circle sub-groups float cycle2 = smootherStep(0,1,map(cycle, 1f/segs, 2f/segs, 0f, 1f)); rotateThrees(cycle2); } else if (cycle <= 3f/segs){ // Rotate 18 circle sub-groups float cycle2 = smootherStep(0,1,map(cycle, 2f/segs, 3f/segs, 0f, 1f)); rotateEighteens(cycle2); } else if (cycle <= 4f/segs){ // Rotate 3 circle sub-groups within rotating 18 circle sub-groups float cycle2 = smootherStep(0,1,map(cycle, 3f/segs, 4f/segs, 0, 1)); rotateThrees(-cycle2); rotateEighteens(cycle2); } else if (cycle <= 4.5f/segs){ // Half rotate 3 circle sub-groups float cycle2 = smootherStep(0,1,map(cycle, 4f/segs, 4.5f/segs, 0, 0.5f)); rotateThrees(cycle2); } else if (cycle <= 5.5f/segs){ // Half rotate 18 circle sub-groups with 3 cirle sub-groups fixed at half-rotation float cycle2 = smootherStep(0,1,map(cycle, 4.5f/segs, 5.5f/segs, 0, 0.5f)); rotateThrees(0.5f); rotateEighteens(cycle2); } else { // Remove half rotation of 3 circle sub-groups float cycle2 = smootherStep(0,1,map(cycle, 5.5f/segs, 6f/segs, 0.5f, 0)); rotateThrees(cycle2); } // Iterate through the circle tiling elements and draw them to the pixel buffer drawTiling(pg); pg.endDraw(); // Transfer the pixel buffer to the views pixel plane image(pg,0,0); println(frameCount); if (frameCount == framesPerCycle) exit(); String strFileName = "C:\\Users\\Sean\\Documents\\Processing\\workspace\\OctagonalCircles\\images\\" + nf(frameCount, 3, 1) + ".gif"; pg.save(strFileName); } void drawTiling(PGraphics pg) { for(int col=0; col<cols; col++) { for(int row=0; row<rows; row++) { for(int i=0; i<circNumPerTrio; i++) { trioCircles[col][row][i].draw(pg); } } } } void rotateThrees(float cycle) { for(int col=0; col<cols; col++) { for(int row=0; row<rows; row++) { rotateThree(trioCircles[col][row][0], trioCircles[col][row][1], trioCircles[col][row][2], cycle); } } } void groupSixes() { for(int col=0; col<cols/2-2; col++) { for(int row=0; row<rows-2; row++) { for(int i = 0; i < threesPerEighteen; i++) { int colOff = (row%2 == 0) ? 1 : 2; sixCircles[col][row][0] = trioCircles[col*2+colOff][row+1][0]; sixCircles[col][row][1] = trioCircles[col*2+colOff-1][row+1][2]; sixCircles[col][row][2] = trioCircles[col*2+colOff][row+1][1]; sixCircles[col][row][3] = trioCircles[col*2+colOff][row+2][1]; sixCircles[col][row][4] = trioCircles[col*2+colOff][row+1][2]; sixCircles[col][row][5] = trioCircles[col*2+colOff+1][row+1][0]; } } } } void rotateSixes(float cycle) { for(int col=0; col<cols/2-2; col++) { for(int row=0; row<rows-2; row++) { rotateSix(col, row, cycle); } } } void rotateSix(int col, int row, float cycle) { Vector3D xyNorm = new Vector3D(0,0,1); Rotation rot = new Rotation(xyNorm, cycle*(1d/3d)*TWO_PI); Vector3D sixCent = calculateSixCenter(col, row); for(int i = 0; i < circNumPerSix; i++) { sixCircles[col][row][i].pos = sixCircles[col][row][i].pos.subtract(sixCent); sixCircles[col][row][i].pos = rot.applyTo(sixCircles[col][row][i].pos); sixCircles[col][row][i].pos = sixCircles[col][row][i].pos.add(sixCent); } } Vector3D calculateSixCenter(int col, int row) { Vector3D cent = new Vector3D(0,0,0); for(int i = 0; i < circNumPerSix; i++) { cent = cent.add(sixCircles[col][row][i].pos); } return cent.scalarMultiply(1d/6d); } void groupEighteens() { for(int col=0; col<eighteensCols; col++) { for(int row=0; row<eighteensRows; row++) { for(int i = 0; i < threesPerEighteen; i++) { if(col*3 < cols && row*2+2 < rows) { if(col%2==0) { eighteensCircles[col][row][0] = trioCircles[col*3][row*2]; eighteensCircles[col][row][1] = trioCircles[col*3][row*2+1]; eighteensCircles[col][row][2] = trioCircles[col*3+1][row*2]; eighteensCircles[col][row][3] = trioCircles[col*3+1][row*2+1]; eighteensCircles[col][row][4] = trioCircles[col*3+2][row*2]; eighteensCircles[col][row][5] = trioCircles[col*3+2][row*2+1]; } else { eighteensCircles[col][row][0] = trioCircles[col*3][row*2+1]; eighteensCircles[col][row][1] = trioCircles[col*3][row*2+2]; eighteensCircles[col][row][2] = trioCircles[col*3+1][row*2+1]; eighteensCircles[col][row][3] = trioCircles[col*3+1][row*2+2]; eighteensCircles[col][row][4] = trioCircles[col*3+2][row*2+1]; eighteensCircles[col][row][5] = trioCircles[col*3+2][row*2+2]; } } } } } } void rotateEighteens(float cycle) { for(int col=0; col<eighteensCols; col++) { for(int row=0; row<eighteensRows; row++) { rotateEghteen(col, row, cycle); } } } void rotateEghteen(int col, int row, float cycle) { Vector3D xyNorm = new Vector3D(0,0,1); Rotation rot = new Rotation(xyNorm, cycle*(1d/3d)*TWO_PI); Vector3D eighteenCent = calculateEighteenCenter(col, row); for(int g = 0; g < threesPerEighteen; g++) { for(int t = 0; t < 3; t++) { eighteensCircles[col][row][g][t].pos = eighteensCircles[col][row][g][t].pos.subtract(eighteenCent); eighteensCircles[col][row][g][t].pos = rot.applyTo(eighteensCircles[col][row][g][t].pos); eighteensCircles[col][row][g][t].pos = eighteensCircles[col][row][g][t].pos.add(eighteenCent); } } } Vector3D calculateEighteenCenter(int col, int row) { Vector3D cent = new Vector3D(0,0,0); for(int g = 0; g < threesPerEighteen; g++) { for(int t = 0; t < 3; t++) { cent = cent.add(eighteensCircles[col][row][g][t].pos); } } return cent.scalarMultiply(1d/18d); } void rotateThree(Circle c0, Circle c1, Circle c2, float cycle) { Vector3D xyNorm = new Vector3D(0,0,1); Rotation rot = new Rotation(xyNorm, cycle*(1d/3d)*TWO_PI); Vector3D center = c0.pos.add(c1.pos).add(c2.pos).scalarMultiply(0.3333333); c0.pos = c0.pos.subtract(center); c1.pos = c1.pos.subtract(center); c2.pos = c2.pos.subtract(center); c0.pos = rot.applyTo(c0.pos); c1.pos = rot.applyTo(c1.pos); c2.pos = rot.applyTo(c2.pos); c0.pos = c0.pos.add(center); c1.pos = c1.pos.add(center); c2.pos = c2.pos.add(center); } void constructTiling(Vector3D center) { for(int col=0; col<cols; col++) { for(int row=0; row<rows; row++) { Vector3D vHDisp; Vector3D vVDisp; int colEvenVectMult = (col - col%2)/2 - cols/4; int colOddVectMult = col%2; int rowEvenVectMult = (row - row%2)/2 - rows/4; int rowOddVectMult = row%2; if(row%2 == 0) { vHDisp = horUnitDisp[0][1].scalarMultiply(colEvenVectMult).add((horUnitDisp[0][0].scalarMultiply(colOddVectMult))); vVDisp = vertUnitDisp[0][1].scalarMultiply(rowEvenVectMult).add((vertUnitDisp[0][0].scalarMultiply(rowOddVectMult))); } else { vHDisp = horUnitDisp[1][1].scalarMultiply(colEvenVectMult).add((horUnitDisp[1][0].scalarMultiply(colOddVectMult))); vVDisp = vertUnitDisp[1][1].scalarMultiply(rowEvenVectMult).add((vertUnitDisp[1][0].scalarMultiply(rowOddVectMult))); } boolean up = (col%2==0)? true : false; if((row%2==1)) up = !up; createPattern(trioCircles[col][row], center.add(vHDisp.add(vVDisp)), up); } } } void createPattern(Circle trio[], Vector3D pos, boolean up) { trio[0] = new Circle(this, pos, radius, radFract, 2f*(float)Math.PI, 0, color(200, 0, 0), 2, color(100)); if(up) { trio[1] = new Circle(this, trio[0].pos.add(vComps[1]), radius, radFract, 2f*(float)Math.PI, 0, color(200, 0, 0), 2, color(100)); trio[2] = new Circle(this, trio[1].pos.add(vComps[9]), radius, radFract, 2f*(float)Math.PI, 0, color(200, 0, 0), 2, color(100)); } else { trio[1] = new Circle(this, trio[0].pos.add(vComps[9]), radius, radFract, 2f*(float)Math.PI, 0, color(200, 0, 0), 2, color(100)); trio[2] = new Circle(this, trio[1].pos.add(vComps[1]), radius, radFract, 2f*(float)Math.PI, 0, color(200, 0, 0), 2, color(100)); } } // The smooth methods are S curve interpolation functions - generally I use them to produce more appealing accel/deccel motions float smoothStep(float edge0, float edge1, float x) { // Scale, and clamp x to 0..1 range x = map(x, edge0, edge1, 0.0f, 1.0f); // Evaluate polynomial return x*x*(3.0f - 2.0f*x); } float smootherStep(float edge0, float edge1, float x) { // Scale, and clamp x to 0..1 range x = map(x, edge0, edge1, 0.0f, 1.0f); // Evaluate polynomial return x*x*x*(x*(x*6 - 15) + 10); } } /////////////////////////////////////////////////////////////////////////// import processing.core.*; import processing.opengl.*; import org.apache.commons.math3.geometry.*; import org.apache.commons.math3.geometry.euclidean.threed.*; public class Circle { PApplet pa; Vector3D pos; float radius; float radFract; float angle; float angleOpen; int fillCol; float edgeWidth; int edgeCol; Circle(PApplet pa, Vector3D pos, float radius, float radFract, float angle, float angleOpen, int fillCol, float edgeWidth, int edgeCol) { this.pa = pa; this.pos = pos; this.radius = radius; this.radFract = radFract; this.angle = angle; this.angleOpen = angleOpen; this.fillCol = fillCol; this.edgeWidth = edgeWidth; this.edgeCol = edgeCol; } void draw(PGraphics pg) { pg.fill(fillCol); pg.strokeWeight(edgeWidth); pg.stroke(edgeCol); pg.arc((float)pos.getX(),(float)pos.getY(), radius*radFract, radius*radFract, 0, 2f*(float)Math.PI); } }