Last active
June 23, 2022 07:26
-
-
Save JustinSDK/b0273c51e912e48ae0b4d665806776f9 to your computer and use it in GitHub Desktop.
FoliageScroll
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
// 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