Skip to content

Instantly share code, notes, and snippets.

@JustinSDK
Last active June 23, 2022 07:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JustinSDK/b0273c51e912e48ae0b4d665806776f9 to your computer and use it in GitHub Desktop.
Save JustinSDK/b0273c51e912e48ae0b4d665806776f9 to your computer and use it in GitHub Desktop.
FoliageScroll
// refactor from https://openprocessing.org/sketch/1491377
import java.util.*;
final int maxSpirals = 500;
final float angleMax = 4 * PI;
final float angleStep = 0.1;
final float minR = 8;
final float distScale = 1.5;
class FoliageScroll {
class Spiral {
// center x, y
final float x;
final float y;
// start r
final float r;
// start angle
final float startAngle;
// total angle
float angle;
int angleSign;
List<PVector> path = new ArrayList<PVector>();
Spiral(float x, float y, float r, float startAngle, int angleSign) {
this.x = x;
this.y = y;
this.r = r;
this.startAngle = startAngle;
this.angleSign = angleSign;
path.add(vt());
}
Spiral(float x, float y, float r, float startAngle) {
this(x, y, r, startAngle, 1);
}
void step(float angleStep) {
angle += angleStep;
path.add(vt());
}
PVector vt() {
float a = angle * angleSign + startAngle;
float s = (angleMax - angle) / angleMax;
return new PVector(
x + r * cos(a) * s,
y + r * sin(a) * s
);
}
void draw() {
push();
strokeWeight(r / 40 + 1);
beginShape();
for (PVector p : path) {
vertex(p.x, p.y);
}
endShape();
pop();
}
}
final int maxSpirals;
final float angleMax;
final float angleStep;
final float minR;
final float distScale;
List<Spiral> spirals = new ArrayList<Spiral>();
int done;
FoliageScroll(int maxSpirals, float angleMax, float angleStep, float minR, float distScale) {
this.maxSpirals = maxSpirals;
this.angleMax = angleMax;
this.angleStep = angleStep;
this.minR = minR;
this.distScale = distScale;
float r = random(100, 200);
spirals.add(new Spiral(r, 0, r, PI));
// mirror x
spirals.add(new Spiral(-r, 0, r, 0));
}
void draw() {
for (Spiral spiral : spirals) {
spiral.draw();
}
}
void tryCreateSpiral() {
for (int i = done; i < spirals.size() && spirals.size() < maxSpirals; i++) {
Optional<Spiral> spiral = tryCreateSpiral(i);
if (spiral.isPresent()) {
spirals.add(spiral.get());
}
}
}
void tryScrollSpirals() {
for (int i = done; i < spirals.size(); i++ ) {
Spiral spiral = spirals.get(i);
spiral.step(angleStep);
if (spiral.angle > angleMax - HALF_PI) {
done++;
}
}
}
Optional<Spiral> tryCreateSpiral(int i) {
Spiral spiralI = spirals.get(i);
if (spiralI.angle <= 3 * HALF_PI) {
return Optional.empty();
}
// candidate r
float cr = spiralI.r * random(0.5, 1.75);
if (cr < minR) { // too small
return Optional.empty();
}
float offAngle = random(0, 3 * HALF_PI);
float offR = spiralI.r *(angleMax - offAngle) / angleMax + cr;
// candidate startAngle
float ca = offAngle * spiralI.angleSign + spiralI.startAngle + PI;
// candidate center
float cx = spiralI.x + offR * cos(ca - PI);
float cy = spiralI.y + offR * sin(ca - PI);
// out of canvas or overloapped
if (cx < -width / 2 + cr || cx > width / 2 - cr || cy < -height / 2 + cr || cy > height / 2 - cr || isOverlapped(i, cx, cy, cr)) {
return Optional.empty();
}
return Optional.of(new Spiral(cx, cy, cr, ca, -spiralI.angleSign));
}
boolean isOverlapped(int i, float cx, float cy, float cr) {
for (int j = 0; j < spirals.size(); j++) {
if (j == i) {
continue;
}
Spiral spiral = spirals.get(j);
// overlapped or not?
float leng = sqrt(pow(cx - spiral.x, 2) + pow(cy - spiral.y, 2));
float sa = startAngle(cx, cy, spiral);
float d = cr * distScale;
if (dist(leng, spiral.r, sa) < d || dist(leng, spiral.r, sa + TAU) < d) {
return true;
}
}
return false;
}
float startAngle(float cx, float cy, Spiral spiral) {
float x = spiral.x;
float y = spiral.y;
float sa = (atan2((cy - y), (cx - x)) - spiral.startAngle) % TAU;
// 0 ~ TAU
return abs((sa + spiral.angleSign * TAU) % TAU);
}
float dist(float leng, float r, float a) {
return abs(leng - r * (angleMax - a) / angleMax);
}
}
FoliageScroll foliageScroll;
void setup() {
size(900, 900);
foliageScroll = new FoliageScroll(maxSpirals, angleMax, angleStep, minR, distScale);
noFill();
}
void draw() {
background(200);
// draw spirals
translate(width / 2, height / 2);
foliageScroll.draw();
foliageScroll.tryCreateSpiral();
foliageScroll.tryScrollSpirals();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment