1
0
Fork 0
vein3d/veingen3d.js

580 lines
14 KiB
JavaScript

paper.install(window);
window.onload = function() {
var canvas = document.getElementById('grower');
canvas.width = 300;
canvas.height = 300;
paper.setup(canvas);
Leaf.veinArea = new Path();
Leaf.auxinArea = new Path();
Leaf.size = 100; //200;
Leaf.auxinR = 9;
Leaf.veinR = Leaf.auxinR;
Leaf.auxinNr = 15;
Leaf.veinGrowRate = 1.5;
Leaf.leafGrowRate = 3; //1.04;
Leaf.maxGrowIters = 10;
//Leaf.maxSize = 1338*0.75; //legs
//Leaf.maxSize = 1511*0.75; //seat
Leaf.maxSize = 290;
Leaf.branches = [];
Leaf.auxin = [];
Leaf.color = new Color(0.8, 0.8, 0.8);
Leaf.midlayerItems = [];
Leaf.frontlayerItems = [];
Leaf.finishedShape = new Path();
Leaf.finished = false;
Leaf.final = false;
//Leaf.mouse = new Path.Circle(new Point(0,0),10);
Leaf.uniform = true;
var leaf = new Leaf(new Point(view.bounds.width/2,view.bounds.height/2));
Leaf.trycount = 10;
/*view.onClick = function(event){
var svg = project.exportSVG({ asString: true });
var svgBlob = new Blob([svg], {type:"image/svg+xml;charset=utf-8"});
var svgUrl = URL.createObjectURL(svgBlob);
var downloadLink = document.createElement("a");
downloadLink.href = svgUrl;
downloadLink.download = "growth.svg";
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}*/
view.onFrame = function(event) {
// On each frame, rotate the path by 3 degrees:
if(leaf.shape.bounds.height<Leaf.maxSize){
leaf.iteration();
}else{
if(!Leaf.finished){
leaf.iteration();
}else{
if(!Leaf.final){
console.log('finalize');
leaf.removeSmallVeins();
leaf.sortChildbranches();
leaf.rearrangeBranches();
leaf.calcWidth();
var svgString = leaf.shape.exportSVG({asString: true, bounds: 'content'});
console.log(svgString);
leaf.shape.remove();
var midlayer = new Layer(Leaf.midlayerItems);
var frontlayer = new Layer(Leaf.frontlayerItems);
Leaf.final = true;
setTimeout(function() {
// fixing a race condition in some browsers
generateTextures();
renderScene();
}, 10);
}
}
}
}
}
class Auxin{
constructor(point, closestVein){
this.pos = point;
this.closest = closestVein;
this.drawNode(this.pos);
Leaf.auxin.push(this);
}
drawNode(point){
this.node = new Path.Circle(point,0);
this.node.fillColor = 'red';
}
remove(){
this.node.remove();
//this.node.fillColor = 'pink';
}
scale(perc, pos){
this.node.scale(perc, pos);
this.pos = this.node.position;
}
}
class Leaf{
constructor(startpoint) {
this.pos = startpoint;
//this.shape = new Path.Circle(new Point(this.pos.x-Leaf.size,this.pos.y-Leaf.size),Leaf.size);
/*this.shape = new Path();
this.shape.add(new Segment(new Point(this.pos.x, this.pos.y), new Point(Leaf.size,0), new Point(-Leaf.size,0)));
this.shape.add(new Point(this.pos.x, this.pos.y-Leaf.size));
this.shape.closed = true;
*/
var rect = new Rectangle(new Point(startpoint.x-Leaf.size/2, startpoint.y-Leaf.size), new Size(Leaf.size, Leaf.size));
this.shape = new Path.Ellipse(rect);
//this.shape.rotation = 20;
//this.shape = new Path.RegularPolygon(new Point(this.pos.x, this.pos.y-Leaf.size), 3, 50);
//this.shape = project.importSVG(footstring2);
//this.shape = project.importSVG(seatstring);
//this.shape = this.shape.children[1];
//this.shape = project.importSVG('leaf.svg', {insert: false, expandShapes: true});
//console.log('shape', this.shape);
//this.shape = this.shape.children[1];
this.shape.strokeColor = 'red';
this.shape.strokeWidth = 2;
this.shape.fillColor = 'yellow';
//this.shape.position = new Point(this.pos.x, this.pos.y);
this.shape.position = new Point(this.pos.x, this.pos.y);
this.shape.scale(Leaf.size/this.shape.bounds.height, this.pos);
//this.shape.translate(new Point(-this.shape.bounds.width/2+Leaf.veinR/2,-this.shape.bounds.height/3+Leaf.veinR/2));
this.veins = new VeinGraph(this.pos);
this.auxin = [];
this.growCount = 0;
}
iteration(){
this.growVeins();
this.removeConsumedAuxin();
if(this.shape.bounds.height<Leaf.maxSize){
this.growLeaf();
}
if(!this.placeAuxin()){
Leaf.trycount--;
}else{
Leaf.trycount = 10;
}
if(Leaf.trycount==0){
Leaf.finished = true;
}
this.attractVeins();
}
placeAuxin(){
var f = false;
for(var i = 0; i<Leaf.auxinNr; i++){
var randPoint = new Point(seededRand(), seededRand());
var point = new Point(this.shape.bounds.width, this.shape.bounds.height).multiply(randPoint).add(this.shape.bounds.topLeft);
if(this.shape.contains(point) && this.veins.getDist(point)>Leaf.veinR && !this.auxinTooClose(point)){
//if(this.isEmptyAuxinNeighbourhood(point) && this.isEmptyVeinNeighbourhood(point)){
this.auxin.push(new Auxin(point));
f = true;
//tmp.fillColor = 'green';
//Leaf.auxinArea.strokeColor = 'red';
//}
}
}
return f;
}
auxinTooClose(point){
for(var i = 0; i<Leaf.auxin.length; i++){
if(Leaf.auxin[i].pos.getDistance(point)<Leaf.auxinR){
return true;
}
}
return false;
}
removeConsumedAuxin(){
var newauxin = []
for(var i = 0; i<this.auxin.length; i++){
if(this.veins.getDist(this.auxin[i].pos)>Leaf.veinR){
newauxin.push(this.auxin[i]);
}else{
this.auxin[i].remove();
}
}
this.auxin = newauxin;
}
attractVeins(){
for(var i = 0; i<this.auxin.length; i++){
//var closestNode = this.veins.getClosest(this.auxin[i]);
var n = this.veins.getClosestNode(this.auxin[i].pos);
//var p = this.veins.getClosestNodePoint(this.auxin[i].pos);
var b = this.veins.getClosestBranch(this.auxin[i].pos);
n.setInfluence(this.auxin[i]);
//var line = new Path.Line(p, this.auxin[i].pos);
//line.strokeColor = 'grey';
this.auxin[i].node.fillColor = b.path.strokeColor;
}
}
growVeins(){
this.veins.grow();
}
growLeaf(){
var perc = (this.shape.bounds.height+Leaf.leafGrowRate)/this.shape.bounds.height;
this.shape.scale(perc, this.pos)
if(Leaf.uniform){
this.scaleAuxin(perc);
this.scaleBranches(perc);
}
}
scaleAuxin(perc){
for(var i = 0; i<Leaf.auxin.length; i++){
Leaf.auxin[i].scale(perc, this.pos);
}
}
scaleBranches(perc){
for(var i = 0; i<Leaf.branches.length; i++){
Leaf.branches[i].scale(perc, this.pos);
}
}
removeSmallVeins(){
this.veins.removeSmallVeins();
}
calcWidth(){
this.veins.calcWidth();
}
sortChildbranches(){
this.veins.sortChildbranches();
}
rearrangeBranches(){
this.veins.rearrangeBranches();
}
}
class VeinGraph{
constructor(point) {
this.root = new VeinBranch(point);
this.root.addConnection(new Point(point.x, point.y-10));
}
getClosestNode(point){
var b = this.getClosestBranch(point);
return b.getClosestNode(point);
}
getClosestNodePoint(point){
var b = this.getClosestBranch(point);
return b.getClosestNodePoint(point);
}
getClosestBranch(point){
//var b = this.root.getClosestBranch(point, this.root);
var b = this.root;
var dist = this.root.path.getNearestPoint(point).getDistance(point);
var dist2 = 0;
for(var i = 0; i<Leaf.branches.length; i++){
dist2 = Leaf.branches[i].path.getNearestPoint(point).getDistance(point);
if(dist2<dist){
dist = dist2;
b = Leaf.branches[i];
}
}
return b;
}
getDist(point){
var b = this.getClosestBranch(point);
var p = b.getClosestPoint(point);
return p.getDistance(point);
}
grow(){
this.root.grow();
}
removeSmallVeins(){
this.root.removeSmallVeins();
}
calcWidth(){
this.root.calcWidth();
}
sortChildbranches(){
this.root.sortChildbranches();
}
rearrangeBranches(){
this.root.rearrangeBranches();
}
}
class VeinBranch{
constructor(point) {
this.path = new Path();
//var col = Color.random();
//this.path.strokeColor = col;
this.path.strokeColor = 'red';
this.path.strokeCap = 'round';
this.path.strokeWidth = 3;
this.nodes = [];
this.childBranches = [];
if(point){
this.addConnection(point);
}
Leaf.branches.push(this);
}
rearrangeBranches(){
if(this.childBranches.length>0){
var newPath = this.path.splitAt(this.path.getLocationOf(this.childBranches[0].path.firstSegment.point));
//newPath.strokeColor = Color.random();
var newBranch = new VeinBranch();
newBranch.path = newPath;
console.log(newBranch.childBranches);
for(var i = 1; i<this.childBranches.length; i++){
newBranch.childBranches.push(this.childBranches[i]);
}
this.childBranches = [this.childBranches[0]];
this.childBranches.push(newBranch);
console.log(this);
this.childBranches[0].rearrangeBranches();
this.childBranches[1].rearrangeBranches();
}
}
sortChildbranches(){
//console.log(this.childBranches);
this.childBranches.sort((a, b) => (this.path.getOffsetOf(a.path.firstSegment.point) > this.path.getOffsetOf(b.path.firstSegment.point)) ? 1 : -1)
//console.log(this.childBranches);
for(var i = 0; i<this.childBranches.length; i++){
this.childBranches[i].sortChildbranches();
}
}
addConnection(point){
this.path.add(point);
this.nodes.push(new VeinNode());
}
addBranch(point1, point2){
var b = new VeinBranch(point1);
b.addConnection(point2);
this.childBranches.push(b);
}
getClosestPoint(point){
var p = this.path.getNearestPoint(point);
return p;
}
getClosestNode(point){
var dist = this.path.firstSegment.point.getDistance(point);
var node = 0;
for( var i = 1; i<this.path.segments.length; i++){
var compdist = this.path.segments[i].point.getDistance(point);
if(compdist<dist){
node = i;
dist = compdist;
}
}
return this.nodes[node];
}
getClosestNodePoint(point){
var dist = this.path.firstSegment.point.getDistance(point);
var node = 0;
for( var i = 1; i<this.path.segments.length; i++){
var compdist = this.path.segments[i].point.getDistance(point);
if(compdist<dist){
node = i;
dist = compdist;
}
}
return this.path.segments[node].point;
}
/*getClosestBranch(point, veinbranch){
var p = this.path.getNearestPoint(point);
var compdist = point.getDistance(p);
var pvb = veinbranch.path.getNearestPoint(point);
var dist = point.getDistance(pvb);
var smallest = this;
if(dist<compdist){
smallest = veinbranch;
compdist = dist;
}
for(var i = 0; i<this.childBranches.length; i++){
var b = this.childBranches[i].getClosestBranch(point,smallest);
p = b.path.getNearestPoint(point);
dist = point.getDistance(p);
if(dist<compdist){
smallest = this.childBranches[i];
compdist = dist;
}
}
return smallest;
}*/
grow(){
for(var i = 0; i<this.nodes.length; i++){
var n = this.nodes[i].grow(this.path.segments[i].point);
if(n!=null){
var newpoint = this.path.segments[i].point.add(n);
if(i==this.nodes.length-1){
this.addConnection(newpoint);
}else{
this.addBranch(this.path.segments[i].point,newpoint);
}
}
}
for(var i=0; i<this.childBranches.length; i++){
this.childBranches[i].grow();
}
}
scale(perc, pos){
this.path.scale(perc, pos);
}
remove(){
this.path.remove();
}
removeSmallVeins(){
for(var i = this.childBranches.length-1; i>=0; i--){
if(this.childBranches[i].path.length<Leaf.auxinR && this.childBranches[i].childBranches.length==0){
this.childBranches[i].remove();
this.childBranches.splice(i,1);
}else{
this.childBranches[i].removeSmallVeins();
}
}
}
calcWidth(){
if(this.childBranches.length==0){
//this.path.strokeWidth = 10;
//return 10;
this.path.strokeWidth = Leaf.auxinR-7;
this.path.strokeColor = Leaf.color;
return Leaf.auxinR-2;
}
this.path.bringToFront();
var wid = 0;
var col = -12;
for(var i = 0; i<this.childBranches.length; i++){
var w = this.childBranches[i].calcWidth();
wid = wid + Math.pow(w,5);
col += w;
}
wid = Math.pow(wid, 1/5);
this.path.strokeWidth = wid-9;
this.path.strokeColor = Leaf.color;
var innerPath1 = this.path.clone();
var innerPath2 = this.path.clone();
this.path.strokeColor.brightness -= col/127;
innerPath1.strokeWidth = (wid-3)/1.5;
innerPath1.strokeColor.brightness -= col/127;
innerPath2.strokeWidth = (wid-3)/2-2;
innerPath2.strokeColor.brightness -= col/16;
Leaf.midlayerItems.push(innerPath1);
Leaf.frontlayerItems.push(innerPath2);
return wid;
}
}
class VeinNode{
constructor() {
this.auxinInfluence = [];
}
grow(pos){
//
if(this.auxinInfluence.length>0){
var direction = new Point(0,0);
var oldDists = [];
for(var i = 0; i<this.auxinInfluence.length; i++){
var dir = this.auxinInfluence[i].pos.subtract(pos).normalize();
direction = direction.add(dir);
oldDists.push(this.auxinInfluence[i].pos.getDistance(pos));
}
if(direction.length<1){
return null;
}
direction = direction.normalize(Leaf.veinGrowRate);
this.auxinInfluence = [];
return direction;
}
this.auxinInfluence = [];
return null;
}
setInfluence(point){
this.auxinInfluence.push(point);
}
}