Procedural Cacti
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1); // Global code will be evaluated once. const turtle = new Turtle(); const cacti = 25; // min=1 max=100 step=1 const columns = 5; //min=1 max=10 step=1 const startX = -75; //min=-100 max=100 step=5 const startY = 95; //min=-100 max=100 step=5 const offsetX = 37; //min=-100 max=100 step=1 const offsetY = 37; //min=-100 max=100 step=1 const trunkThickness = 4; //min=1 max=15 step=.1 const branchThickness = 2; //min=1 max=15 step=.1 const trunkHeightMin = 15; //min=5 max=100 step=1 const trunkHeightMax = 30; //min=5 max=100 step=1 const branchHeightMin = 2; //min=0 max=25 step=1 const branchHeightMax = 10; //min=0 max=25 step=1 const branchWidthMin = 2; //min=0 max=25 step=1 const branchWidthMax = 7; //min=0 max=25 step=1 const firstBranchMin = 5; //min=0 max=25 step=1 const firstBranchMax = 10; //min=0 max=25 step=1 const branchSpacingMin = 3; //min=0 max=25 step=1 const branchSpacingMax = 15; //min=0 max=25 step=1 const needleDensity = 0; //min=0 max=500 step=10 const needlesPerBunch = 10; //min=0 max=25 step=1 const centerline = 0; // min=0, max=1, step=1, (No Centerline, Centerline) const cullTallBranches = 1; //min=0, max=1, step=1 (Leave Tall Branches, Cull Tall Branches) function newCactus(x,y, bSpacing, bWidth, bHeight, bCull, bThickness,) { //Trunk var cactus = { x: x, y: y, height: randomRange([trunkHeightMin,trunkHeightMax]), thickness: trunkThickness, branches: [[],[]], //Left, Right density: needleDensity, bunchSize: needlesPerBunch } //Branches var branchSide = 0; while (branchSide <= 1) { var trunkOffset = randomRange([firstBranchMin,firstBranchMax]); while (trunkOffset < cactus.height) { var branch = { y: trunkOffset, side: branchSide, thickness: branchThickness, width: randomRange([branchWidthMin,branchWidthMax]) + ( cactus.thickness / 2 ), height: randomRange([branchHeightMin,branchHeightMax]) } if ((trunkOffset + branch.height) < cactus.height || !cullTallBranches){ if ( branchSide == 0 ) cactus.branches[branchSide].push(branch) else cactus.branches[branchSide].unshift(branch) } trunkOffset += branch.height + randomRange([branchSpacingMin,branchSpacingMax]) } branchSide++; } return cactus; } function drawCactus(cactus){ //start at the bottom left of the trunk pointing up turtle.penup(); turtle.jump(cactus.x - cactus.thickness / 2, cactus.y); turtle.seth(-90); turtle.pendown(); for (var branch of cactus.branches[0]){ drawBranch(cactus, branch); } turtle.goto(turtle.x(), cactus.y - cactus.height); turtle.seth(-90); turtle.circle(cactus.thickness/2,180); for (var branch of cactus.branches[1]){ drawBranch(cactus, branch); } turtle.goto(turtle.x(),cactus.y); if (cactus.density) drawNeedles(cactus); if (cactus.centerline) { drawTrunkC(cactus) for (var branches of cactus.branches) { for (var branch of branches){ drawBranchC(cactus, branch) } } } } function drawBranch(cactus, branch) { if (branch.side == 0) { turtle.goto(turtle.x(), cactus.y - branch.y + branch.thickness / 2 ); turtle.goto(cactus.x - branch.width, turtle.y()); turtle.seth(180); turtle.circle(branch.thickness / 2,90); turtle.goto(turtle.x(), turtle.y() - branch.height); turtle.circle(branch.thickness/2,180); turtle.goto(turtle.x(), turtle.y() + branch.height - branch.thickness /2); turtle.goto(cactus.x - cactus.thickness / 2, turtle.y()); } else { turtle.goto(turtle.x(), cactus.y - branch.y - branch.thickness / 2); turtle.goto(cactus.x + branch.width - branch.thickness/2, turtle.y()); turtle.goto(turtle.x(), turtle.y() - branch.height + branch.thickness / 2); turtle.seth(-90); turtle.circle(branch.thickness/2,180); turtle.goto(turtle.x(), turtle.y() + branch.height); turtle.circle(branch.thickness/2, 90) turtle.goto(cactus.x + cactus.thickness / 2, turtle.y()); } } function drawTrunkC(cactus){ turtle.penup(); turtle.jump(cactus.x, cactus.y); turtle.pendown(); turtle.seth(-90); turtle.forward(cactus.height); } function drawBranchC(cactus, branch) { turtle.penup(); turtle.jump(cactus.x, cactus.y - branch.y); turtle.pendown(); turtle.setheading(180 * (1-branch.side)); turtle.forward(branch.width); turtle.setheading(-90); turtle.forward(branch.height); } function drawNeedles(cactus) { //trunk drawNeedlesSegment( [cactus.x,cactus.y - cactus.thickness / 4], [cactus.x,cactus.y - cactus.height], 270, cactus.thickness, cactus.density, cactus.bunchSize ); //arms for(branchSide of cactus.branches){ for(branch of branchSide) { var side = branch.side * 2 - 1 drawNeedlesSegment( [ cactus.x + cactus.thickness / 2 * side, cactus.y - branch.y ], [ cactus.x + branch.width * side, cactus.y - branch.y ], 270 - side * 90, branch.thickness, cactus.density, cactus.bunchSize ); drawNeedlesSegment( [ cactus.x + branch.width * side, cactus.y - branch.y ], [ cactus.x + branch.width * side, cactus.y - branch.y - branch.height], 270, branch.thickness, cactus.density, cactus.bunchSize ); } } } function drawNeedlesSegment(start,end,direction,thickness,density,bunchSize){ console.log(start,end,direction,thickness,density,bunchSize); var length = Math.hypot(start[0] - end[0], start[1] - end[1]); var needles = Math.floor(length * density / 100); var needleRange = [thickness/8,thickness/2]; var needleLength = [thickness/4,thickness/2]; while(needles >= 1) { var t = Math.random(); var pos = { x: start[0] + t * (end[0] - start[0]), y: start[1] + t * (end[1] - start[1]) }; drawBunch( randomRange([pos.x - thickness/2, pos.x + thickness/2]), randomRange([pos.y - thickness/2, pos.y + thickness/2]), randomRange([direction-45,direction+45]), bunchSize, needleLength ) needles--; } } function drawBunch(x,y,direction,bunchSize, needleLength) { while(bunchSize >= 1){ drawNeedle( x, y, randomRange([direction-60,direction+60]), randomRange(needleLength), ) bunchSize--; } } function drawNeedle(x,y,h,l) { console.log(x,y,h,l); turtle.jump(x,y); turtle.seth(h); turtle.pendown(); turtle.forward(l); turtle.penup(); } function randomRange(range) { return Math.round(Math.random() * (range[1] - range[0])) + range[0]; } function walk(i) { var cactus = newCactus( startX + i%columns * offsetX , //x startY - Math.floor(i/columns) * offsetY, //y ); drawCactus(cactus); return i < (cacti -1); }