xxxxxxxxxx
280
// important fixes & improvements:
// - desired length is now a property of nodes, not of connections so they cant be vaiable
// - do some angular or otherwise non-node based repulsion??
// - after that i can start experimenting with different flower structures etc
// sources for next steps:
// - that girl from textile museum
// - willy morry <3
let rhiz = [];
let pullstepdiv = 0.5; //how much to divide the steps taken each frame
let maxLeafSize = 20;
let growthFalloff = 0.85;
let leafCount = 7;
let upDraft = -1;
let sideDraft = 0.1;
// saving/downloading stuff:
let coords = [];
let paused = false;
class Node {
constructor(x,y,links){
this.pos = createVector(x,y)
this.links = links;
this.id = rhiz.length;
this.restLength = 25;
for(let i=0; i<this.links.length; i++){
rhiz[this.links[i]].links.push(this.id);
}
}
drawLinks(){
for(let i=0;i<this.links.length;i++){
if(this.id<this.links[i]){
line(this.pos.x, this.pos.y, rhiz[this.links[i]].pos.x, rhiz[this.links[i]].pos.y);
}
}
}
pushPull(){
let move = createVector(0,0)
let linkcount = this.links.length + 1;
if(this.isRoot){
return
}
for(let i=0;i<this.links.length;i++){
let p = createVector( rhiz[this.links[i]].pos.x, rhiz[this.links[i]].pos.y );
move.add( p5.Vector.mult(
p5.Vector.sub(this.pos, p), // the vector towards the linked node
this.restLength / (this.pos.dist(p)+0.0001) - 1.0 ) // how close the current lengh is to the rest length
);
}
// if(this instanceof Leaf){
// linkcount += rhiz[this.links[0]].links.length;
// for(let i=0;i<rhiz[this.links[0]].links.length;i++){
// let p = createVector(
// rhiz[rhiz[this.links[0]].links[i]].pos.x,
// rhiz[rhiz[this.links[0]].links[i]].pos.y );
// move.add( p5.Vector.mult(
// p5.Vector.sub(this.pos, p), // the vector towards the linked node
// this.restLength / (this.pos.dist(p)+0.0001) - 1.0 ) // how close the current lengh is to the rest length
// );
// }
// }
move.div(linkcount*pullstepdiv)
this.pos.add(move);
this.pos.add(createVector(sideDraft*(noise(millis()*0.001)-0.5),upDraft));
}
}
class Leaf extends Node {
constructor(x,y,links, leafSize){
super(x,y,links);
// this.restLength = leafSize;
}
}
class Stem extends Node {
constructor(x,y,links,growChance, isRoot){
super(x,y,links);
this.growChance = growChance;
this.isRoot = isRoot;
}
grow(){
if(random()>1-this.growChance){
rhiz.push(new Stem(
this.pos.x,
this.pos.y,
[this.id],
growthFalloff * this.growChance,
false
));
rhiz[rhiz.length-1].grow();
if(random()>1-this.growChance/2){
this.grow();
}
}
//or flower
else{
let flowVec = createVector(0,maxLeafSize*this.growChance)
for(let i=0; i<leafCount; i++){
rhiz.push(new Leaf(
this.pos.x,
this.pos.y,
[this.id, rhiz.length-1],
maxLeafSize*this.growChance
));
}
}
}
}
class Root extends Node {
constructor(x,y,links){
super(x,y,links);
}
}
function setup() {
createCanvas(500, 500);
//saving stuff
createButton('pause/play').mousePressed(() => {paused = !paused }).position(20,height + 20);
createButton('download csv').mousePressed(saveCSV).position(120,height + 20);
rhiz.push(new Stem(
width/2,
height/2,
[],
1.0,
false));
}
function draw() {
if(paused){
return
}
background(255);
rhiz[0].pos = createVector(mouseX,mouseY);
for(let i=1;i<rhiz.length;i++){
rhiz[i].pushPull();
}
for(let i=0;i<rhiz.length;i++){
point(rhiz[i].pos.x,rhiz[i].pos.y);
rhiz[i].drawLinks();
}
text('click to flower', 10, 15)
text('nodes: ' + rhiz.length, 10, 25);
text('draw time: ' + int(deltaTime) + ' ms', 10, 35)
}
function mouseClicked(){
// growRandom();
// rhiz.push(new Stem(
// 20+random()*(width-40),
// height-10,
// [],
// 1.0,
// true));
// rhiz[rhiz.length-1].grow();
print(rhiz);
}
function growRandom(){
let rId = floor(random() * rhiz.length)
if(rhiz[rId] instanceof Stem && rhiz[rId].links.length<4){
rhiz[rId].grow();
}
else{
growRandom();
}
}
function keyPressed() {
if (keyCode === ENTER) {
rhiz.push(new Stem(
20+random()*(width-40),
height-10,
[],
1.0,
true));
rhiz[rhiz.length-1].grow();
}
}
//saving stuff
function getEmbroidery(startId) {
coords.push(rhiz[startId].pos);
if(rhiz[rhiz[startId].links[1]] instanceof Leaf){
for(let i=1;i<rhiz[startId].links.length-1; i++){
coords.push(rhiz[rhiz[startId].links[i]].pos)
coords.push(rhiz[rhiz[startId].links[i+1]].pos)
coords.push(rhiz[startId].pos);
}
return
}
for(let i=0; i<rhiz[startId].links.length; i++){
if(!rhiz[startId].isRoot && i==0){
continue
}
if(rhiz[rhiz[startId].links[i]] instanceof Stem){
getEmbroidery(rhiz[startId].links[i]);
}
if(p5.Vector.dist(coords[coords.length-1],rhiz[startId].pos) > 1){
coords.push(rhiz[startId].pos);
}
}
}
function saveCSV(){
coords = [];
for(let i=0;i<rhiz.length;i++){
if(rhiz[i].isRoot){
getEmbroidery(i);
}
}
let table = new p5.Table();
table.addColumn('x');
table.addColumn('y');
for(let i=20; i<(width-20); i+= 25){
let newRow = table.addRow();
newRow.setNum('x', i);
newRow.setNum('y', round(height-10));
}
for(let i=0;i<coords.length;i++){
let newRow = table.addRow();
newRow.setNum('x', round(coords[i].x));
newRow.setNum('y', round(coords[i].y));
}
saveTable(table,'fg2.csv')
}