Created
June 23, 2012 14:34
-
-
Save shinaisan/2978524 to your computer and use it in GitHub Desktop.
Foxtail Grass in Processing.js
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
/* -*- mode: java; indent-tabs-mode; nil -*- */ | |
int canvasWidth = 500; | |
int canvasHeight = 500; | |
ControlPoint[] controlPoints; | |
CheckBox chkShowControlPoints; | |
Setaria setaria; | |
void setup() { | |
frameRate(16); | |
size(canvasWidth, canvasHeight); | |
controlPoints = new ControlPoint[4]; | |
controlPoints[0] = new ControlPoint(400, 520, 32); | |
controlPoints[1] = new ControlPoint(480, 400, 32); | |
controlPoints[2] = new ControlPoint(200, 64, 32); | |
controlPoints[3] = new ControlPoint(64, 64, 32); | |
for (int i = 0; i < controlPoints.length; i++) { | |
controlPoints[i].setRange(controlPoints[i].x - 100, | |
50, | |
controlPoints[i].x + 100, | |
550); | |
} | |
chkShowControlPoints = new CheckBox(8, 8, 32, "Show Control Points", true); | |
setaria = new Setaria(controlPoints); | |
update(); | |
} | |
void draw() { | |
update(); | |
background(#193608); | |
int currentTimeSec = millis() / 1000; | |
strokeWeight(3); | |
stroke(255); | |
noFill(); | |
if (chkShowControlPoints.value) { | |
for (int i = 0; i < controlPoints.length; i++) { | |
controlPoints[i].draw(); | |
} | |
} | |
chkShowControlPoints.draw(); | |
setaria.draw(); | |
} | |
void update() { | |
int mx = mouseX; | |
int my = mouseY; | |
for (int i = 0; i < controlPoints.length; i++) { | |
controlPoints[i].track(mx, my); | |
} | |
setaria.update(); | |
} | |
void mousePressed() { | |
for (int i = 0; i < controlPoints.length; i++) { | |
controlPoints[i].mousePressed(); | |
} | |
chkShowControlPoints.mousePressed(); | |
} | |
void mouseReleased() { | |
for (int i = 0; i < controlPoints.length; i++) { | |
controlPoints[i].mouseReleased(); | |
} | |
} | |
interface Drawable { | |
void draw(); | |
} | |
class Point implements Drawable { | |
int x; | |
int y; | |
Point(int x, int y) { | |
moveTo(x, y); | |
} | |
void moveTo(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
void add(int dx, int dy) { | |
this.x += dx; | |
this.y += dy; | |
} | |
void add(Point pt) { | |
add(pt.x, pt.y); | |
} | |
void scalar(float f) { | |
this.x *= f; | |
this.y *= f; | |
} | |
void draw() { | |
point(this.x, this.y); | |
} | |
} | |
class ControlPoint implements Drawable { | |
int left; | |
int top; | |
int right; | |
int bottom; | |
int x; | |
int y; | |
int radius; | |
boolean dragging; | |
ControlPoint(int x, int y, int r) { | |
setRange(x, y, x, y); | |
moveTo(x, y); | |
this.radius = r; | |
this.dragging = false; | |
} | |
void setRange(int left, int top, int right, int bottom) { | |
this.left = left; | |
this.top = top; | |
this.right = right; | |
this.bottom = bottom; | |
moveTo(this.x, this.y); | |
} | |
boolean isBusy() { | |
return (this.isMouseOver() || this.dragging); | |
} | |
boolean isMouseOver() { | |
int mx = mouseX; | |
int my = mouseY; | |
int dx = this.x - mx; | |
int dy = this.y - my; | |
return (dx * dx + dy * dy <= this.radius * this.radius); | |
} | |
void mousePressed() { | |
this.dragging = isMouseOver(); | |
} | |
void mouseReleased() { | |
this.dragging = false; | |
} | |
void moveTo(int x, int y) { | |
if (x < this.left) { | |
x = this.left; | |
} else if (x >= this.right) { | |
x = this.right; | |
} | |
if (y < this.top) { | |
y = this.top; | |
} else if (y >= this.bottom) { | |
y = this.bottom; | |
} | |
this.x = x; | |
this.y = y; | |
} | |
void track(int mx, int my) { | |
if (this.dragging) { | |
moveTo(mx, my); | |
} | |
} | |
void draw() { | |
noFill(); | |
int alpha = abs((millis() % 2000) - 1000) / 1000 * 255; | |
stroke(255, alpha); | |
ellipse(this.x, this.y, this.radius, this.radius); | |
} | |
} | |
class Setaria implements Drawable { | |
Point[] stemPoints; | |
ControlPoint[] controlPoints; | |
Setaria(ControlPoint[] controlPoints) { | |
this.controlPoints = controlPoints; | |
this.stemPoints = new Point[7]; | |
for (int i = 0; i < stemPoints.length; i++) { | |
this.stemPoints[i] = new Point(0, 0); | |
} | |
update(6); | |
} | |
void update(int dw) { | |
int w = dw * 3; | |
for (int i = 0; i < 3; i++) { | |
int j = i; | |
if (i > 0) j = i - 1; | |
int dx = controlPoints[j].y - controlPoints[i + 1].y; | |
int dy = controlPoints[i + 1].x - controlPoints[j].x; | |
float r = w / sqrt(dx*dx + dy*dy); | |
stemPoints[i].moveTo(controlPoints[i].x, controlPoints[i].y); | |
stemPoints[i].add(r*dx, r*dy); | |
dx *= -1; | |
dy *= -1; | |
stemPoints[6 - i].moveTo(controlPoints[i].x, controlPoints[i].y); | |
stemPoints[6 - i].add(r*dx, r*dy); | |
w -= dw; | |
} | |
stemPoints[3].moveTo(controlPoints[3].x, controlPoints[3].y); | |
} | |
void draw() { | |
drawStem(6, #629a37); | |
drawSpikes(); | |
} | |
void drawStem(int dw, color c) { | |
update(dw); | |
strokeWeight(1); | |
stroke(c); | |
fill(c); | |
beginShape(); | |
vertex(stemPoints[0].x, stemPoints[0].y); | |
bezierVertex(stemPoints[1].x, stemPoints[1].y, | |
stemPoints[2].x, stemPoints[2].y, | |
stemPoints[3].x, stemPoints[3].y); | |
bezierVertex(stemPoints[4].x, stemPoints[4].y, | |
stemPoints[5].x, stemPoints[5].y, | |
stemPoints[6].x, stemPoints[6].y); | |
endShape(CLOSE); | |
} | |
void drawSpikes() { | |
update(6); | |
drawSpikes(stemPoints[0].x, stemPoints[0].y, | |
stemPoints[1].x, stemPoints[1].y, | |
stemPoints[2].x, stemPoints[2].y, | |
stemPoints[3].x, stemPoints[3].y, | |
20, 60, 60, 90, 40); | |
drawSpikes(stemPoints[3].x, stemPoints[3].y, | |
stemPoints[4].x, stemPoints[4].y, | |
stemPoints[5].x, stemPoints[5].y, | |
stemPoints[6].x, stemPoints[6].y, | |
0, 40, 60, 40, 90); | |
} | |
void drawSpikes(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, | |
int start, int end, int count, int w1, int w2) { | |
randomSeed(0); | |
for (int i = start; i <= end; i++) { | |
float t = (float)i / count; | |
float weight = w1 + (w2 - w1)*t; | |
float x = bezierPoint(x1, x2, x3, x4, t); | |
float y = bezierPoint(y1, y2, y3, y4, t); | |
float tx = bezierTangent(x1, x2, x3, x4, t); | |
float ty = bezierTangent(y1, y2, y3, y4, t); | |
float tr = sqrt(tx*tx + ty*ty); | |
float dx = random(16) - 8; | |
float dy = random(16) - 8; | |
x += dx; | |
y += dy; | |
strokeWeight(16); | |
stroke(#d0f0a7, 128); | |
point(x, y); | |
strokeWeight(1); | |
stroke(#d9f1c3, 64); | |
int n = 50; | |
for (int j = 0; j < n; j++) { | |
float r = weight + random(weight / 2); | |
float cs = - ty / tr; | |
float sn = tx / tr; | |
float angle = radians(random(120) - 60); | |
float cs2 = cos(angle); | |
float sn2 = sin(angle); | |
cs = cs*cs2 - sn*sn2; | |
sn = sn*cs2 + cs*sn2; | |
line(x, y, x + cs*r, y + sn*r); | |
} | |
} | |
} | |
} | |
class CheckBox implements Drawable { | |
int left; | |
int top; | |
int height; | |
String caption; | |
boolean value; | |
PFont font; | |
CheckBox(int x, int y, int h, String caption, boolean initialValue) { | |
this.left = x; | |
this.top = y; | |
this.height = h; | |
this.value = initialValue; | |
this.caption = caption; | |
this.font = loadFont(PFont.list()[0], this.height); | |
} | |
boolean isMouseOver() { | |
int mx = mouseX; | |
int my = mouseY; | |
return ((mx >= this.left) && (mx < this.left + this.height) && | |
(my >= this.top) && (my < this.top + this.height)); | |
} | |
void mousePressed() { | |
if (this.isMouseOver()) { | |
this.value = !this.value; | |
} | |
} | |
void draw() { | |
stroke(#80c060); | |
strokeWeight(4); | |
noFill(); | |
rect(this.left, this.top, this.height, this.height); | |
fill(#80c060); | |
textFont(this.font); | |
textAlign(LEFT, BOTTOM); | |
text(this.caption, this.left + this.height * 1.2, this.top + this.height); | |
if (this.value) { | |
line(this.left, this.top + this.height / 2, | |
this.left + this.height * 1 / 3, this.top + this.height); | |
line(this.left + this.height * 1 / 3, this.top + this.height, | |
this.left + this.height, this.top); | |
} | |
} | |
} |
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
<html> | |
<head> | |
<title>Foxtail Grass</title> | |
<script type="text/javascript" src="processing.js"></script> | |
</head> | |
<body> | |
<h1>Foxtail Grass</h1> | |
<br/> | |
<script type="application/processing" src="foxtail.processing"></script><canvas></canvas> | |
<br/> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment