Created
July 19, 2021 08:00
-
-
Save peternewman22/2e4ac197c4b22fca03caceacc94d4bc3 to your computer and use it in GitHub Desktop.
ChaikinCurveSubdivision implementation and animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// music from https://audionautix.com/free-music/meditative | |
import processing.sound.*; | |
SoundFile file; | |
int ptCount, stop; | |
PVector[] cpts0; | |
int everyXFrames; | |
int level; | |
Curve[] curves; | |
boolean isStopped; | |
void setup() { | |
size(1000, 500); | |
file = new SoundFile(this, "RunningWaters.wav"); | |
isStopped = true; | |
ptCount = 50; | |
level = 0; | |
everyXFrames = 2; | |
cpts0 = new PVector[ptCount]; | |
for (int i = 0; i < cpts0.length; i++) { | |
cpts0[i] = new PVector(i*(width/ptCount)+random(-50, 50), random(-75, 75)); | |
} | |
curves = new Curve[4]; | |
curves[0] = new Curve(cpts0); | |
curves[1] = new Curve(subdivide(curves[0].cpts)); | |
curves[2] = new Curve(subdivide(curves[1].cpts)); | |
curves[3] = new Curve(subdivide(curves[2].cpts)); | |
// the current drawing level | |
level = 0; | |
// load the soundfile | |
strokeWeight(1); | |
noFill(); | |
noLoop(); | |
} | |
void draw(){ | |
background(#f5f5f5); | |
translate(15, height/2); | |
curves[level].drawPoints(); | |
// draw all the other lines also | |
for(int i = 0; i < level+1; i++){ | |
curves[i].drawLine(); | |
} | |
if(frameCount%5 == 0){ | |
curves[level].update(); | |
} | |
} | |
// The Algorithm! | |
PVector[] subdivide(PVector[] cpts) { | |
// divide the thing into 3 sections: 25%, 50%, 25% or cumulatively 25%, 75%, 100% | |
PVector[] newCpts = new PVector[cpts.length*2]; | |
for (int i = 0; i < cpts.length-1; i++) { | |
PVector original = PVector.sub(cpts[i+1], cpts[i]); | |
float l = original.mag(); | |
PVector start = PVector.add(cpts[i], original.copy().normalize().setMag(0.25*l)); | |
PVector end = PVector.add(cpts[i], original.copy().normalize().setMag(0.75*l)); | |
// make sure they go to the right index in the next array | |
newCpts[2*i] = start; | |
newCpts[2*i + 1] = end; | |
} | |
// not actually sure what to do with the last couple of points... I just set them both to the parent's end point. | |
newCpts[newCpts.length -1] = cpts[cpts.length - 1]; | |
newCpts[newCpts.length - 2] = cpts[cpts.length - 1]; | |
return newCpts; | |
} | |
void keyPressed(){ | |
if(keyCode == ' '){ | |
file.play(); | |
loop(); | |
} | |
} | |
class Curve { | |
boolean isDrawn; | |
PVector[] cpts; | |
int alpha; | |
int stop; | |
Curve(PVector[] cpts_) { | |
cpts = cpts_; | |
isDrawn = false; | |
alpha = 255; | |
stop = 1; | |
} | |
// this is a bit clunky - it talks directly to global variables | |
// that's probably a no-no | |
void update() { | |
stop += 1; | |
if (stop >= cpts.length - 1) { | |
isDrawn = true; | |
level = (level + 1)%4; | |
println("Now progressing to level " + level); | |
curves[level].stop = 1; | |
curves[level].isDrawn = false; | |
} | |
} | |
void drawPoints(){ | |
strokeWeight(4); | |
stroke(0, 50); | |
for(PVector p : cpts){ | |
point(p.x, p.y); | |
} | |
} | |
void drawLine() { | |
strokeWeight(2); | |
if (isDrawn) { | |
alpha = 25; | |
} else { | |
alpha = 255; | |
} | |
stroke(0, alpha); | |
beginShape(); | |
for (int i = 0; i < stop; i++) { | |
vertex(cpts[i].x, cpts[i].y); | |
} | |
endShape(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment