├── LICENSE ├── README.md ├── TreeD.html ├── TreeD.js ├── index.html └── main.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chris Harrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TreeGenerator 2 | Generates a pretty tree in the browser using adjustable parameters. 3 | 4 | Check it out [here,](http://someuser-321.github.io/TreeGenerator/) or in 3D [here!](http://someuser-321.github.io/TreeGenerator/TreeD.html) 5 | -------------------------------------------------------------------------------- /TreeD.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TreeD thing 5 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TreeD.js: -------------------------------------------------------------------------------- 1 | var slider_size, 2 | slider_level, 3 | slider_rot, 4 | slider_lenRand, 5 | slider_branchProb, 6 | slider_rotRand, 7 | slider_flowerProb; 8 | 9 | var button_seed, 10 | button_newSeed, 11 | button_randomParams, 12 | button_change, 13 | button_rotation; 14 | 15 | var label_size, 16 | label_level, 17 | label_rot, 18 | label_lenRand, 19 | label_branchProb, 20 | label_rotRand, 21 | label_flowerProb, 22 | label_perf, 23 | label_seed, 24 | label_source, 25 | label_source2; 26 | 27 | var div_inputs; 28 | 29 | var input_seed; 30 | 31 | var size, 32 | maxLevel, 33 | rot, 34 | lenRan, 35 | branchProb, 36 | rotRand, 37 | flowerProb; 38 | 39 | var hide = false, 40 | prog = 1, 41 | growing = false, 42 | mutating = false, 43 | randSeed = 80, 44 | paramSeed = Math.floor(Math.random() * 1000), 45 | randBias = 0; 46 | 47 | var mouseX_, 48 | mouseY_, 49 | rotateX_ = 0, 50 | rotateY_ = 0, 51 | zoom = 2, 52 | doRotate = true; 53 | 54 | function setup() 55 | { 56 | createCanvas(window.innerWidth, window.innerHeight, WEBGL); 57 | 58 | var fov = 60 / 180 * PI; 59 | var cameraZ = (height / 2) / tan(fov / 2); 60 | perspective(60 / 180 * PI, width / height, cameraZ * 0.001, cameraZ * 100); 61 | 62 | rotateX_ = PI/8; 63 | 64 | slider_size = createSlider(0, 1, 0.5, 0.01); 65 | slider_size.position(10, 10); 66 | slider_level = createSlider(1, 12, 9, 1); 67 | slider_level.position(10, 30); 68 | slider_rot = createSlider(0, 1, 0.125, 1 / (3 * 5 * 8)); 69 | slider_rot.position(10, 50); 70 | slider_lenRand = createSlider(0, 1, 0.5, 0.01); 71 | slider_lenRand.position(10, 70); 72 | slider_branchProb = createSlider(0, 1, 0.98, 0.01); 73 | slider_branchProb.position(10, 90); 74 | slider_rotRand = createSlider(0, 1, 0.4, 0.01); 75 | slider_rotRand.position(10, 110); 76 | slider_flowerProb = createSlider(0, 1, 0.5, 0.01); 77 | slider_flowerProb.position(10, 130); 78 | 79 | slider_size.input(function(){readInputs(true)}); 80 | slider_level.input(function(){readInputs(true)}); 81 | slider_rot.input(function(){readInputs(true)}); 82 | slider_lenRand.input(function(){readInputs(true)}); 83 | slider_branchProb.input(function(){readInputs(true)}); 84 | slider_rotRand.input(function(){readInputs(true)}); 85 | slider_flowerProb.input(function(){readInputs(true)}); 86 | 87 | 88 | label_size = createSpan('Size'); 89 | label_size.position(150, 10); 90 | label_level = createSpan('Recursion level'); 91 | label_level.position(150, 30); 92 | label_rot = createSpan('Split angle'); 93 | label_rot.position(150, 50); 94 | label_lenRand = createSpan('Length variation'); 95 | label_lenRand.position(150, 70); 96 | label_branchProb = createSpan('Split probability'); 97 | label_branchProb.position(150, 90); 98 | label_rotRand = createSpan('Split rotation variation'); 99 | label_rotRand.position(150, 110); 100 | label_flowerProb = createSpan('Flower probability'); 101 | label_flowerProb.position(150, 130); 102 | 103 | label_seed = createSpan('Seed:'); 104 | label_seed.position(10, 162); 105 | 106 | input_seed = createInput(randSeed); 107 | input_seed.position(50, 160); 108 | input_seed.style('width', '50px') 109 | 110 | button_seed = createButton('Watch it grow!'); 111 | button_seed.position(110, 160); 112 | button_seed.mousePressed(function() { 113 | randSeed = input_seed.value(); 114 | startGrow(); 115 | }); 116 | 117 | button_newSeed = createButton('Generate new tree'); 118 | button_newSeed.position(10, 190); 119 | button_newSeed.mousePressed(function(){ 120 | randSeed = Math.floor(Math.random() * 1000); 121 | prog = 100; 122 | input_seed.value(randSeed); 123 | startGrow(); 124 | }); 125 | 126 | button_randomParams = createButton('Randomise parameters'); 127 | button_randomParams.position(10, 220); 128 | button_randomParams.mousePressed(function(){ 129 | randomSeed(paramSeed); 130 | 131 | slider_level.value(1 * slider_level.value() + rand2() * slider_level.attribute('step')); 132 | slider_rot.value(1 * slider_rot.value() + 5 * rand2() * slider_rot.attribute('step')); 133 | slider_lenRand.value(1 * slider_lenRand.value() + 5 * rand2() * slider_lenRand.attribute('step')); 134 | slider_branchProb.value(1 * slider_branchProb.value() + 5 * rand2() * slider_branchProb.attribute('step')); 135 | slider_rotRand.value(1 * slider_rotRand.value() + 5 * rand2() * slider_rotRand.attribute('step')); 136 | slider_flowerProb.value(1 * slider_flowerProb.value() + 5 * rand2() * slider_flowerProb.attribute('step')); 137 | 138 | paramSeed = 1000 * random(); 139 | randomSeed(randSeed); 140 | 141 | readInputs(true); 142 | }); 143 | 144 | button_change = createButton('Enable wind'); 145 | button_change.position(10, 250); 146 | button_change.mousePressed(function(){ 147 | if ( !mutating ) 148 | { 149 | button_change.html('Disable wind') 150 | mutateTime = millis(); 151 | mutating = true; 152 | mutate(); 153 | } 154 | else 155 | { 156 | button_change.html('Enable wind') 157 | randBias = 0; 158 | mutating = false; 159 | } 160 | }); 161 | 162 | button_rotation = createButton('Disable rotation'); 163 | button_rotation.position(10, 280); 164 | button_rotation.mousePressed(function(){ 165 | if ( doRotate ) 166 | button_rotation.html('Enable rotation') 167 | else 168 | button_rotation.html('Disable rotation') 169 | 170 | doRotate = !doRotate; 171 | }); 172 | 173 | button_hide = createButton('Hide UI (click this area to show again)'); 174 | button_hide.position(10, 310); 175 | button_hide.mousePressed(hideUI); 176 | 177 | label_perf = createSpan('Generated in #ms'); 178 | label_perf.position(10, 340); 179 | //label_perf.style('display', 'none'); 180 | 181 | label_source = createA('https://github.com/someuser-321/TreeGenerator', 'Check it out on GitHub!'); 182 | label_source.position(10, 360); 183 | label_source2 = createA('https://someuser-321.github.io/TreeGenerator/index.html', 'Original 2D Version'); 184 | label_source2.position(10, 380); 185 | 186 | div_inputs = createDiv(''); 187 | 188 | mouseX_ = mouseX; 189 | mouseY_ = mouseY; 190 | 191 | button_change.html('Disable wind') 192 | mutateTime = millis(); 193 | mutating = true; 194 | mutate(); 195 | 196 | readInputs(false); 197 | startGrow(); 198 | } 199 | 200 | function mouseDragged() 201 | { 202 | if ( mouseX < 330 && mouseY < 400 ) 203 | return true; 204 | 205 | rotateX_ += (mouseY - mouseY_) / 500; 206 | rotateY_ += (mouseX - mouseX_) / 500; 207 | 208 | mouseX_ = mouseX; 209 | mouseY_ = mouseY; 210 | 211 | loop(); 212 | 213 | return false; 214 | } 215 | 216 | function mouseMoved() 217 | { 218 | mouseX_ = mouseX; 219 | mouseY_ = mouseY; 220 | 221 | if ( mouseX > 330 || mouseY > 400 ) 222 | return false; 223 | } 224 | 225 | function mouseClicked() 226 | { 227 | if ( mouseX > 330 || mouseY > 400 ) 228 | return false; 229 | } 230 | 231 | function mouseWheel(event) 232 | { 233 | zoom *= (event.delta > 0 ? 1.1 : 1 / 1.1); 234 | loop(); 235 | 236 | return false; 237 | } 238 | 239 | function mouseReleased() 240 | { 241 | if ( mouseX > 330 || mouseY > 400 ) 242 | return false; 243 | 244 | if ( hide ) 245 | showUI(); 246 | hide = !hide; 247 | 248 | } 249 | 250 | function touchEnded() 251 | { 252 | if ( hide ) 253 | showUI(); 254 | hide = !hide; 255 | 256 | return false; 257 | } 258 | 259 | function showUI() 260 | { 261 | slider_size.style('visibility', 'initial'); 262 | slider_level.style('visibility', 'initial'); 263 | slider_rot.style('visibility', 'initial'); 264 | slider_lenRand.style('visibility', 'initial'); 265 | slider_branchProb.style('visibility', 'initial'); 266 | slider_rotRand.style('visibility', 'initial'); 267 | slider_flowerProb.style('visibility', 'initial'); 268 | 269 | button_seed.style('visibility', 'initial'); 270 | button_newSeed.style('visibility', 'initial'); 271 | button_randomParams.style('visibility', 'initial'); 272 | button_change.style('visibility', 'initial'); 273 | button_rotation.style('visibility', 'initial'); 274 | button_hide.style('visibility', 'initial'); 275 | 276 | label_size.style('visibility', 'initial'); 277 | label_level.style('visibility', 'initial'); 278 | label_rot.style('visibility', 'initial'); 279 | label_lenRand.style('visibility', 'initial'); 280 | label_branchProb.style('visibility', 'initial'); 281 | label_rotRand.style('visibility', 'initial'); 282 | label_flowerProb.style('visibility', 'initial'); 283 | label_perf.style('visibility', 'initial'); 284 | label_seed.style('visibility', 'initial'); 285 | label_source.style('visibility', 'initial'); 286 | label_source2.style('visibility', 'initial'); 287 | 288 | input_seed.style('visibility', 'initial'); 289 | 290 | div_inputs.style('visibility', 'initial'); 291 | } 292 | 293 | function hideUI() 294 | { 295 | slider_size.style('visibility', 'hidden'); 296 | slider_level.style('visibility', 'hidden'); 297 | slider_rot.style('visibility', 'hidden'); 298 | slider_lenRand.style('visibility', 'hidden'); 299 | slider_branchProb.style('visibility', 'hidden'); 300 | slider_rotRand.style('visibility', 'hidden'); 301 | slider_flowerProb.style('visibility', 'hidden'); 302 | 303 | button_seed.style('visibility', 'hidden'); 304 | button_newSeed.style('visibility', 'hidden'); 305 | button_randomParams.style('visibility', 'hidden'); 306 | button_change.style('visibility', 'hidden'); 307 | button_rotation.style('visibility', 'hidden'); 308 | button_hide.style('visibility', 'hidden'); 309 | 310 | label_size.style('visibility', 'hidden'); 311 | label_level.style('visibility', 'hidden'); 312 | label_rot.style('visibility', 'hidden'); 313 | label_lenRand.style('visibility', 'hidden'); 314 | label_branchProb.style('visibility', 'hidden'); 315 | label_rotRand.style('visibility', 'hidden'); 316 | label_flowerProb.style('visibility', 'hidden'); 317 | label_perf.style('visibility', 'hidden'); 318 | label_seed.style('visibility', 'hidden'); 319 | label_source.style('visibility', 'hidden'); 320 | label_source2.style('visibility', 'hidden'); 321 | 322 | input_seed.style('visibility', 'hidden'); 323 | 324 | div_inputs.style('visibility', 'hidden'); 325 | } 326 | 327 | function readInputs(updateTree) 328 | { 329 | size = slider_size.value(); 330 | maxLevel = slider_level.value(); 331 | rot = slider_rot.value(); 332 | lenRand = slider_lenRand.value(); 333 | branchProb = slider_branchProb.value(); 334 | rotRand = slider_rotRand.value(); 335 | flowerProb = slider_flowerProb.value(); 336 | 337 | if ( updateTree && !growing ) 338 | { 339 | prog = maxLevel + 1; 340 | loop(); 341 | } 342 | } 343 | 344 | function mutate() 345 | { 346 | var startTime = millis(); 347 | randomSeed(paramSeed); 348 | 349 | var n = noise(startTime / 20000) - 0.5; 350 | 351 | randBias = 4 * Math.abs(n) * n * (mutating ? 1 : 0); 352 | 353 | paramSeed = 1000 * random(); 354 | randomSeed(randSeed); 355 | readInputs(true); 356 | 357 | var diff = millis() - startTime; 358 | 359 | if ( diff < 20 ) 360 | setTimeout(mutate, 20 - diff); 361 | else 362 | setTimeout(mutate, 1); 363 | } 364 | 365 | function windowResized() 366 | { 367 | resizeCanvas(windowWidth, windowHeight); 368 | 369 | var fov = 60 / 180 * PI; 370 | var cameraZ = (height / 2) / tan(fov / 2); 371 | perspective(60 / 180 * PI, width / height, cameraZ * 0.1, cameraZ * 10); 372 | 373 | loop(); 374 | } 375 | 376 | function draw() 377 | { 378 | var startFrameTime = millis(); 379 | background(0, 0, 0); 380 | 381 | 382 | scale(1, -1); 383 | 384 | /* 385 | ambientMaterial(255, 0, 0); 386 | push(); 387 | translate(100, 0, 0); 388 | box(200, 10, 10); 389 | pop(); 390 | 391 | ambientMaterial(0, 255, 0); 392 | push(); 393 | translate(0, 100, 0); 394 | box(10, 200, 10); 395 | pop(); 396 | 397 | ambientMaterial(0, 0, 255); 398 | push(); 399 | translate(0, 0, 100); 400 | box(10, 10, 200); 401 | pop(); 402 | */ 403 | 404 | translate(0, -height * (size+0.25), -zoom * height * size); 405 | 406 | 407 | rotate(-rotateX_, [1, 0, 0]); 408 | rotate(rotateY_, [0, 1, 0]); 409 | 410 | push(); 411 | rotate(-PI/2, [1, 0, 0]); 412 | ambientMaterial(255, 255, 255); 413 | plane(1000, -1000); 414 | pop(); 415 | 416 | ambientLight(20); 417 | pointLight(255, 255, 255, 1000, 1000, 1000); 418 | 419 | 420 | /* 421 | ambientMaterial(255, 0, 0); 422 | push(); 423 | translate(100, 0, 0); 424 | box(200, 5, 5); 425 | pop(); 426 | 427 | ambientMaterial(0, 255, 0); 428 | push(); 429 | translate(0, 100, 0); 430 | box(5, 200, 5); 431 | pop(); 432 | 433 | ambientMaterial(0, 0, 255); 434 | push(); 435 | translate(0, 0, 100); 436 | box(5, 5, 200); 437 | pop(); 438 | */ 439 | 440 | branch(1, randSeed); 441 | 442 | var frameTime = (millis() - startFrameTime); 443 | label_perf.html('Generated in ' + Math.floor(frameTime * 10) / 10 + 'ms'); 444 | 445 | var frameTime_ = Math.max(frameTime/1000, 1/(frameRate()||60)); 446 | 447 | if ( doRotate ) 448 | rotateY_ += 1/180 * 2*PI * frameTime_; 449 | 450 | noLoop(); 451 | } 452 | 453 | function branch(level, seed) 454 | { 455 | if ( prog < level ) 456 | return; 457 | 458 | randomSeed(seed); 459 | 460 | var seed1 = random(1000), 461 | seed2 = random(1000); 462 | 463 | var growthLevel = (prog - level > 1) || (prog >= maxLevel + 1) ? 1 : (prog - level); 464 | 465 | var width = 50 * size * Math.pow((maxLevel - level + 1) / maxLevel, 2); 466 | var len = growthLevel * size * height * (1 + rand2() * lenRand); 467 | 468 | translate(0, (len / level) / 2, 0); 469 | 470 | ambientMaterial(255, 255, 255); 471 | box(width, len / level, width); 472 | 473 | translate(0, (len / level) / 2, 0); 474 | 475 | var doBranch1 = rand() < branchProb; 476 | var doBranch2 = rand() < branchProb; 477 | 478 | var doFlowers = rand() < flowerProb && prog >= maxLevel && level >= maxLevel; 479 | 480 | if ( level < maxLevel ) 481 | { 482 | var r11 = rot * PI * (1 + rand2() * rotRand + randWind()); 483 | var r12 = rot * PI * (1 + rand2() * rotRand + randWind()); 484 | var r21 = rot * PI * (1 + rand2() * rotRand - randWind()); 485 | var r22 = rot * PI * (1 + rand2() * rotRand - randWind()); 486 | 487 | if ( doBranch1 ) 488 | { 489 | push(); 490 | rotate(PI/2 + r11, [0, 1, 0]); 491 | rotate(r12, [1, 0, 0]); 492 | branch(level + 1, seed1); 493 | pop(); 494 | } 495 | if ( doBranch2 ) 496 | { 497 | push(); 498 | rotate(PI/2 + r21, [0, 1, 0]); 499 | rotate(-r22, [1, 0, 0]); 500 | branch(level + 1, seed2); 501 | pop(); 502 | } 503 | } 504 | 505 | if ( doFlowers ) 506 | { 507 | ambientMaterial(255, 150, 150); 508 | var flowerSize = rand() * growthLevel * size * 50; 509 | for ( var i=0 ; i<4 ; i++ ) 510 | { 511 | rotate(PI/4, [1, 0, 0]); 512 | rotate(2 * PI * i/4, [0, 0, 1]) 513 | 514 | box(2, flowerSize, 2); 515 | } 516 | } 517 | } 518 | 519 | function startGrow() 520 | { 521 | growing = true; 522 | prog = 1; 523 | grow(); 524 | } 525 | 526 | function grow() 527 | { 528 | if ( prog > (maxLevel + 3) ) 529 | { 530 | prog = maxLevel + 3; 531 | loop(); 532 | growing = false; 533 | return; 534 | } 535 | 536 | var startTime = millis(); 537 | loop(); 538 | var diff = millis() - startTime; 539 | 540 | prog += maxLevel / 8 * Math.max(diff, 20) / 1000; 541 | setTimeout(grow, Math.max(1, 20 - diff)); 542 | } 543 | 544 | 545 | function rand() 546 | { 547 | return random(1000) / 1000; 548 | } 549 | 550 | function rand2() 551 | { 552 | return random(2000) / 1000 - 1; 553 | } 554 | 555 | function randWind() 556 | { 557 | return /*rand2() **/ randBias; 558 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tree thing 5 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | var slider_size, 2 | slider_level, 3 | slider_rot, 4 | slider_lenRand, 5 | slider_branchProb, 6 | slider_rotRand, 7 | slider_leafProb; 8 | 9 | var button_seed, 10 | button_newSeed, 11 | button_randomParams, 12 | button_change; 13 | 14 | var label_size, 15 | label_level, 16 | label_rot, 17 | label_lenRand, 18 | label_branchProb, 19 | label_rotRand, 20 | label_leafProb, 21 | label_perf, 22 | label_seed, 23 | label_source, 24 | label_source2; 25 | 26 | var div_inputs; 27 | 28 | var input_seed, 29 | size, 30 | maxLevel, 31 | rot, 32 | lenRan, 33 | branchProb, 34 | rotRand, 35 | leafProb; 36 | 37 | var hide = false, 38 | prog = 1, 39 | growing = false, 40 | mutating = false, 41 | randSeed = 80, 42 | paramSeed = Math.floor(Math.random()*1000), 43 | randBias = 0; 44 | 45 | 46 | function setup() 47 | { 48 | createCanvas(window.innerWidth, window.innerHeight); 49 | 50 | slider_size = createSlider(100, 200, /mobile/i.test(window.navigator.userAgent) ? 100 : 150, 1); 51 | slider_size.position(10, 10); 52 | slider_level = createSlider(1, 13, 11, 1); 53 | slider_level.position(10, 30); 54 | slider_rot = createSlider(0, PI/2, (PI/2) / 4, (PI/2) / (3 * 5 * 8)); 55 | slider_rot.position(10, 50); 56 | slider_lenRand = createSlider(0, 1, 1, 0.01); 57 | slider_lenRand.position(10, 70); 58 | slider_branchProb = createSlider(0, 1, 0.9, 0.01); 59 | slider_branchProb.position(10, 90); 60 | slider_rotRand = createSlider(0, 1, 0.1, 0.01); 61 | slider_rotRand.position(10, 110); 62 | slider_leafProb = createSlider(0, 1, 0.5, 0.01); 63 | slider_leafProb.position(10, 130); 64 | 65 | slider_size.input(function(){readInputs(true)}); 66 | slider_level.input(function(){readInputs(true)}); 67 | slider_rot.input(function(){readInputs(true)}); 68 | slider_lenRand.input(function(){readInputs(true)}); 69 | slider_branchProb.input(function(){readInputs(true)}); 70 | slider_rotRand.input(function(){readInputs(true)}); 71 | slider_leafProb.input(function(){readInputs(true)}); 72 | 73 | 74 | label_size = createSpan('Size'); 75 | label_size.position(150, 10); 76 | label_level = createSpan('Recursion level'); 77 | label_level.position(150, 30); 78 | label_rot = createSpan('Split angle'); 79 | label_rot.position(150, 50); 80 | label_lenRand = createSpan('Length variation'); 81 | label_lenRand.position(150, 70); 82 | label_branchProb = createSpan('Split probability'); 83 | label_branchProb.position(150, 90); 84 | label_rotRand = createSpan('Split rotation variation'); 85 | label_rotRand.position(150, 110); 86 | label_leafProb = createSpan('Flower probability'); 87 | label_leafProb.position(150, 130); 88 | 89 | label_seed = createSpan('Seed:'); 90 | label_seed.position(10, 162); 91 | 92 | input_seed = createInput(randSeed); 93 | input_seed.position(50, 160); 94 | input_seed.style('width', '50px') 95 | 96 | button_seed = createButton('Watch it grow!'); 97 | button_seed.position(110, 160); 98 | button_seed.mousePressed(function() { 99 | randSeed = input_seed.value(); 100 | startGrow(); 101 | }); 102 | 103 | button_newSeed = createButton('Generate new tree'); 104 | button_newSeed.position(10, 190); 105 | button_newSeed.mousePressed(function(){ 106 | randSeed = Math.floor(Math.random() * 1000); 107 | prog = 100; 108 | input_seed.value(randSeed); 109 | startGrow(); 110 | }); 111 | 112 | button_randomParams = createButton('Randomise parameters'); 113 | button_randomParams.position(10, 220); 114 | button_randomParams.mousePressed(function(){ 115 | randomSeed(paramSeed); 116 | 117 | slider_level.value(1 * slider_level.value() + 4 * rand2() * slider_level.attribute('step')); 118 | slider_rot.value(1 * slider_rot.value() + 4 * rand2() * slider_rot.attribute('step')); 119 | slider_lenRand.value(1 * slider_lenRand.value() + 4 * rand2() * slider_lenRand.attribute('step')); 120 | slider_branchProb.value(1 * slider_branchProb.value() + 4 * rand2() * slider_branchProb.attribute('step')); 121 | slider_rotRand.value(1 * slider_rotRand.value() + 4 * rand2() * slider_rotRand.attribute('step')); 122 | slider_leafProb.value(1 * slider_leafProb.value() + 4 * rand2() * slider_leafProb.attribute('step')); 123 | 124 | paramSeed = 1000 * random(); 125 | randomSeed(randSeed); 126 | 127 | readInputs(true); 128 | }); 129 | 130 | button_change = createButton('Enable wind'); 131 | button_change.position(10, 250); 132 | button_change.mousePressed(function(){ 133 | if ( !mutating ) 134 | { 135 | button_change.html('Disable wind') 136 | mutateTime = millis(); 137 | mutating = true; 138 | mutate(); 139 | } 140 | else 141 | { 142 | button_change.html('Enable wind') 143 | mutating = false; 144 | } 145 | }); 146 | 147 | button_hide = createButton('Hide UI (click this area to show again)'); 148 | button_hide.position(10, 280); 149 | button_hide.mousePressed(hideUI); 150 | 151 | label_perf = createSpan('Generated in #ms'); 152 | label_perf.position(10, 310); 153 | //label_perf.style('display', 'none'); 154 | 155 | label_source = createA('https://github.com/someuser-321/TreeGenerator', 'Check it out on GitHub!'); 156 | label_source.position(10, 330); 157 | label_source2 = createA('https://someuser-321.github.io/TreeGenerator/TreeD.html', 'See me in 3D!'); 158 | label_source2.position(10, 350); 159 | 160 | div_inputs = createDiv(''); 161 | 162 | mX = mouseX; 163 | mY = mouseY; 164 | panX = 0; 165 | panY = 0; 166 | 167 | readInputs(false); 168 | startGrow(); 169 | } 170 | 171 | function mouseReleased() 172 | { 173 | if ( mouseX > 330 || mouseY > 400 ) 174 | return false; 175 | 176 | if ( hide ) 177 | showUI(); 178 | hide = !hide; 179 | } 180 | 181 | function touchEnded() 182 | { 183 | if ( hide ) 184 | showUI(); 185 | hide = !hide; 186 | 187 | return false; 188 | } 189 | 190 | function showUI() 191 | { 192 | slider_size.style('visibility', 'initial'); 193 | slider_level.style('visibility', 'initial'); 194 | slider_rot.style('visibility', 'initial'); 195 | slider_lenRand.style('visibility', 'initial'); 196 | slider_branchProb.style('visibility', 'initial'); 197 | slider_rotRand.style('visibility', 'initial'); 198 | slider_leafProb.style('visibility', 'initial'); 199 | 200 | button_seed.style('visibility', 'initial'); 201 | button_newSeed.style('visibility', 'initial'); 202 | button_randomParams.style('visibility', 'initial'); 203 | button_change.style('visibility', 'initial'); 204 | button_hide.style('visibility', 'initial'); 205 | 206 | label_size.style('visibility', 'initial'); 207 | label_level.style('visibility', 'initial'); 208 | label_rot.style('visibility', 'initial'); 209 | label_lenRand.style('visibility', 'initial'); 210 | label_branchProb.style('visibility', 'initial'); 211 | label_rotRand.style('visibility', 'initial'); 212 | label_leafProb.style('visibility', 'initial'); 213 | label_perf.style('visibility', 'initial'); 214 | label_seed.style('visibility', 'initial'); 215 | label_source.style('visibility', 'initial'); 216 | label_source2.style('visibility', 'initial'); 217 | 218 | input_seed.style('visibility', 'initial'); 219 | 220 | div_inputs.style('visibility', 'initial'); 221 | } 222 | 223 | function hideUI() 224 | { 225 | slider_size.style('visibility', 'hidden'); 226 | slider_level.style('visibility', 'hidden'); 227 | slider_rot.style('visibility', 'hidden'); 228 | slider_lenRand.style('visibility', 'hidden'); 229 | slider_branchProb.style('visibility', 'hidden'); 230 | slider_rotRand.style('visibility', 'hidden'); 231 | slider_leafProb.style('visibility', 'hidden'); 232 | 233 | button_seed.style('visibility', 'hidden'); 234 | button_newSeed.style('visibility', 'hidden'); 235 | button_randomParams.style('visibility', 'hidden'); 236 | button_change.style('visibility', 'hidden'); 237 | button_hide.style('visibility', 'hidden'); 238 | 239 | label_size.style('visibility', 'hidden'); 240 | label_level.style('visibility', 'hidden'); 241 | label_rot.style('visibility', 'hidden'); 242 | label_lenRand.style('visibility', 'hidden'); 243 | label_branchProb.style('visibility', 'hidden'); 244 | label_rotRand.style('visibility', 'hidden'); 245 | label_leafProb.style('visibility', 'hidden'); 246 | label_perf.style('visibility', 'hidden'); 247 | label_seed.style('visibility', 'hidden'); 248 | label_source.style('visibility', 'hidden'); 249 | label_source2.style('visibility', 'hidden'); 250 | 251 | input_seed.style('visibility', 'hidden'); 252 | 253 | div_inputs.style('visibility', 'hidden'); 254 | } 255 | 256 | function readInputs(updateTree) 257 | { 258 | size = slider_size.value(); 259 | maxLevel = slider_level.value(); 260 | rot = slider_rot.value(); 261 | lenRand = slider_lenRand.value(); 262 | branchProb = slider_branchProb.value(); 263 | rotRand = slider_rotRand.value(); 264 | leafProb = slider_leafProb.value(); 265 | 266 | if ( updateTree && !growing ) 267 | { 268 | prog = maxLevel + 1; 269 | loop(); 270 | } 271 | } 272 | 273 | function mutate() 274 | { 275 | if ( !mutating ) 276 | return; 277 | 278 | var startTime = millis(); 279 | randomSeed(paramSeed); 280 | 281 | var n = noise(startTime/20000) - 0.5; 282 | 283 | randBias = 4 * Math.abs(n) * n; 284 | 285 | paramSeed = 1000 * random(); 286 | randomSeed(randSeed); 287 | readInputs(true); 288 | 289 | var diff = millis() - startTime; 290 | 291 | if ( diff < 20 ) 292 | setTimeout(mutate, 20 - diff); 293 | else 294 | setTimeout(mutate, 1); 295 | } 296 | 297 | function windowResized() 298 | { 299 | resizeCanvas(windowWidth, windowHeight); 300 | } 301 | 302 | function draw() 303 | { 304 | var startTime = millis(); 305 | 306 | stroke(255, 255, 255); 307 | 308 | background(0, 0, 0); 309 | translate(width / 2, height); 310 | scale(1, -1); 311 | 312 | translate(0, 20); 313 | 314 | branch(1, randSeed); 315 | 316 | var endTime = millis(); 317 | label_perf.html('Generated in ' + Math.floor((endTime - startTime) * 10) / 10 + 'ms'); 318 | 319 | noLoop(); 320 | } 321 | 322 | function branch(level, seed) 323 | { 324 | if ( prog < level ) 325 | return; 326 | 327 | randomSeed(seed); 328 | 329 | var seed1 = random(1000), 330 | seed2 = random(1000); 331 | 332 | var growthLevel = (prog - level > 1) || (prog >= maxLevel + 1) ? 1 : (prog - level); 333 | 334 | strokeWeight(12 * Math.pow((maxLevel - level + 1) / maxLevel, 2)); 335 | 336 | var len = growthLevel * size* (1 + rand2() * lenRand); 337 | 338 | line(0, 0, 0, len / level); 339 | translate(0, len / level); 340 | 341 | 342 | var doBranch1 = rand() < branchProb; 343 | var doBranch2 = rand() < branchProb; 344 | 345 | var doLeaves = rand() < leafProb; 346 | 347 | if ( level < maxLevel ) 348 | { 349 | 350 | var r1 = rot * (1 + rrand() * rotRand); 351 | var r2 = -rot * (1 - rrand() * rotRand); 352 | 353 | if ( doBranch1 ) 354 | { 355 | push(); 356 | rotate(r1); 357 | branch(level + 1, seed1); 358 | pop(); 359 | } 360 | if ( doBranch2 ) 361 | { 362 | push(); 363 | rotate(r2); 364 | branch(level + 1, seed2); 365 | pop(); 366 | } 367 | } 368 | 369 | if ( (level >= maxLevel || (!doBranch1 && !doBranch2)) && doLeaves ) 370 | { 371 | var p = Math.min(1, Math.max(0, prog - level)); 372 | 373 | var flowerSize = (size / 100) * p * (1 / 6) * (len / level); 374 | 375 | strokeWeight(1); 376 | stroke(240 + 15 * rand2(), 140 + 15 * rand2(), 140 + 15 * rand2()); 377 | 378 | rotate(-PI); 379 | for ( var i=0 ; i<=8 ; i++ ) 380 | { 381 | line(0, 0, 0, flowerSize * (1 + 0.5 * rand2())); 382 | rotate(2 * PI/8); 383 | } 384 | } 385 | } 386 | 387 | function startGrow() 388 | { 389 | growing = true; 390 | prog = 1; 391 | grow(); 392 | } 393 | 394 | function grow() 395 | { 396 | if ( prog > (maxLevel + 3) ) 397 | { 398 | prog = maxLevel + 3; 399 | loop(); 400 | growing = false; 401 | return; 402 | } 403 | 404 | var startTime = millis(); 405 | loop(); 406 | var diff = millis() - startTime; 407 | 408 | prog += maxLevel / 8 * Math.max(diff, 20) / 1000; 409 | setTimeout(grow, Math.max(1, 20 - diff)); 410 | } 411 | 412 | 413 | function rand() 414 | { 415 | return random(1000) / 1000; 416 | } 417 | 418 | function rand2() 419 | { 420 | return random(2000) / 1000 - 1; 421 | } 422 | 423 | function rrand() 424 | { 425 | return rand2() + randBias; 426 | } --------------------------------------------------------------------------------