-
Notifications
You must be signed in to change notification settings - Fork 0
/
classic.js
6256 lines (5989 loc) · 200 KB
/
classic.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
L-systems Renderer implementation in Exponential Idle.
Disclaimer: Differences between LSR and other L-system implementations all
around the web.
- / and \ may be swapped.
- + turns anti-clockwise, - turns clockwise.
- \ and / are used instead of < and >.
- Y-up is used here instead of Z-up.
- Quaternions are used instead of actually intuitive concepts such as matrices.
- Quaternions maths are absolutely butchered.
(c) 2022 Temple of Pan (R) (TM) All rights reversed.
*/
import { FreeCost } from '../api/Costs';
import { theory } from '../api/Theory';
import { Utils } from '../api/Utils';
import { Vector3 } from '../api/Vector3';
import { ui } from '../api/ui/UI';
import { Color } from '../api/ui/properties/Color';
import { FontFamily } from '../api/ui/properties/FontFamily';
import { Keyboard } from '../api/ui/properties/Keyboard';
import { LayoutOptions } from '../api/ui/properties/LayoutOptions';
import { TextAlignment } from '../api/ui/properties/TextAlignment';
import { Thickness } from '../api/ui/properties/Thickness';
import { TouchType } from '../api/ui/properties/TouchType';
import { Localization } from '../api/Localization';
import { MathExpression } from '../api/MathExpression';
import { ClearButtonVisibility } from '../api/ui/properties/ClearButtonVisibility';
import { LineBreakMode } from '../api/ui/properties/LineBreakMode';
import { BigNumber } from '../api/BigNumber';
import { Upgrade } from '../api/Upgrades';
import { Button } from '../api/ui/Button';
import { Frame } from '../api/ui/Frame';
var id = 'L_systems_renderer';
var getName = (language) =>
{
let names =
{
en: 'L-systems Renderer',
};
return names[language] || names.en;
}
var getDescription = (language) =>
{
let descs =
{
en:
`An educational tool that allows you to model fractals figures and plants.
Supported L-system features:
- Stochastic (randomised) rules
- 3D turtle controls
- Polygon modelling
Other features:
- Can save a whole army of systems!
- Camera modes: static and turtle-following
- Drawing speed and advanced stroke options!
Warning: v0.20 might break your internal state. Be sure to back it up, and ` +
`in case it's corrupted, please contact me.`,
};
return descs[language] || descs.en;
}
var authors = 'propfeds\n\nThanks to:\nSir Gilles-Philippe Paillé, for ' +
'providing help with quaternions\nskyhigh173#3120, for ' +
'suggesting clipboard and JSON internal state formatting';
var version = 1.01;
let time = 0;
let page = 0;
let offlineReset = true;
let gameIsOffline = false;
let altTerEq = false;
let tickDelayMode = false;
let resetLvlOnConstruct = true;
let measurePerformance = false;
let debugCamPath = false;
let maxCharsPerTick = 5000;
let menuLang = Localization.language;
let savedSystems = new Map();
let getImageSize = (width) =>
{
if(width >= 1080)
return 48;
if(width >= 720)
return 36;
if(width >= 360)
return 24;
return 20;
}
let getBtnSize = (width) =>
{
if(width >= 1080)
return 96;
if(width >= 720)
return 72;
if(width >= 360)
return 48;
return 40;
}
let getMediumBtnSize = (width) =>
{
if(width >= 1080)
return 88;
if(width >= 720)
return 66;
if(width >= 360)
return 44;
return 36;
}
let getSmallBtnSize = (width) =>
{
if(width >= 1080)
return 80;
if(width >= 720)
return 60;
if(width >= 360)
return 40;
return 32;
}
const BUTTON_HEIGHT = getBtnSize(ui.screenWidth);
const SMALL_BUTTON_HEIGHT = getSmallBtnSize(ui.screenWidth);
const ENTRY_CHAR_LIMIT = 5000;
const TRIM_SP = /\s+/g;
const BACKTRACK_LIST = new Set('+-&^\\/|[$T');
const locStrings =
{
en:
{
versionName: 'v1.01',
welcomeSystemName: 'Arrow',
welcomeSystemDesc: 'Welcome to the L-systems Renderer!',
equationOverlayLong: '{0} – {1}\n\n{2}\n\n{3}',
equationOverlay: '{0}\n\n{1}',
rendererLoading: `\\begin{{matrix}}Loading...&\\text{{Lv. {0}}}&({1}
\\text{{ chars}})\\end{{matrix}}`,
currencyTime: ' (elapsed)',
varLvDesc: '\\text{{Level: }}{0}{1}',
varTdDesc: '\\text{{Tick length: }}{0}\\text{{ sec}}',
varTdDescInf: '\\text{{Tick length: }}\\infty',
varTsDesc: '\\text{{Tickspeed: }}{0}/\\text{{sec}}',
varIdDesc: '\\text{{Init. delay: }}{0}\\text{{ sec}}',
upgResumeInfo: 'Resumes the last rendered system',
saPatienceTitle: 'You\'re watching grass grow.',
saPatienceDesc: 'Let the renderer draw a 10-minute long figure or ' +
'playlist.',
saPatienceHint: 'Be patient.',
btnSave: 'Save',
btnClear: 'Clear All',
btnDefault: '* Reset to Defaults',
btnAdd: 'Add',
btnUp: '▲',
btnDown: '▼',
btnReroll: 'Reroll',
btnConstruct: 'Construct',
btnDelete: 'Delete',
btnView: 'View',
btnClipboard: 'Clipboard',
btnOverwrite: 'Overwrite',
btnSaveCopy: 'Save as Copy',
btnSelect: 'Select',
btnSelected: '(Selected)',
btnPrev: 'Previous',
btnNext: 'Next',
btnClose: 'Close',
btnImport: 'Import',
btnContents: 'Table of\nContents',
btnPage: '{0}',
btnMenuLSystem: 'L-system menu',
btnMenuRenderer: 'Renderer menu',
btnMenuSave: 'Save/load',
btnMenuTheory: 'Settings',
btnMenuManual: 'User guide',
btnResume: 'Resume – {0}',
btnStartMeasure: 'Measure performance',
btnEndMeasure: 'Stop measuring',
measurement: '{0}: max {1}ms, avg {2}ms over {3} ticks',
rerollSeed: 'You are about to reroll the system\'s seed.',
resetRenderer: 'You are about to reset the renderer.',
menuSequence: '{0} (Level {1})',
labelLevelSeq: 'Level {0}: {1} chars',
labelChars: '({0} chars)',
menuLSystem: 'L-system Menu',
labelAxiom: 'Axiom: ',
labelAngle: 'Turning angle (°): ',
labelRules: 'Production rules: {0}',
labelIgnored: 'Ignored symbols: ',
labelTropism: 'Tropism (gravity): ',
labelSeed: 'Seed (≠ 0): ',
menuRenderer: 'Renderer Menu',
labelInitScale: '* Initial scale: ',
labelFigScale: '* Figure scale: ',
labelCamMode: 'Camera mode: {0}',
camModes: ['Fixed', 'Linear', 'Quadratic'],
labelCamCentre: 'Fixed camera centre (x,): ',
labelCamOffset: '... centre (y, z): ',
labelFollowFactor: 'Follow factor (0-1): ',
labelLoopMode: 'Looping mode: {0}',
loopModes: ['Off', 'Level', 'Playlist'],
labelUpright: '* Upright figure: ',
labelBTTail: 'Draw tail end: ',
labelLoadModels: '* Load models: ',
labelQuickdraw: '* Quickdraw: ',
labelQuickBT: '* Quick backtrack: ',
labelHesitate: '* Stutter on backtrack: ',
labelHesitateApex: '* Stutter at apex: ',
labelHesitateFork: '* Stutter at fork: ',
labelOldTropism: '* Alternate tropism method: ',
labelBTList: '* Backtrack list: ',
labelRequireReset: '* Modifying this setting will require a reset.',
menuSave: 'Save/Load Menu',
labelCurrentSystem: 'Current system: ',
labelSavedSystems: 'Saved systems: {0}',
labelApplyCamera: 'Applies static camera: ',
menuClipboard: 'Clipboard Menu',
labelTotalLength: 'Total length: {0}',
labelEntryCharLimit: `Warning: This entry has been capped at {0} ` +
`characters. Proceed with caution.`,
menuNaming: 'Save System',
labelName: 'Title: ',
defaultSystemName: 'Untitled L-system',
labelDesc: 'Description: ',
noDescription: 'No description.',
duplicateSuffix: ' (copy)',
menuTheory: 'Theory Settings',
labelOfflineReset: 'Reset graph on tabbing in: ',
labelResetLvl: 'Reset renderer controls on construction: ',
labelTerEq: 'Tertiary equation: {0}',
terEqModes: ['Coordinates', 'Orientation'],
labelMeasure: 'Measure performance: ',
debugCamPath: 'Debug camera path: ',
labelMaxCharsPerTick: 'Maximum loaded chars/tick: ',
labelInternalState: 'Internal state: ',
menuManual: 'User Guide ({0}/{1})',
menuTOC: 'Table of Contents',
labelSource: 'Source: ',
manualSystemDesc: 'From the user guide, page {0}.',
manual:
[
{
title: 'Introduction',
contents:
`Welcome to the Classic L-systems Renderer! This guide aims to help you ` +
`understand the basics of L-systems, as well as instructions on how to ` +
`effectively use this theory to construct and render them.
Let's start discovering the wonders of L-systems (and the renderer).
Notice: A gallery for L-systems has opened! Visit page 28 for details.`
},
{
title: 'Controls: Theory screen',
contents:
`The theory screen consists of the renderer and its controls.
Level: the iteration/generation/stage of the system. Pressing + or - will ` +
`derive/revert the system.
- Pressing the Level button will reveal all levels of the system.
- Holding + or - will buy/refund levels in bulks of 10.
Tickspeed: controls the renderer's drawing speed (up to 10 lines/sec, which ` +
`produces less accurate lines).
- Pressing the Tickspeed button will toggle between Tickspeed and Tick ` +
`length modes.
- Holding - will create an 'anchor' on the current level before setting it ` +
`to 0, pausing the renderer. Holding + afterwards will return the renderer ` +
`to the previously anchored speed.
Reset: located on the top right. Pressing this button will reset the renderer.
Menu buttons: You pressed on one of them to get here, did you?
- L-system menu: allows you to edit the currently displayed system.
- Renderer menu: configures the camera along with other renderer behaviour.
- Save/load: store your favourite L-systems here.
- Settings: configure general options for the theory.`
},
{
title: 'Configuring the L-system',
contents:
`Design your L-system using the L-systems menu.
- Axiom: the system's starting sequence.
- Turning angle: the angle the turtle turns when turns the turtle (in degrees).
- Production rules: an unlimited number of rules can be added using the ` +
`'Add' button.
- Ignored symbols: the turtle will stand still when encountering these symbols.
- Tropism (gravity): determines the amount of gravity applied by the tropism ` +
`(T) command.
- Seed: determines the seed for a stochastic system. Can be manually set or ` +
`rerolled.
Note: Any blank rules will be trimmed afterwards.`
},
{
title: 'Configuring the renderer',
contents:
`Configure the visual representation of your L-system with the Renderer menu.
Camera options:
- Figure scale: determines the zoom level's inverse using a formula. For ` +
`instance, a figure scale of 2^lv will zoom the figure out by a factor of ` +
`2 every level.
- Camera mode: toggles between Fixed, Linear and Quadratic. The latter two ` +
`modes follow the turtle.
- Fixed camera centre: determines camera position in Fixed mode using a ` +
`written formula similar to figure scale.
- Follow factor: changes how quickly the camera follows the turtle.
- Upright figure: rotates the figure by 90 degrees counter-clockwise around ` +
`the z-axis so that it heads upwards.
Renderer logic:
- Looping mode: the Level mode repeats a single level, while the Playlist ` +
`mode draws levels consecutively.
- Draw tail end: whether to draw the last backtrack after finishing the ` +
`sequence.
Advanced stroke options:
- Quickdraw: skips over straight consecutive segments.
- Quick backtrack: works similarly, but on the way back.
- Stutter at apex: pause for one tick at the tips of lines.
- Stutter at fork: pause for one tick after backtracking through branches.`
},
{
title: 'Saving and loading',
contents:
`The Save/load menu allows you to save your favourite L-systems along with ` +
`their camera configurations.
- Clipboard: allows you to export the system as a string to share with your ` +
`fellow gardeners, or import one from them for personal consumption.
Warning: The entry can only hold up to 5000 characters.
- Save: set the title and description for a new system, or overwrite one of ` +
`the existing ones.
- View: allows you to edit, load and delete saved systems.`
},
{
title: 'Theory settings',
contents:
`The Settings menu contain several general options for the theory.
- Reset graph on tabbing in: when this option is turned off, the graph will ` +
`resume the current drawing after the game enters focus, assuming it does ` +
`not close itself by then. The theory will not draw when the game is not in ` +
`focus, regardless of this setting.
- Reset level on construction: this option is generally turned on for ` +
`safety, however, if you are trying to design and edit systems for a while, ` +
`it is recommended to turn it off for convenience.
- Tertiary equation: switches between the display of turtle coordinates and ` +
`orientation (quaternion).
- Measure performance: displays performance statistics at the top of the ` +
`screen.
- Maximum loaded chars/tick: adjusts how fast a system loads, in order to ` +
`prevent 'Maximum statements' errors from occuring.
- Internal state: allows you to export the entire save data.
Warning: The entry can only hold up to 5000 characters.`
},
{
title: 'L-systems: A primer',
contents:
`Developed in 1968 by biologist Aristid Lindenmayer, an L-system is a formal ` +
`grammar that describes the growth of a sequence (string). It is often used ` +
`to model plants and draw fractal figures.
Every L-system starts with a sequence, called the axiom. From the axiom, the ` +
`sequence grows according to a set of production rules that describe how ` +
`each symbol (character) in the sequence would be rewritten in the next level.
Each rule is represented in the form of:
{symbol} = {derivation(s)}
Considering a simple system with the axiom of b and the rules:
b = a
a = ab,
the sequence will grow as follows:
Level 0: b
Level 1: a
Level 2: ab
Level 3: aba
Level 4: abaab
Level 5: abaababa`
},
{
title: 'L-system and turtle graphics',
contents:
`Owing to its sequential nature, an L-system can be represented as a list of ` +
`instructions when read by a turtle. Not that the turtle can actually ` +
`comprehend this crap though.
Here are the basic symbols and their respective instructions:
F: moves turtle forward to draw a line of length 1.
+: rotates turtle counter-clockwise by an angle.
-: rotates turtle clockwise by an angle.
Note: In the original grammar, the lower-case f is used to move the turtle ` +
`forward without drawing anything, but that is simply impossible with this ` +
`game's 3D graph. So in this theory, any non-reserved symbol will draw a ` +
`line. This includes both upper- and lower-case letters (except T), and ` +
`potentially anything you can throw at it.`
},
{
title: 'Example: The dragon curve',
contents:
`Also known as the Heighway dragon, the curve was first discovered by John ` +
`Heighway in 1966, along with two fellow physicists in NASA, William Harter ` +
`and Bruce Banks.
Legends have it, that when you fold a thin piece of paper at the middle over ` +
`and over again, and then release it while making sure every fold is exactly ` +
`90°, a dragon would spawn. But be careful, as every time you fold, although ` +
`the dragon gets thicker, its 'length' would shrink by a factor of sqrt(2).
Press 'Construct' to see the dragon in action.
Axiom: FX
Y = -FX-Y
X = X+YF+
Turning angle: 90°
Applies static camera:
Scale: 4*sqrt(2)^lv
Centre: (0, 0, 0)`
},
{
title: 'Example: Sierpiński triangle',
contents:
`The Sierpiński triangle (or gasket/sieve) is a fractal of an equilateral ` +
`triangle containing equilateral triangles inside it, containing equilateral ` +
`triangles inside it, containing equilateral triangles.
Did you know that when you take Pascal's triangle then select only the even ` +
`numbers, the Sierpiński triangle will appear?
Axiom: X
X = +Y-X-Y+
Y = -X+Y+X-
Turning angle: 60°
Applies static camera:
Scale: 2^lv
Centre: (0.5*2^lv, sqrt(3)/4*2^lv, 0)`
},
{
title: 'Branching mechanisms',
contents:
`Although numerous fractals can be created using only the basic symbols, ` +
`when it comes to modelling branching structures such as trees, the turtle ` +
`wishes it could be split in two... Using a stack mechanism, we can ` +
`essentially allow the turtle to return to a point in the past to take on a ` +
`new path.
Stack operations are represented with square brackets:
[: records the turtle's position and facing onto a stack.
]: take the topmost element (position and facing) off the stack, and move ` +
`the turtle there.
%: cuts off the remainder of a branch, by deleting every symbol up until ` +
`the closing bracket ] in the branch.
Note: Due to the game's 3D graph only allowing one continuous path to be ` +
`drawn, the turtle will not actually divide itself, but instead backtrack ` +
`through the old path.`
},
{
title: 'Example: Arrow weed',
contents:
`Meet the default system, now standing upright like a real tree.
The symbol F here represents the stem and branches, which expand to twice ` +
`their size every level. Meanwhile X, sitting at the top of each branch, ` +
`represents what's known as a vegetative apex - the tip of an axis (stem or ` +
`branch) that grows into new branches and leaves.
Let's try cutting its branches off using %!
Axiom: X
F = FF
X = F[+X][-X]FX
Turning angle: 30°
Applies static camera:
Scale: 1.5*2^lv
Centre: (0, 1.2*2^lv, 0)
Upright`
},
{
title: 'Stochastic L-systems',
contents:
`When it comes to modelling vegetative structures, sometimes the mimicking ` +
`of environment variables in their growth process is needed. Stochastic ` +
`L-systems offer a simplified approach to this by introducing randomness to ` +
`a plant's shape.
To create a stochastic rule for an L-system, simply list several derivations ` +
`within the rule, separated by commas:
{symbol} = {derivation_0}, {derivation_1}, ...
When the system is grown, one of the possible derivations will be randomly ` +
`selected (with equal chance) for each symbol. The selection process is ` +
`controlled by the system's seed.
A system's seed can either be changed manually within the L-systems menu, or ` +
`randomly reassigned using the 'Reroll' button on the top right corner of ` +
`the theory screen.
Note: setting the seed to 0 will disable the random generation.`
},
{
title: 'Example: Stochastic tree',
contents:
`This tree generates a random shape every time it rolls.
Axiom: X
F = FF
X = F-[[X]+X]+F[+FX]-X, F+[[X]-X]-F[-FX]+X
Turning angle: 22.5°
Applies static camera:
Scale: 1.5*2^lv
Centre: (0, 1.2*2^lv, 0)
Upright`
},
{
title: 'Example: Snowflake',
contents:
`Honey I told you every snowflake is different can you stop licking them please
Axiom: [X]+[X]+[X]+[X]+[X]+[X]
X = F[+F][-F]X
F = F[+i][-i]F
i = Ii, IIi
Turning angle: 60°
Ignored: i
Applies static camera:
Scale: 2*2^lv
Centre: (0, 0, 0)`
},
{
title: 'L-systems in 3D',
contents:
`Using a yaw-pitch-roll orientation system, we can also generate figures in 3D.
Counter-clockwise and clockwise respectively,
+ -: rotate turtle on the z-axis (yaw).
& ^: rotate turtle on the y-axis (pitch).
\\ /: rotate turtle on the x-axis (roll).
|: reverses the turtle's direction.
T: applies a force of gravity (tropism) to the turtle's current heading, so ` +
`that it drops downward (with a positive tropism factor), or lifts upward ` +
`(with a negative tropism factor). The factor should be in the range from ` +
`-1 to 1.
$: rolls the turtle around its own axis, so that its up vector is closest to ` +
`absolute verticality i.e. the y-axis, and subsequently, its direction is ` +
`closest to lying on a horizontal plane.
Note: In other L-system implementations, < and > may be used instead of \\ ` +
`and / like in this theory.
Note 2: Other L-system implementations may also start the turtle facing the ` +
`y-axis or z-axis instead of the x-axis. To adopt those systems into LSR, ` +
`swap the axes around until the desired results are achieved.
Note 3: Other L-system implementations may swap counter-clockwise and ` +
`clockwise rotations.`
},
{
title: 'Example: Blackboard tree',
contents:
`Modelled after a blackboard tree (Alstonia scholaris) in its infant state.
Axiom: F
F = Y[++++++MF][-----NF][^^^^^OF][&&&&&PF]
M = Z-M
N = Z+N
O = Z&O
P = Z^P
Y = Z-ZY+
Z = ZZ
Turning angle: 8°
Applies static camera:
Scale: 2*2^lv
Centre: (0, 1.2*2^lv, 0)
Upright`,
source: 'https://www.bioquest.org/products/files/13157_Real-time%203D%20Plant%20Structure%20Modeling%20by%20L-System.pdf'
},
{
title: 'Example: Hilbert curve (3D)',
contents:
`The Hilbert curve is a fractal figure that fills the space of a 2D plane ` +
`using only a single line. This is the 3D version.
It's recommended to draw at a low tickspeed (high tick length).
Axiom: X
X = ^/XF^/XFX-F^\\\\XFX&F+\\\\XFX-F\\X-\\
Turning angle: 90°
Ignore: X
Applies static camera:
Scale: 2^lv
Centre: (0.5*2^lv-0.5, 0.5*2^lv-0.5, 0.5*2^lv-0.5)`
},
{
title: 'Example: Fern',
contents:
`A 3D fern.
Axiom: FFFA
A = [++++++++++++++FC]B^+B[--------------FD]B+BA
C = [---------FF][+++++++++FF]B&&+C
D = [---------FF][+++++++++FF]B&&-D
Turning angle: 4°
Applies static camera: (mathematically unproven)
Scale: 3*1.3^lv
Centre: (0, 1.8*1.3^lv, 0)
Upright`,
source: 'http://jobtalle.com/lindenmayer_systems.html'
},
{
title: 'Modelling: Polygon tool',
contents:
`To keep up with demands for higher levels of detail pertaining flowers, ` +
`leaves and other organs, the classic L-system syntax was extended to ` +
`accomplish this. Introducing the polygon tool, designed to hide away ` +
`unneeded points and draw contours by connecting the dots, literally.
{: initiates polygon drawing mode.
.: sets a polygon vertex.
}: ends the polygon drawing mode.
Normal commands inside a polygon block will not draw lines, making it great ` +
`for hiding away any scaffolding in the creation of models.
Note: Due to how Exponential Idle's 3D graph works, the polygon tool in LSR ` +
`works differently from that described in The Algorithmic Beauty of Plants. ` +
`Therefore, it is advised to make some adjustments when adopting schemes ` +
`from the book into LSR.`
},
{
title: 'Example: Lily pad (hollow)',
contents:
`This is a hollow lily pad. Can you make it draw some lines on the periphery?
Tip: Open the sequence menu to see which points are being dotted.
Axiom: {[A}]{[B}]
A = [+A]C.
B = [-B]C.
C = GC
Turning angle: 27°
Applies static camera:
Scale: lv
Centre: (0, lv/2-1, 0)
Upright`
},
{
title: 'Dedicated models for symbols',
contents:
`While the polygon mode is useful when it comes to building custom models, ` +
`the problem of separating between models and growth processing rules still ` +
`remains, as writing the model in a different rule will delay its drawing by ` +
`one level. With a special kind of rule, we can assign dedicated models to ` +
`each symbol to be drawn instantly.
To declare a model rule, attach a ~ (tilde) in front of the symbol:
~{symbol} = {model}
The model will be represented as a temporary sequence that cannot evolve, ` +
`replacing the default action of drawing a straight line.`
},
{
title: 'Example: Lilac branch',
contents:
`Ripped straight off of page 92 of The Algorithmic Beauty of Plants.
K represents the flower, and A the flower buds.
Axiom: AK
A = [--//K][++//K]I///A
I = Fi
i = Fj
j = J[--FFA][++FFA]
~K = F[+++[--F+F]^^^[--F+F]^^^[--F+F]^^^[--F+F]]
Turning angle: 30°
Applies static camera:
Scale: 3*lv
Centre: (0, 1.5*lv, 0)
Upright`
},
{
title: 'Appendix: Summary of symbols',
contents:
`Any letter (except T): moves turtle forward to draw a line of length 1.
+ -: rotate turtle on the z-axis (yaw).
& ^: rotate turtle on the y-axis (pitch).
\\ /: rotate turtle on the x-axis (roll).
|: reverses turtle direction.
T: applies tropism (gravity) to branch.
$: aligns turtle's up vector to vertical.
[: pushes turtle state onto a stack.
]: pops the stack's topmost element onto the turtle.
%: cuts off the remainder of a branch.
{: initiates polygon drawing mode.
.: sets a polygon vertex.
}: ends the polygon drawing mode.
~: declares a symbol's model.
,: separates between stochastic derivations.`
},
{
title: 'Appendix: Advanced artistry in LSR',
contents:
`Welcome to the LSR Art Academy. Thanks for finishing the manual, by the way!
And today's class: Tick length.
For a background observation, Exponential Idle's 3D graph seems to be using ` +
`a Bézier-like spline with no locality. Therefore, it is not suitable for ` +
`drawing straight lines. However, this does not mean we cannot get anything ` +
`out of it, and today's class will demonstrate otherwise.
Now, while tickspeed might be more of a familiar concept to the idle ` +
`fellows, in LSR it posesses a flaw: it is not consistent. For instance, at ` +
`9 tickspeed, the renderer would skip one tick out of every 10, making the ` +
`line quality really inconsistent. And although there might be value in ` +
`mixing drawing styles this way, we will not be going into details about it ` +
`in this lecture. Instead, we will be discussing the various tick lengths ` +
`and their artistic applications.
- 0.1 sec: the quickest stroke in the West. This is the equivalent of 10 ` +
`tickspeed. Figures drawn at this speed are jumbled and chaotic, making it ` +
`suitable for large-scale figures, where errors become tiny and hard to notice.
- 0.2 sec: characterised by quick but elegant strokes, accompanied by motifs ` +
`of leaves and flowers, this speed feels at home with plant modelling. It ` +
`offers a good compromise between speed and precision, although even 0.1 ` +
`would be too slow for large scale figures.
- 0.3 sec: Tick length 0.3 holds the most powerful secret in this whole ` +
`universe: it can create the straightest lines out of this family. No ` +
`trickery needed! As the 3D graph seems to be running on a 3-tick cycle, ` +
`the sampled points line up precisely with the renderer's drawing.
- 0.4 sec: this one can really spice the figure up by tying up cute knots ` +
`between corners occasionally, mimicking leaf shapes on a tree.
- 0.5 sec: with slight occasional overshoots, tick length 0.5 proves itself ` +
`of use when it comes to bringing that rough sketch feeling to a figure.
- 0.6 sec and above: I don't care, class dismissed.`
},
{
title: 'Advanced artistry in LSR (2)',
contents:
`Welcome back, class! We're learning about something simple today, by the way:
Backtrack options, or why Hesitation is Not Defeat.
Now, open your renderer menu textbook to the last section. There are about 4 ` +
`options here as you can see - each of them with advantages and, non-advantages!
- Quickdraw: is generally a decent option when it comes to saving some time, ` +
`but when compared to quick backtrack, it poses a greater drawback when it ` +
`comes to both precision and aesthetics.
- Quick backtrack: this one's a reliable one, trading only a little beauty ` +
`for some extra speed.
- Stutter at apex/fork: now, this is what I mean when I say hesitation is ` +
`not defeat. Pausing for even just one tick can give your figure just enough ` +
`cohesion it really needs. To prove this, try loading the Arrow weed then ` +
`alternate between drawing with these option on and off, at 0.1 tick length, ` +
`or 10 tickspeed. There will be a noticeable difference, even from afar.
Class dismissed, and stay tuned for next week's lecture, on the Art of Looping!`
},
{
title: 'Advanced artistry in LSR (3)',
contents:
`Welcome back, class! Today is only an extension of last class, and so we'll ` +
`be going through the concept of looping. This relates to last week's class ` +
`about backtracking.
First, I want everybody to construct a Cantor set as follows:
Axiom: X++F
X = +Y-XFX-Y+
F = FFF
Y = YY
Turning angle: 90°
Then, apply the following static camera:
Scale: 0.5*3^lv
Centre: (0.5*3^lv, 2^(lv-1)-.5, 0)
Now, enter the Level looping mode, turn 'Draw tail end' on and draw the ` +
`Cantor set at level 2 or higher. You will see that the turtle goes back and ` +
`forth when it meets the bottom left corner. Can anybody confirm that? Good.
Now, turn 'Draw tail end' off. Can anyone tell me what would happen?
That's right. The turtle will keep going around in a loop of this delicious ` +
`Cantor bread. It really loves it.
Generally, in figures such as this or the Koch snowflake, it'd be better to ` +
`loop than to eat your own tail. Fortunately for you, there aren't many ` +
`figures like these. Do note though, that quick backtrack will not trigger, ` +
`due to the tail end being a backtrack itself, of course.`
},
{
title: 'Gallery',
contents:
`Welcome to a L-systems gallery. Enjoy!
Notice: The gallery is open for submission!
Mail me your own L-systems, so it can be included in the gallery.
Maybe over Discord. Reddit account. Arcane-mail logistics!`
},
{
title: 'Lilac branch (Advanced)',
contents:
`A more complex version of the previous lilac branch in the Models section, ` +
`complete with detailed models and copious utilisation of tropism.
Axiom: +SA
S = FS
A = T[--//K][++//K]I///A
~A = [+++aaaa]
~a = -{[^-F.][--FF.][&-F.].}+^^^
~K = [FT[F]+++kkkk]
~k = -{[^--F.][F-^-F.][^--F|++^--F|+F+F.][-F+F.][&--F|++&--F|+F+F.][F-&-F.][&--F.].}+^^^
I = Fi
i = Fj
j = J[--FFA][++FFA]
Turning angle: 30°
Tropism: 0.16
Applies static camera:
Scale: 2*lv+1
Centre: (2*lv+1, lv/2+3/4, 0)`
},
{
title: 'Botched Cultivar FF',
contents:
`Represents a common source of carbohydrates.
Axiom: X
F = FF
X = F-[[X]+X]+F[-X]-X
Turning angle: 15°
Applies static camera:
Scale: 2^lv
Centre: (0, 2^lv, 0)
Upright`
},
{
title: 'Botched Cultivar FXF',
contents:
`Commonly called the Cyclone, cultivar FXF resembles a coil of barbed wire. ` +
`Legends have it, once a snake moult has weathered enough, a new life is ` +
`born unto the tattered husk, and from there, it stretches.
Axiom: X
F = F[+F]XF
X = F-[[X]+X]+F[-FX]-X
Turning angle: 27°
Applies static camera: (mathematically unproven)
Scale: 1.5*2^lv
Centre: (0.225*2^lv, -0.75*2^lv, 0)`
},
{
title: 'Botched Cultivar XEXF',
contents:
`Bearing the shape of a thistle, cultivar XEXF embodies the strength and ` +
`resilience of nature against the harsh logarithm drop-off. It also smells ` +
`really, really good.
Axiom: X
E = XEXF-
F = FX+[E]X
X = F-[X+[X[++E]F]]+F[X+FX]-X
Turning angle: 22.5°
Applies static camera: (mathematically unproven)
Scale: 3^lv
Centre: (0.25*3^lv, 0.75*3^lv, 0)
Upright`
}
]
}
};
/**
* Returns a localised string.
* @param {string} name the string's internal name.
* @returns {string}
*/
let getLoc = (name, lang = menuLang) =>
{
if(lang in locStrings && name in locStrings[lang])
return locStrings[lang][name];
if(name in locStrings.en)
return locStrings.en[name];
return `String missing: ${lang}.${name}`;
}
/**
* Returns a string of a fixed decimal number, with a fairly uniform width.
* @param {number} x the number.
* @returns {string}
*/
let getCoordString = (x) => x.toFixed(x >= -0.01 ?
(x <= 9.999 ? 3 : (x <= 99.99 ? 2 : 1)) :
(x < -9.99 ? (x < -99.9 ? 0 : 1) : 2)
);
/**
* Compares equality for every member of two sets, disregarding order.
* @param {Set} xs set 1.
* @param {Set} ys set 2.
* @returns {boolean}
*/
let eqSet = (xs, ys) => xs.size === ys.size && [...xs].every((x) => ys.has(x));
/**
* Represents a linear congruential generator.
*/
class LCG
{
/**
* @constructor
* @param {number} seed (default: 0) the starting seed for the generator.
*/
constructor(seed = 0)
{
/**
* @type {number} the mod of this realm.
*/
this.m = 0x80000000; // 2**31;
/**
* @type {number} some constant
*/
this.a = 1103515245;
/**
* @type {number} some other constant.
*/
this.c = 12345;
/**
* @type {number} the LCG's current state.
*/
this.state = seed % this.m;
}
/**
* Returns a random integer within [0, 2^31).
* @returns {number}
*/
get nextInt()
{
this.state = (this.a * this.state + this.c) % this.m;
return this.state;
}
/**
* Returns a random floating point number within [0, 1] or [0, 1).
* @param {boolean} [includeEnd] (default: false) whether to include the
* number 1 in the range.
* @returns {number}
*/
nextFloat(includeEnd = false)
{
if(includeEnd)
{
// [0, 1]
return this.nextInt / (this.m - 1);
}
else
{