xxxxxxxxxx
289
// important fixes & improvements:
// desired length is now a property of nodes, not of connections so they cant be vaiable
// sources for next steps:
// - that girl from textile museum
// - willy morry <3
let stitchdist = 5; // this is kinda dumb and easy to fix but im lazy so stitch distance gets multiplied with scale so if u want an actual stitch distance of 2mm on scale 4 u need to set stitchdist to 5
let embroideryScale = 4;
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++){
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 / p5.Vector.dist(this.pos, p)) - 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 / p5.Vector.dist(this.pos, p)) - 1.0 ) // how close the current lengh is to the rest length
);
}
}
move.div(linkcount*pullstepdiv)
if(this instanceof Root){
move = createVector(0,-upDraft);
}
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('press ENTER to flower', 10, 15)
text('nodes: ' + rhiz.length, 10, 25);
text('draw time: ' + int(deltaTime) + ' ms', 10, 35)
line(20,height-10,width-20,height-10)
}
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(
30+random()*(width-60),
height-10,
[],
1.0,
true));
rhiz[rhiz.length-1].grow();
}
}
//saving stuff
function getEmbroidery(startId) {
pushStitch(startId);
if(rhiz[rhiz[startId].links[1]] instanceof Leaf){
for(let i=1;i<rhiz[startId].links.length-1; i++){
pushStitch(rhiz[startId].links[i])
pushStitch(rhiz[startId].links[i+1])
pushStitch(startId);
}
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]);
}
pushStitch(startId);
}
}
function pushStitch(index){
let jump = p5.Vector.sub(rhiz[index].pos, coords[coords.length-1]);
if(jump.mag() < 1){ return }
let tc = []
for(let i=stitchdist; i < jump.mag(); i+=stitchdist){
tc.push(
p5.Vector.add(coords[coords.length-1],
p5.Vector.mult(jump, i/jump.mag())
)
);
}
coords.push(tc, rhiz[index].pos);
}
function saveCSV(){
coords = [];
for(let i=0;i<rhiz.length;i++){
if(rhiz[i].isRoot){
coords.push(rhiz[i].pos);
getEmbroidery(i);
}
}
let table = new p5.Table();
table.addColumn('x');
table.addColumn('y');
for(let i=20; i<(width-20); i+= stitchdist){
let newRow = table.addRow();
newRow.setNum('x', round(i * embroideryScale));
newRow.setNum('y', round((height-10) * embroideryScale) );
}
for(let i=0;i<coords.length;i++){
let newRow = table.addRow();
newRow.setNum('x', round(coords[i].x * embroideryScale));
newRow.setNum('y', round(coords[i].y * embroideryScale));
}
saveTable(table,'fg1-2.csv')
}