/**
* cFigure
* a collection of cShapes
* Square Crow, June 2010
*/
/**
LOG
===
TO DO
=====
*/
class cFigure{
float cenX; float cenY; // center of the figure
float bearingAbs; // facing direction in rad, 0 == UP
cShape[] pieces; // shapes in the collection
float[][] template; // [vertexNumber][{dX, dY, radius, angle}]
float[][] vertices; // last stored position
float[] bearingRel;
int index = 0; // for manual init of points
cFigure(int numShapes, float iX, float iY){
// contructor for manual init of shapes
template = new float[numShapes][4];
vertices = new float[numShapes][2];
pieces = new cShape[numShapes];
bearingRel = new float[numShapes];
cenX = iX; cenY = iY;
bearingAbs = 0;
}
void add_cShape(float dX, float dY, cShape cshpRef, float relBearing){
if(index < template.length){
template[index][0] = dX; template[index][1] = dY;
// relative pos expressed as a vector
template[index][2] = sqrt(pow(dX,2) + pow(dY,2)); // calc radius
pieces[index] = cshpRef;
bearingRel[index] = relBearing;
float quadrant_offset = 0;
float raw = atan(-dX/dY);
if(dX > 0 && dY > 0){quadrant_offset = -PI;} else
if(dX < 0 && dY > 0){quadrant_offset = PI;}
template[index][3] = raw + quadrant_offset; // calc theta
index++;
} else {println("cFigure.add_cShape: attepted to add too many vertices!");}
}
void add_cShape_with_vector(float radius, float theta, cShape cshpRef, float relBearing){
// In the case that it is easier to express vertices as a vertex from the center
if(index < template.length){
pieces[index] = cshpRef;
bearingRel[index] = relBearing;
template[index][0] = -1; template[index][1] = -1; //never used again
// relative pos expressed as a vector
template[index][2] = radius;
template[index][3] = theta;
index++;
} else {println("cShape.add_vertex_as_vector: attepted to add too many vertices!");}
}
void make_penta_pinwheel(float radius, float cWidth, float cHeight){
// helper function makes a five-bladed pinwheel with rectangular blades
// You must first instantiate a cFigure with 5 pieces
cShape temp;
for(int i = 0; i < 5; i++){
temp = new cShape(4,0.0,0.0);
temp.make_rect(cWidth,cHeight);
add_cShape_with_vector(radius + cHeight/2.0, i*TWO_PI/5, temp, i*TWO_PI/5);
}
}
float normalize_2PI(float raw){
// Normalize bearing to range 0 - 2*PI
float rtnBearing = 0;
if(raw < 0){
rtnBearing = 2*PI + raw;
}else if(raw > 2*PI){
rtnBearing = raw - 2*PI;
}else{rtnBearing = raw;}
return rtnBearing;
}
void set_bearing(float newTheta){
// set bearing to a specified angle
bearingAbs = normalize_2PI(newTheta);
//calc_points();
}
void change_bearing(float dTheta){
// change bearing by a specified amount
bearingAbs = normalize_2PI(bearingAbs + dTheta);
//calc_points();
}
void set_center(float iX, float iY){
// set the absolute position of figure center
cenX = iX; cenY = iY;
calc_points();
}
void move(float dX, float dY){
// move the figure center by a specified amount
cenX += dX;
cenY += dY;
}
void move_vector(float distance){
// move the figure center in direction of bearing by amount
cenX += distance * sin(bearingAbs);
cenY += distance * cos(bearingAbs) * -1;
}
void calc_points(){
// calculate and store absolute position of points in the shape
for(int i = 0; i < template.length; i++){
vertices[i][0] = cenX + template[i][2] * sin(bearingAbs+template[i][3]);
vertices[i][1] = cenY + template[i][2] * cos(bearingAbs+template[i][3]) * -1;
}
}
void set_color(int inColor){
// Set the color for all cShapes in the cFigure
// Note that it is possible to set the color of shapes individually
for(int i = 0; i < template.length; i++){
pieces[i].set_color(inColor);
// Note that it is possible to set the color of shapes individually
}
}
void paint(){
calc_points();
for(int i = 0; i < template.length; i++){
// place cShapes and then paint each one
pieces[i].set_center(vertices[i][0],vertices[i][1]);
pieces[i].set_bearing(normalize_2PI(bearingAbs + bearingRel[i]));
pieces[i].paint();
}
}
}
/**
* cShape
* custom shape class for use with sketches
* Square Crow, June 2010
*/
/**
LOG
===
2010-06-13: wrote normalize_2PI for DRY principle
2010-06-13: wrote helper function make_rect() to simplify creating rectangles
TO DO
=====
* Add ability to make curves in the figure
* Add ability to scale a figure
*/
// Special Shape Types
public static int RECT = -1;
class cShape{
float cenX; float cenY; // center of the shape
float bearing; // facing direction in rad, 0 == UP
float[][] template; // [vertexNumber][{dX, dY, radius, angle}]
float[][] vertices; // last stored position
int index = 0; // for manual init of points
int shapeColor = 255;
cShape(int numPoints, float iX, float iY){
// contructor for manual init of points
template = new float[numPoints][4];
vertices = new float[numPoints][2];
cenX = iX; cenY = iY;
bearing = 0;
}
void make_rect(float iWidth, float iHeight){
// helper function makes rectangle iWidth by iHeight
// You must first instantiate a cShape with 4 points
add_vertex(-iWidth/2, -iHeight/2);
add_vertex(iWidth/2, -iHeight/2);
add_vertex(iWidth/2, iHeight/2);
add_vertex(-iWidth/2, iHeight/2);
}
void add_vertex(float dX, float dY){
if(index < template.length){
template[index][0] = dX; template[index][1] = dY;
// relative pos expressed as a vector
template[index][2] = sqrt(pow(dX,2) + pow(dY,2)); // calc radius
float quadrant_offset = 0;
float raw = atan(-dX/dY);
if(dX > 0 && dY > 0){quadrant_offset = -PI;} else
if(dX < 0 && dY > 0){quadrant_offset = PI;}
template[index][3] = raw + quadrant_offset; // calc theta
index++;
} else {println("cShape.add_vertex: attepted to add too many vertices!");}
}
void add_vertex_as_vector(float radius, float theta){
// In the case that it is easier to express vertices as a vertex from the center
if(index < template.length){
template[index][0] = -1; template[index][1] = -1; //never used again
// relative pos expressed as a vector
template[index][2] = radius;
template[index][3] = theta;
index++;
} else {println("cShape.add_vertex_as_vector: attepted to add too many vertices!");}
}
void calc_points(){
// calculate and store absolute position of points in the shape
for(int i = 0; i < template.length; i++){
vertices[i][0] = cenX + template[i][2] * sin(bearing+template[i][3]);
vertices[i][1] = cenY + template[i][2] * cos(bearing+template[i][3]) * -1;
}
}
void set_bearing(float newTheta){
// set bearing to a specified angle
bearing = normalize_2PI(newTheta);
//calc_points();
}
void change_bearing(float dTheta){
// change bearing by a specified amount
bearing = normalize_2PI(bearing + dTheta);
//calc_points();
}
void set_center(float iX, float iY){
// set the absolute position of figure center
cenX = iX; cenY = iY;
calc_points();
}
void move(float dX, float dY){
// move the figure center by a specified amount
cenX += dX;
cenY += dY;
}
void move_vector(float distance){
// move the figure center in direction of bearing by amount
cenX += distance * sin(bearing);
cenY += distance * cos(bearing) * -1;
}
float normalize_2PI(float raw){
// Normalize bearing to range 0 - 2*PI
float rtnBearing = 0;
if(raw < 0){
rtnBearing = 2*PI + raw;
}else if(raw > 2*PI){
rtnBearing = raw - 2*PI;
}else{rtnBearing = raw;}
return rtnBearing;
}
void set_color(int inColor){
shapeColor = inColor;
}
void paint(){
calc_points(); // calculate any transformations made this frame
fill(shapeColor);
beginShape();
for(int i = 0; i < template.length; i++){
vertex(vertices[i][0],vertices[i][1]);
//println(str(vertices[i][0]) + " , " + str(vertices[i][1]));
}
vertex(vertices[0][0],vertices[0][1]); // close the loop
endShape();
//println(template.length);
}
}
/**
* pentaSpinner
* spinning stars move about the screen
* Square Crow, June 2010
*/
/**
LOG
===
2010-06-13: wrote cShape class as a workaround to the way Processing
handles rotations and translations, instead operating on
a shape-by-shape basis. This class should be widely
applicable to other sketches
2010-06-27: wrote cFigure class to manipulate groups of cShapes as a unit
TO DO
=====
* write a function to move pinwheels around in their orbits
*/
// * Globals *
int WINWIDTH = 400; // = 400;
int WINHEIGHT = 400; // = 400;
// * Definitions *
float normalize_2PI(float raw){
// Normalize bearing to range 0 - 2*PI
float rtnBearing = 0;
if(raw < 0){
rtnBearing = 2*PI + raw;
}else if(raw > 2*PI){
rtnBearing = raw - 2*PI;
}else{rtnBearing = raw;}
return rtnBearing;
}
void orbit_pinwheel(){
for(int i = 0; i < 5; i++){
//coords[cFigure][{radius, bearing, turn_rate, orbit_rate, center_x, center_y}]
Pinwheels[i].change_bearing(coords[i][2]);
if(i > 0){
coords[i][1] = normalize_2PI(coords[i][1] + coords[i][3]);
Pinwheels[i].set_center(coords[i][4] + coords[i][0] * sin(coords[i][1]),
coords[i][5] + coords[i][0] * cos(coords[i][1]) * -1);
}
Pinwheels[i].paint();
}
}
// * Setup *
//cShape dfFigure;
//cFigure Assembly;
//cShape square1;
//cShape square2;
//cShape square3;
//cFigure Spinner;
cFigure[] Pinwheels;
float[][] coords;
void setup(){
size(WINWIDTH,WINHEIGHT);
noStroke(); // no outlines
//smooth(); //remove for faster performance
frameRate(30);
//noLoop(); // used only for static pictures
rectMode(CENTER);
float rad;
// dfFigure = new cShape(4,200.0,100.0); // quadrilateral cShape
// dfFigure.make_rect(40,40);
// Assembly = new cFigure(3,200.0,200.0);
// square1 = new cShape(4, 0.0, 0.0);
// square1.make_rect(40,40);
// square2 = new cShape(4, 0.0, 0.0);
// square2.make_rect(40,40);
// square3 = new cShape(4, 0.0, 0.0);
// square3.make_rect(40,40);
// Assembly.add_cShape(-30.0, -30.0, square1, 1*PI/16);
// Assembly.add_cShape(30.0, -30.0, square2, 2*PI/16);
// Assembly.add_cShape(-30.0, 30.0, square3, 3*PI/16);
// Spinner = new cFigure(5,200.0,200.0);
// rad = 100.0;
// Spinner.make_penta_pinwheel(rad, 2.0*rad*cos(TWO_PI/10.0), 2.5*rad);
coords = new float[5][6]; // orbit all but the largest pinwheel
//coords[cFigure][{radius, bearing, turn_rate, orbit_rate, center_x, center_y}]
Pinwheels = new cFigure[5];
float baseTurnRate = PI/50;
for(int i = 5; i > 0; i--){
Pinwheels[5 - i] = new cFigure(5, 200.0, 200.0 - (5 - i) * 20.0);
//coords[cFigure][{radius, bearing, turn_rate, orbit_rate, center_x, center_y}]
coords[5 - i] = new float[] {(5 - i) * 20.0, i*TWO_PI/5, (1-2*(i%2))*baseTurnRate, -(1-2*(i%2))*baseTurnRate, 200.0, 200.0};
rad = 100.0 * i/5;
Pinwheels[5 - i].make_penta_pinwheel(rad, 2.0*rad*cos(TWO_PI/10.0), 2.5*rad);
Pinwheels[5 - i].set_color(255 * (i%2)); // alternate black and white
}
}
// * Main Loop *
void draw(){
background(0); // repaint background, for animation
//translate(width/2, height/2);
//translate(100, 100);
//rotate(PI/3.0);
//rect(100, 100, 55, 55);
// dfFigure.change_bearing(PI/15);
// dfFigure.paint();
// Assembly.change_bearing(PI/30);
// Assembly.paint();
orbit_pinwheel();
// Pinwheels[0].change_bearing(PI/30);
// Pinwheels[0].paint();
}
An animation of spinning pinwheels.
I wrote some classes to help me manage moving shapes and groups of shapes, being frustrated with the way that transforms "stack". About halfway through I realized that this frustration probably comes from a lack of knowledge on how to use those functions properly, but I forged ahead with my own methods anyway.
I expect to re-write this in a sane way.