Skip to content

Instantly share code, notes, and snippets.

@shinaisan
Created June 23, 2012 14:34
Show Gist options
  • Save shinaisan/2978524 to your computer and use it in GitHub Desktop.
Save shinaisan/2978524 to your computer and use it in GitHub Desktop.
Foxtail Grass in Processing.js
/* -*- 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);
}
}
}
<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