<
PrototypeJungle
>

Drop Forest

This module will be introduced via annotated code, which generates the image

Here is the code:


import {rs as linePP} from '/shape/line.mjs';
import {rs as basicP} from '/generators/basics.mjs';
import {rs as addDropMethods} from '/mlib/drop.mjs';
import {rs as addForestDropMethods} from '/mlib/dropForest.mjs';

let rs = basicP.instantiate();
addDropMethods(rs);
addForestDropMethods(rs);

rs.setName('drop_dandelion');
let ht = 360;
let wd = 1* ht;

let topParams = {width:wd,height:ht,framePadding:0.15*ht,stayWithin:Circle.mk(Point.mk(0,0),0.5*ht)};  

let forestDropParams = {fromEnds:1, extendWhich:'first', sepNext:0.01, dropTries:10,
  sepNext:0.1, maxDrops:Infinity, splitChance:.40, splitAmount:0.05*Math.PI, 
  directionChange:0.0*Math.PI, randomDirectionChange:0.025*Math.PI, segLength:5,maxDrops:10000};

let ringParams = {numSeeds:15,segLength:5,ringRadius:0.15 * 0.5 * wd};

Object.assign(rs,topParams);

rs.initProtos = function () {
  this.lineP = linePP.instantiate();
  this.lineP.stroke = 'white';
  this.lineP['stroke-width'] = .5;
}

rs.initialForestDrop = function () {
  let segs = this.ringSeeds(ringParams); 
  let lines = segs.map((sg) => this.genLine(sg,this.lineP)); 
  return {geometries:segs,shapes:lines};
}

rs.generateForestDrop = function (p) {
  let sw = this.stayWithin;
  let segs = this.generateFan(p);
  let isegs = [];
  segs.forEach( (s)=>  {
    if (sw.contains(s,p)) {
      isegs.push(s);
    }
  });
  let lines = isegs.map( s => this.genLine(s,this.lineP,forestDropParams.lineExt));
  return {geometries:isegs,shapes:lines};
}

rs.initialize = function () {
  this.initProtos();
  this.addFrame();
  this.generateForestDrops(forestDropParams);
}

export {rs};

This module resembles the drop module, except that only LineSegments may be dropped by dropGenerator, and that these segments are dropped in such a way as to extend an already existing tree. In this mode, illustrated by the dandelion image just above, the current state consists of a set of trees (a forest) of segments. Each segment in the forest is either interior, meaning that its end1 has been continued by one or more segments, or terminal, meaning that there is no continuing segment emerging from its end1.

Here is an explanation of the parameters:

dropTries:number : if this number of consectutive drops fail due to collision, terminate the algorithm.

maxDrops:number: the maximum number of drop trials

Note generateForestDrop, relies on generateFan to do the core of its work:


rs.generateForestDrop = function (p) {
  let sw = this.stayWithin;
  let segs = this.generateFan(p);
  ...
  return {geometries:isegs,shapes:lines};
}

generateFan, which returns an array of LineSegments extends the tree by either one or two segments (controlled by splitChance). The segments, in either case, emanate from startingPoint, which will be a terminal node of the existing tree. In the latter case, it produces a branching of the tree, and in the former case not. Parameters to generateFan are:

segLength:number: the length of segments dropped.

extendWhich:string: which of the terminal nodes to extend. Possible values 'first', meaning the earliest added terminal node that has not yet been extended, 'random' meaning a randomly selected terminal node, and 'last' meaning the last added terminal node.

sepNext: the separation between the terminal node and the new segment(s)

splitChance: the chance that a split (two segments) rather than a linear continuation (one segment), will be chosen.

splitAmount: if a split is chosen, the angular difference between the two new segments.

directionChange: the direction of the new segment, or center of the new branch, relative to the terminal segment being extended

randomDirectionChange: a random value to be added to directionChange is chosen at random from the interval [[-randomDirectionChange,randomDirectionChange]]

Note that initialForestDrop relies on ringSeeds to do its work:


rs.initialForestDrop = function () {
  let segs = this.ringSeeds(ringParams); 
  let lines = segs.map((sg) => this.genLine(sg,this.lineP)); 
  return {geometries:segs,shapes:lines};
}

ringSeeds returns an array of LineSegments which play the role of seeds for starting the construction of a tree, thence the name. The parameters to ringSeeds are:

numSeeds:number: ringSeeds generates a ring of outwardly directed segments, evenly placed around a ring. numSeeds is their count.

segLength:number: the length of segments dropped.

ringRadius:number: self-explanatory