Last active
September 26, 2024 14:28
-
-
Save JustinSDK/6a7399d0401ac0c77b0db67ae9a07b91 to your computer and use it in GitHub Desktop.
Differential line growth with p5.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
// Differential line growth with p5.js | |
// refactored from http://www.codeplastic.com/2017/07/22/differential-line-growth-with-processing/ | |
const maxNodeNumbers = 400; | |
const nodesStart = 15; | |
const rayStart = 15; | |
let diffLine; | |
function setup() { | |
createCanvas(300, 300); | |
stroke(0); | |
fill(0, 255, 255); | |
diffLine = new DifferentialLine(nodesStart, rayStart, createVector(width / 2, height / 2)); | |
} | |
function draw() { | |
background(200); | |
diffLine.update(); | |
render(diffLine.nodes); | |
if(diffLine.nodes.length >= maxNodeNumbers) { | |
noLoop(); | |
} | |
} | |
function render(nodes) { | |
beginShape(); | |
for(let node of nodes) { | |
const {x, y} = node.coordinate; | |
vertex(x, y); | |
} | |
endShape(CLOSE); | |
} | |
class Node { | |
constructor(coordinate, velocity, maxSpeed = 0.5, maxForce = 1) { | |
this.coordinate = coordinate; | |
this.velocity = velocity; | |
this.maxSpeed = maxSpeed; | |
this.maxForce = maxForce; | |
} | |
applyForce(force) { | |
this.velocity.add(force); | |
this.velocity.limit(this.maxSpeed); | |
} | |
updateCoordinate() { | |
this.coordinate.add(this.velocity); | |
} | |
cohesion(other) { | |
const desired = p5.Vector.sub(other, this.coordinate).setMag( | |
this.maxSpeed | |
); | |
return p5.Vector.sub(desired, this.velocity).limit(this.maxForce); | |
} | |
seperate(other, separationDistance = 15) { | |
const v = p5.Vector.sub(this.coordinate, other.coordinate); | |
const dist = v.mag(); | |
if(dist < separationDistance) { | |
return v.div(dist * dist); | |
} | |
return v.mult(0); // 零向量 | |
} | |
} | |
class DifferentialLine { | |
constructor(nodesStart, rayStart, center) { | |
const aStep = TWO_PI / nodesStart; | |
const nodes = []; | |
for (let a = 0; a < TWO_PI; a += aStep) { | |
nodes.push( | |
new Node( | |
createVector(rayStart, 0).rotate(a).add(center), | |
p5.Vector.random2D() | |
) | |
); | |
} | |
this.nodes = nodes; | |
} | |
update() { | |
this.differentiate(); | |
this.grow(); | |
} | |
differentiate(separationCohesionRatio = 1.5) { | |
const allSeperation = []; | |
for(let i = 0; i < this.nodes.length; i++) { | |
allSeperation.push(createVector()); | |
} | |
for(let i = 0; i < this.nodes.length; i++) { | |
const {sep, seps} = seperate(this.nodes, i, allSeperation); | |
const coh = cohesion(this.nodes, i); | |
this.nodes[i].applyForce( | |
sep.mult(separationCohesionRatio).add(coh) | |
); | |
this.nodes[i].updateCoordinate(); | |
// 更新 allSeperation | |
for(let j = i + 1; j < this.nodes.length; j++) { | |
let sep = seps[j - i - 1]; | |
if(sep.mag() > 0) { | |
allSeperation[j].sub(seps[j - i - 1]); | |
} | |
} | |
} | |
} | |
grow(maxEdgeLength = 15) { | |
const n = this.nodes.length; | |
const nodes = []; | |
for(let i = 0, j = 1; i < n; i++, j++) { | |
const node = this.nodes[i]; | |
const nxNode = this.nodes[j % n]; | |
nodes.push(node); | |
const dist = p5.Vector.dist(node.coordinate, nxNode.coordinate); | |
if(dist > maxEdgeLength) { | |
const {x, y} = middlePoint(node.coordinate, nxNode.coordinate); | |
nodes.push( | |
new Node( | |
createVector(x, y), | |
p5.Vector.random2D(), | |
(node.maxSpeed + nxNode.maxSpeed) / 2, | |
(node.maxForce + nxNode.maxForce) / 2 | |
) | |
); | |
} | |
} | |
this.nodes = nodes; | |
} | |
} | |
function seperate(nodes, i, allSeperation) { | |
const node = nodes[i]; | |
const sep = allSeperation[i]; | |
let seps = []; | |
for(let j = i + 1; j < nodes.length; j++) { | |
const sep_j = node.seperate(nodes[j]); | |
if(sep_j.mag() > 0) { | |
sep.add(sep_j); | |
} | |
seps.push(sep_j); | |
} | |
sep.normalize().mult(node.maxSpeed).limit(node.maxForce); | |
return {sep, seps}; | |
} | |
function cohesion(nodes, i) { | |
const n = nodes.length; | |
return nodes[i].cohesion( | |
middlePoint( | |
nodes[(n + i - 1) % n].coordinate, | |
nodes[(i + 1) % n].coordinate | |
) | |
); | |
} | |
function middlePoint(p1, p2) { | |
return p5.Vector.add(p1, p2).div(2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment