]> git.r.bdr.sh - rbdr/lissajous/commitdiff
Make 3D
authorRuben Beltran del Rio <redacted>
Tue, 7 May 2024 20:37:27 +0000 (22:37 +0200)
committerRuben Beltran del Rio <redacted>
Tue, 7 May 2024 20:37:27 +0000 (22:37 +0200)
42 files changed:
eslint.config.js [new file with mode: 0644]
images/far.svg [new file with mode: 0644]
images/fast-pitch.svg [new file with mode: 0644]
images/fast-roll.svg [new file with mode: 0644]
images/fast-yaw.svg [new file with mode: 0644]
images/ha.svg [new file with mode: 0644]
images/hf.svg [new file with mode: 0644]
images/la.svg [new file with mode: 0644]
images/lf.svg [new file with mode: 0644]
images/long.svg [new file with mode: 0644]
images/near.svg [new file with mode: 0644]
images/no-pitch.svg [new file with mode: 0644]
images/no-roll.svg [new file with mode: 0644]
images/no-yaw.svg [new file with mode: 0644]
images/short.svg [new file with mode: 0644]
index.html
lib/components/configuration.js [new file with mode: 0644]
lib/components/radius.js [new file with mode: 0644]
lib/components/triple_amplitude.js [new file with mode: 0644]
lib/components/triple_frequency.js
lib/components/up.js [new file with mode: 0644]
lib/config.js
lib/factories/curves.js
lib/factories/global.js [new file with mode: 0644]
lib/factories/ui.js [new file with mode: 0644]
lib/main.js
lib/nodes/ample.js [new file with mode: 0644]
lib/nodes/cameras.js [new file with mode: 0644]
lib/nodes/configurable.js [new file with mode: 0644]
lib/nodes/frequent.js [new file with mode: 0644]
lib/nodes/lissajous_curve.js
lib/systems/amplitude_adjuster.js [new file with mode: 0644]
lib/systems/camera_adjuster.js [new file with mode: 0644]
lib/systems/camera_rotator.js [new file with mode: 0644]
lib/systems/frequency_adjuster.js [new file with mode: 0644]
lib/systems/global_adjuster.js [new file with mode: 0644]
lib/systems/lissajous_position_updater.js
lib/systems/webgl_renderer.js
lib/webgl_utils.js
package.json
pnpm-lock.yaml
style.css

diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644 (file)
index 0000000..d66512c
--- /dev/null
@@ -0,0 +1,21 @@
+import hapi from '@hapi/eslint-plugin';
+
+export default [{
+  languageOptions: {
+    globals: {
+      document: true,
+      window: true,
+      console: true
+    }
+  },
+  plugins: {
+    hapi
+  },
+  rules: {
+    indent: [
+      2,
+      2
+    ],
+    'no-undef': 2
+  }
+}];
diff --git a/images/far.svg b/images/far.svg
new file mode 100644 (file)
index 0000000..c7ddda9
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Far</title>
+    <g id="Far" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M8.84114125,7.77951579 L8.87864783,7.74195335 C9.47733227,7.14855951 10.0081534,6.91999375 10.5192146,7.02445268 C10.7431984,7.07023412 10.9489468,7.17680976 11.1576448,7.33991034 L11.1865854,7.36278854 L11.2147758,7.38562354 L11.2425122,7.4086793 L11.2608972,7.42430281 L11.2792998,7.44021998 L11.2978079,7.45650901 L11.3259597,7.48181091 L11.3548423,7.50838945 L11.3847521,7.53650862 L11.4159853,7.56643239 L11.4602,7.60959413 L11.4836065,7.63274958 L11.5334565,7.6826007 L11.6171409,7.76740115 L11.6983045,7.85014284 L11.7455007,7.89785119 L11.8005927,7.95289976 L11.8474263,7.99894405 L11.8910145,8.04095295 L11.9184782,8.06689047 L11.9448116,8.09129471 L11.9701394,8.11426975 L11.9945865,8.13591968 C12.0026026,8.14292704 12.0104927,8.14973089 12.0182776,8.15634859 L12.0413375,8.17566057 C12.0565281,8.18818604 12.0713812,8.20003628 12.0860632,8.21135008 L12.1079785,8.22793579 L12.1297619,8.24382092 L12.1515383,8.25910956 C12.1588097,8.26411792 12.1661008,8.26904422 12.1734323,8.27390579 L12.1955689,8.28831372 C12.1992855,8.29068846 12.2030173,8.29305136 12.2067671,8.29540459 C12.3816963,8.40518377 12.5404728,8.44381786 12.7168537,8.41241788 L12.733442,8.40926899 L12.8481342,8.97459072 C12.509114,9.04291652 12.1976729,8.97126873 11.8984333,8.78347682 L11.8759852,8.76922158 L11.8539702,8.75489529 C11.8503335,8.75249871 11.846712,8.75009696 11.8431045,8.74768894 L11.8216149,8.73315668 L11.8003926,8.71842081 L11.7793712,8.70342831 L11.7584845,8.68812617 L11.7376661,8.67246135 L11.7168496,8.65638084 C11.7133767,8.65366316 11.7099011,8.65092596 11.7064214,8.64816812 L11.6854835,8.63136464 L11.6643817,8.6140129 C11.6608474,8.6110723 11.6573035,8.60810666 11.6537486,8.60511486 L11.6322766,8.58684131 L11.6104749,8.56788694 L11.5882771,8.54819872 L11.5540928,8.51717445 L11.5306146,8.49541965 L11.4941987,8.46104695 L11.4561454,8.42442657 L11.4162307,8.38537957 L11.3742309,8.34372699 L11.3146005,8.28382778 L11.1752469,8.14216069 L11.1380978,8.10480166 L11.0919906,8.05896272 L11.0604925,8.02804531 L11.0299609,7.99843574 L11.0003525,7.9701062 L10.9716245,7.94302884 L10.9437335,7.91717586 L10.9166366,7.89251941 L10.8902905,7.86903168 L10.8646523,7.84668484 L10.8396788,7.82545106 C10.8355696,7.8220033 10.8314862,7.81860077 10.8274279,7.81524287 L10.8033706,7.7956265 C10.7914848,7.78608152 10.7798078,7.77692784 10.7683155,7.76814982 L10.7455697,7.75109002 C10.7305604,7.74004455 10.7158506,7.72964837 10.7013827,7.71986438 L10.6798552,7.70564289 C10.5837334,7.64367056 10.4973275,7.60874566 10.4029615,7.58945763 C10.2367995,7.55549475 10.0509048,7.59024079 9.83432161,7.71320977 L9.80764709,7.72870946 C9.75398832,7.760596 9.69846217,7.79782148 9.64090775,7.84067252 L9.61196076,7.86256816 C9.60225493,7.87002386 9.59249201,7.87763715 9.58267126,7.88540936 L9.55303478,7.90920408 L9.52304683,7.93396027 C9.50796425,7.94658006 9.49274819,7.9595634 9.47739615,7.97291475 L9.44651001,8.00010947 L9.41525676,8.02829353 L9.38363194,8.0574749 L9.35163108,8.08766153 L9.31924971,8.1188614 C9.31382085,8.12414625 9.30837596,8.12947366 9.30291493,8.13484378 L9.26995441,8.16757841 C9.26442847,8.17312006 9.25888621,8.17870475 9.25332754,8.18433266 L9.21778182,8.22048421 L9.18027525,8.25804665 C8.5815908,8.85144049 8.05076972,9.08000625 7.53970852,8.97554732 C7.31572466,8.92976588 7.10997629,8.82319024 6.90127825,8.66008966 L6.87233768,8.63721146 L6.84414731,8.61437646 L6.81641092,8.5913207 L6.79802592,8.57569719 L6.77962327,8.55978002 L6.76111519,8.54349099 L6.73296341,8.51818909 L6.70408074,8.49161055 L6.67417094,8.46349138 L6.64293779,8.43356761 L6.59872309,8.39040587 L6.5753166,8.36725042 L6.52546653,8.3173993 L6.44178213,8.23259885 L6.37199578,8.16146508 L6.31577694,8.10480166 L6.26966975,8.05896272 L6.23817165,8.02804531 L6.20763999,7.99843574 L6.17803167,7.9701062 L6.14930359,7.94302884 L6.12141262,7.91717586 L6.09431568,7.89251941 L6.06796966,7.86903168 L6.04233145,7.84668484 L6.01735794,7.82545106 C6.01324869,7.8220033 6.00916534,7.81860077 6.00510698,7.81524287 L5.98104971,7.7956265 C5.96916391,7.78608152 5.95748695,7.77692784 5.94599461,7.76814982 L5.92324887,7.75109002 C5.90823956,7.74004455 5.89352971,7.72964837 5.87906183,7.71986438 L5.85753432,7.70564289 C5.76141253,7.64367056 5.6750066,7.60874566 5.58064067,7.58945763 C5.41447862,7.55549475 5.22858396,7.59024079 5.01200074,7.71320977 L4.98532622,7.72870946 C4.93166744,7.760596 4.8761413,7.79782148 4.81858687,7.84067252 L4.78963989,7.86256816 C4.77993406,7.87002386 4.77017114,7.87763715 4.76035039,7.88540936 L4.7307139,7.90920408 L4.70072596,7.93396027 C4.68564338,7.94658006 4.67042732,7.9595634 4.65507528,7.97291475 L4.62418913,8.00010947 L4.59293589,8.02829353 L4.56131107,8.0574749 L4.52931021,8.08766153 L4.49692883,8.1188614 C4.49149998,8.12414625 4.48605509,8.12947366 4.48059406,8.13484378 L4.44763353,8.16757841 C4.4421076,8.17312006 4.43656534,8.17870475 4.43100667,8.18433266 L4.41428132,8.20134618 L4,7.79865382 L4.01882038,7.77951579 L4.05632695,7.74195335 C4.6550114,7.14855951 5.18583248,6.91999375 5.69689368,7.02445268 C5.92087754,7.07023412 6.12662591,7.17680976 6.33532395,7.33991034 L6.36426452,7.36278854 L6.39245489,7.38562354 L6.42019128,7.4086793 L6.43857628,7.42430281 L6.45697893,7.44021998 L6.47548701,7.45650901 L6.50363878,7.48181091 L6.53252146,7.50838945 L6.56243126,7.53650862 L6.59366441,7.56643239 L6.63787911,7.60959413 L6.6612856,7.63274958 L6.71113567,7.6826007 L6.79482007,7.76740115 L6.86460642,7.83853492 L6.92082526,7.89519834 L6.96693244,7.94103728 L6.99843055,7.97195469 L7.0289622,8.00156426 L7.05857053,8.0298938 L7.08729861,8.05697116 L7.11518958,8.08282414 L7.14228651,8.10748059 L7.16863254,8.13096832 L7.19427075,8.15331516 L7.21924426,8.17454894 C7.22335351,8.1779967 7.22743686,8.18139923 7.23149522,8.18475713 L7.25555249,8.2043735 C7.26743829,8.21391848 7.27911525,8.22307216 7.29060759,8.23185018 L7.31335333,8.24890998 C7.32836264,8.25995545 7.34307249,8.27035163 7.35754037,8.28013562 L7.37906788,8.29435711 C7.47518967,8.35632944 7.5615956,8.39125434 7.65596153,8.41054237 C7.82212358,8.44450525 8.00801824,8.40975921 8.22460146,8.28679023 L8.25127598,8.27129054 C8.30493476,8.239404 8.3604609,8.20217852 8.41801533,8.15932748 L8.44696231,8.13743184 C8.45666814,8.12997614 8.46643106,8.12236285 8.47625181,8.11459064 L8.5058883,8.09079592 L8.53587624,8.06603973 C8.55095882,8.05341994 8.56617488,8.0404366 8.58152692,8.02708525 L8.61241306,7.99989053 L8.64366631,7.97170647 L8.67529113,7.9425251 L8.70729199,7.91233847 L8.73967337,7.8811386 C8.74510222,7.87585375 8.75054711,7.87052634 8.75600814,7.86515622 L8.78896866,7.83242159 C8.7944946,7.82687994 8.80003686,7.82129525 8.80559553,7.81566734 L8.84114125,7.77951579 Z" id="Path" fill="#000000" fill-rule="nonzero" transform="translate(8.4241, 8) scale(1, -1) translate(-8.4241, -8)"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/fast-pitch.svg b/images/fast-pitch.svg
new file mode 100644 (file)
index 0000000..2b33b10
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Fast Pitch</title>
+    <g id="Fast-Pitch" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <line x1="-6.44908963e-17" y1="8" x2="16" y2="8" id="Line" stroke="#000000" stroke-linecap="square"></line>
+        <polygon id="Triangle" fill="#000000" points="8 4 13 7 3 7"></polygon>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/fast-roll.svg b/images/fast-roll.svg
new file mode 100644 (file)
index 0000000..5b31f82
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Fast Roll</title>
+    <g id="Fast-Roll" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <line x1="8" y1="7.5" x2="8" y2="8.5" id="Line" stroke="#000000" stroke-linecap="square"></line>
+        <polygon id="Triangle" fill="#000000" transform="translate(7, 4.5) rotate(-270) translate(-7, -4.5)" points="7 3.5 9.5 5.5 4.5 5.5"></polygon>
+        <polygon id="Triangle" fill="#000000" transform="translate(9, 11.5) rotate(-90) translate(-9, -11.5)" points="9 10.5 11.5 12.5 6.5 12.5"></polygon>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/fast-yaw.svg b/images/fast-yaw.svg
new file mode 100644 (file)
index 0000000..08fb702
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Fast Yaw</title>
+    <g id="Fast-Yaw" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <line x1="8" y1="-2.57571742e-14" x2="8" y2="16" id="Line" stroke="#000000" stroke-linecap="square"></line>
+        <polygon id="Triangle" fill="#000000" transform="translate(10.5, 8) rotate(-270) translate(-10.5, -8)" points="10.5 6.5 15.5 9.5 5.5 9.5"></polygon>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/ha.svg b/images/ha.svg
new file mode 100644 (file)
index 0000000..8ce7cdc
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>High Amplitude</title>
+    <g id="High-Amplitude" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M8.75419133,6.67709472 L8.82201413,6.45172009 C9.90460972,2.89135706 10.8644886,1.51996247 11.7886359,2.1467161 C12.1936639,2.42140472 12.5657167,3.06085857 12.9431035,4.03946205 L12.9954365,4.17673124 L13.0464129,4.31374122 L13.0965683,4.4520758 L13.1298137,4.54581688 L13.1630911,4.64131988 L13.1965591,4.73905409 L13.2474657,4.89086545 L13.299694,5.05033672 L13.3537796,5.21905174 L13.4102582,5.39859431 L13.4902112,5.65756478 L13.532537,5.79649747 L13.6226804,6.09560421 L13.7740061,6.60440689 L13.9207734,7.10085705 L14.0061178,7.38710712 L14.1057403,7.71739859 L14.190429,7.99366431 L14.2692491,8.24571768 L14.3189116,8.40134283 L14.36653,8.54776826 L14.41233,8.68561851 L14.4565374,8.81551809 C14.4710328,8.85756223 14.4853004,8.89838536 14.4993778,8.93809155 L14.5410768,9.05396341 C14.5685459,9.12911623 14.5954046,9.20021768 14.6219539,9.26810046 L14.6615832,9.36761471 L14.700974,9.46292549 L14.7403519,9.55465733 C14.7535008,9.58470751 14.7666852,9.61426529 14.7799427,9.64343476 L14.8199721,9.7298823 C14.8266927,9.74413077 14.833441,9.75830818 14.8402217,9.77242754 C15.1565445,10.4311026 15.4436586,10.6629071 15.7626065,10.4745073 L15.792603,10.4556139 L16,13.8475443 C15.3869529,14.2574991 14.8237767,13.8276124 14.2826645,12.7008609 L14.2420718,12.6153295 L14.2022624,12.5293718 C14.1956861,12.5149923 14.1891374,12.5005818 14.1826139,12.4861336 L14.1437544,12.3989401 L14.1053783,12.3105248 L14.0673656,12.2205699 L14.0295964,12.128757 L13.9919506,12.0347681 L13.9543084,11.938285 C13.9480283,11.921979 13.9417434,11.9055558 13.9354512,11.8890087 L13.8975893,11.7881878 L13.8594312,11.6840774 C13.8530401,11.6664338 13.8466317,11.64864 13.8402035,11.6306891 L13.8013759,11.5210478 L13.761952,11.4073216 L13.721812,11.2891923 L13.6599967,11.1030467 L13.6175413,10.9725179 L13.5516908,10.7662817 L13.4828793,10.5465594 L13.410702,10.3122774 L13.334754,10.0623619 L13.226925,9.70296666 L12.9749333,8.85296414 L12.9077568,8.62880998 L12.8243816,8.35377633 L12.7674239,8.16827186 L12.7122138,7.99061446 L12.6586733,7.82063718 L12.6067246,7.65817307 L12.5562896,7.50305515 L12.5072905,7.35511648 L12.4596492,7.21419009 L12.4132878,7.08010903 L12.3681285,6.95270634 C12.3606978,6.93201982 12.3533139,6.9116046 12.3459752,6.89145721 L12.3024726,6.77375903 C12.2809796,6.71648909 12.2598643,6.66156702 12.2390828,6.6088989 L12.1979519,6.5065401 C12.1708107,6.44026731 12.144211,6.37789024 12.1180489,6.31918627 L12.0791209,6.23385733 C11.9053047,5.86202337 11.7490577,5.65247398 11.5784166,5.53674578 C11.2779473,5.33296849 10.9417957,5.54144473 10.5501503,6.27925865 L10.501915,6.37225676 C10.4048843,6.56357599 10.3044769,6.78692888 10.2004017,7.04403513 L10.1480572,7.17540899 C10.1305062,7.22014318 10.112852,7.26582292 10.0950933,7.31245618 L10.0415019,7.45522447 L9.98727494,7.60376163 C9.96000124,7.67948038 9.93248619,7.75738038 9.90472523,7.83748848 L9.8488741,8.0006568 L9.79235914,8.16976118 L9.73517227,8.34484939 L9.67730541,8.5259692 L9.61875047,8.71316838 C9.60893352,8.74487751 9.59908757,8.77684194 9.58921244,8.80906266 L9.52961023,9.00547047 C9.51961773,9.03872034 9.50959572,9.0722285 9.49954402,9.10599593 L9.43526702,9.32290528 L9.36744422,9.54827991 C8.28484863,13.1086429 7.32496974,14.4800375 6.40082246,13.8532839 C5.9957945,13.5785953 5.62374161,12.9391414 5.24635485,11.9605379 L5.19402189,11.8232688 L5.1430455,11.6862588 L5.09289004,11.5479242 L5.05964461,11.4541831 L5.02636726,11.3586801 L4.99289928,11.2609459 L4.94199268,11.1091346 L4.8897644,10.9496633 L4.83567878,10.7809483 L4.77920017,10.6014057 L4.69924713,10.3424352 L4.6569214,10.2035025 L4.56677797,9.90439579 L4.41545223,9.39559311 L4.28925821,8.96879046 L4.1875982,8.62880998 L4.104223,8.35377633 L4.04726528,8.16827186 L3.99205516,7.99061446 L3.9385147,7.82063718 L3.88656596,7.65817307 L3.83613099,7.50305515 L3.78713185,7.35511648 L3.73949057,7.21419009 L3.69312923,7.08010903 L3.64796987,6.95270634 C3.64053914,6.93201982 3.63315525,6.9116046 3.62581657,6.89145721 L3.58231403,6.77375903 C3.56082103,6.71648909 3.53970571,6.66156702 3.5189242,6.6088989 L3.4777933,6.5065401 C3.4506521,6.44026731 3.42405241,6.37789024 3.39789028,6.31918627 L3.35896228,6.23385733 C3.18514613,5.86202337 3.02889908,5.65247398 2.85825802,5.53674578 C2.55778871,5.33296849 2.22163711,5.54144473 1.82999165,6.27925865 L1.78175636,6.37225676 C1.68472569,6.56357599 1.58431828,6.78692888 1.48024313,7.04403513 L1.42789857,7.17540899 C1.4103476,7.22014318 1.39269341,7.26582292 1.37493464,7.31245618 L1.32134325,7.45522447 L1.26711632,7.60376163 C1.23984263,7.67948038 1.21232758,7.75738038 1.18456662,7.83748848 L1.12871549,8.0006568 L1.07220053,8.16976118 L1.01501366,8.34484939 L0.957146793,8.5259692 L0.898591852,8.71316838 C0.888774906,8.74487751 0.878928954,8.77684194 0.869053827,8.80906266 L0.809451615,9.00547047 C0.799459114,9.03872034 0.789437102,9.0722285 0.779385408,9.10599593 L0.74914112,9.20807706 L0,6.79192294 L0.0340327145,6.67709472 L0.101855513,6.45172009 C1.1844511,2.89135706 2.14432999,1.51996247 3.06847728,2.1467161 C3.47350524,2.42140472 3.84555812,3.06085857 4.22294488,4.03946205 L4.27527785,4.17673124 L4.32625424,4.31374122 L4.3764097,4.4520758 L4.40965512,4.54581688 L4.44293247,4.64131988 L4.47640046,4.73905409 L4.52730706,4.89086545 L4.57953534,5.05033672 L4.63362096,5.21905174 L4.69009956,5.39859431 L4.77005261,5.65756478 L4.81237834,5.79649747 L4.90252177,6.09560421 L5.05384751,6.60440689 L5.18004152,7.03120954 L5.28170153,7.37119002 L5.36507673,7.64622367 L5.42203446,7.83172814 L5.47724458,8.00938554 L5.53078503,8.17936282 L5.58273377,8.34182693 L5.63316874,8.49694485 L5.68216789,8.64488352 L5.72980916,8.78580991 L5.77617051,8.91989097 L5.82132987,9.04729366 C5.82876059,9.06798018 5.83614448,9.0883954 5.84348316,9.10854279 L5.8869857,9.22624097 C5.9084787,9.28351091 5.92959403,9.33843298 5.95037553,9.3911011 L5.99150644,9.4934599 C6.01864763,9.55973269 6.04524733,9.62210976 6.07140945,9.68081373 L6.11033746,9.76614267 C6.28415361,10.1379766 6.44040066,10.347526 6.61104171,10.4632542 C6.91151102,10.6670315 7.24766262,10.4585553 7.63930808,9.72074135 L7.68754337,9.62774324 C7.78457404,9.43642401 7.88498146,9.21307112 7.9890566,8.95596487 L8.04140116,8.82459101 C8.05895213,8.77985682 8.07660633,8.73417708 8.0943651,8.68754382 L8.14795649,8.54477553 L8.20218341,8.39623837 C8.22945711,8.32051962 8.25697216,8.24261962 8.28473312,8.16251152 L8.34058425,7.9993432 L8.39709921,7.83023882 L8.45428608,7.65515061 L8.51215294,7.4740308 L8.57070788,7.28683162 C8.58052483,7.25512249 8.59037078,7.22315806 8.60024591,7.19093734 L8.65984812,6.99452953 C8.66984062,6.96127966 8.67986263,6.9277715 8.68991433,6.89400407 L8.75419133,6.67709472 Z" id="A-Wave" fill="#000000" fill-rule="nonzero"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/hf.svg b/images/hf.svg
new file mode 100644 (file)
index 0000000..9d90165
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>High Frequency</title>
+    <g id="High-Frequency" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M8.75419133,6.89757893 L8.82201413,6.70976674 C9.90460972,3.74279755 10.8644886,2.59996873 11.7886359,3.12226341 C12.1936639,3.3511706 12.5657167,3.88404881 12.9431035,4.69955171 L12.9954365,4.8139427 L13.0464129,4.92811768 L13.0965683,5.0433965 L13.1298137,5.12151407 L13.1630911,5.2010999 L13.1965591,5.28254507 L13.2474657,5.40905454 L13.299694,5.54194727 L13.3537796,5.68254311 L13.4102582,5.83216193 L13.4902112,6.04797065 L13.532537,6.16374789 L13.6226804,6.41300351 L13.7740061,6.83700574 L13.9207734,7.2507142 L14.0061178,7.48925593 L14.1057403,7.76449882 L14.190429,7.99472026 L14.2692491,8.20476473 L14.3189116,8.33445236 L14.36653,8.45647355 L14.41233,8.57134876 L14.4565374,8.67959841 C14.4710328,8.7146352 14.4853004,8.74865446 14.4993778,8.78174296 L14.5410768,8.87830284 C14.5685459,8.94093019 14.5954046,9.0001814 14.6219539,9.05675038 L14.6615832,9.13967893 L14.700974,9.21910458 L14.7403519,9.29554778 C14.7535008,9.32058959 14.7666852,9.34522108 14.7799427,9.36952896 L14.8199721,9.44156859 C14.8266927,9.45344231 14.833441,9.46525681 14.8402217,9.47702295 C15.1565445,10.0259189 15.4436586,10.2190893 15.7626065,10.0620894 L15.792603,10.0463449 L16,12.8729536 C15.3869529,13.2145826 14.8237767,12.8563437 14.2826645,11.9173841 L14.2420718,11.8461079 L14.2022624,11.7744765 C14.1956861,11.7624936 14.1891374,11.7504848 14.1826139,11.7384447 L14.1437544,11.6657834 L14.1053783,11.592104 L14.0673656,11.5171416 L14.0295964,11.4406309 L13.9919506,11.3623068 L13.9543084,11.2819042 C13.9480283,11.2683158 13.9417434,11.2546298 13.9354512,11.2408406 L13.8975893,11.1568232 L13.8594312,11.0700645 C13.8530401,11.0553615 13.8466317,11.0405333 13.8402035,11.0255743 L13.8013759,10.9342065 L13.761952,10.8394347 L13.721812,10.7409936 L13.6599967,10.5858723 L13.6175413,10.4770982 L13.5516908,10.3052347 L13.4828793,10.1221329 L13.410702,9.92689787 L13.334754,9.71863496 L13.226925,9.41913889 L12.9749333,8.71080345 L12.9077568,8.52400831 L12.8243816,8.29481361 L12.7674239,8.14022655 L12.7122138,7.99217872 L12.6586733,7.85053099 L12.6067246,7.71514422 L12.5562896,7.58587929 L12.5072905,7.46259707 L12.4596492,7.34515841 L12.4132878,7.23342419 L12.3681285,7.12725529 C12.3606978,7.11001651 12.3533139,7.09300383 12.3459752,7.07621434 L12.3024726,6.97813252 C12.2809796,6.93040758 12.2598643,6.88463918 12.2390828,6.84074908 L12.1979519,6.75545008 C12.1708107,6.70022276 12.144211,6.64824186 12.1180489,6.59932189 L12.0791209,6.52821444 C11.9053047,6.21835281 11.7490577,6.04372832 11.5784166,5.94728815 C11.2779473,5.77747374 10.9417957,5.95120394 10.5501503,6.56604887 L10.501915,6.6435473 C10.4048843,6.80298 10.3044769,6.9891074 10.2004017,7.2033626 L10.1480572,7.31284082 C10.1305062,7.35011931 10.112852,7.38818577 10.0950933,7.42704682 L10.0415019,7.54602039 L9.98727494,7.66980135 C9.96000124,7.73290032 9.93248619,7.79781698 9.90472523,7.86457374 L9.8488741,8.00054734 L9.79235914,8.14146765 L9.73517227,8.28737449 L9.67730541,8.43830767 L9.61875047,8.59430699 C9.60893352,8.62073126 9.59908757,8.64736829 9.58921244,8.67421889 L9.52961023,8.83789206 C9.51961773,8.86560028 9.50959572,8.89352375 9.49954402,8.92166328 L9.43526702,9.10242107 L9.36744422,9.29023326 C8.28484863,12.2572025 7.32496974,13.4000313 6.40082246,12.8777366 C5.9957945,12.6488294 5.62374161,12.1159512 5.24635485,11.3004483 L5.19402189,11.1860573 L5.1430455,11.0718823 L5.09289004,10.9566035 L5.05964461,10.8784859 L5.02636726,10.7989001 L4.99289928,10.7174549 L4.94199268,10.5909455 L4.8897644,10.4580527 L4.83567878,10.3174569 L4.77920017,10.1678381 L4.69924713,9.95202935 L4.6569214,9.83625211 L4.56677797,9.58699649 L4.41545223,9.16299426 L4.28925821,8.80732539 L4.1875982,8.52400831 L4.104223,8.29481361 L4.04726528,8.14022655 L3.99205516,7.99217872 L3.9385147,7.85053099 L3.88656596,7.71514422 L3.83613099,7.58587929 L3.78713185,7.46259707 L3.73949057,7.34515841 L3.69312923,7.23342419 L3.64796987,7.12725529 C3.64053914,7.11001651 3.63315525,7.09300383 3.62581657,7.07621434 L3.58231403,6.97813252 C3.56082103,6.93040758 3.53970571,6.88463918 3.5189242,6.84074908 L3.4777933,6.75545008 C3.4506521,6.70022276 3.42405241,6.64824186 3.39789028,6.59932189 L3.35896228,6.52821444 C3.18514613,6.21835281 3.02889908,6.04372832 2.85825802,5.94728815 C2.55778871,5.77747374 2.22163711,5.95120394 1.82999165,6.56604887 L1.78175636,6.6435473 C1.68472569,6.80298 1.58431828,6.9891074 1.48024313,7.2033626 L1.42789857,7.31284082 C1.4103476,7.35011931 1.39269341,7.38818577 1.37493464,7.42704682 L1.32134325,7.54602039 L1.26711632,7.66980135 C1.23984263,7.73290032 1.21232758,7.79781698 1.18456662,7.86457374 L1.12871549,8.00054734 L1.07220053,8.14146765 L1.01501366,8.28737449 L0.957146793,8.43830767 L0.898591852,8.59430699 C0.888774906,8.62073126 0.878928954,8.64736829 0.869053827,8.67421889 L0.809451615,8.83789206 C0.799459114,8.86560028 0.789437102,8.89352375 0.779385408,8.92166328 L0.74914112,9.00673089 L0,6.99326911 L0.0340327145,6.89757893 L0.101855513,6.70976674 C1.1844511,3.74279755 2.14432999,2.59996873 3.06847728,3.12226341 C3.47350524,3.3511706 3.84555812,3.88404881 4.22294488,4.69955171 L4.27527785,4.8139427 L4.32625424,4.92811768 L4.3764097,5.0433965 L4.40965512,5.12151407 L4.44293247,5.2010999 L4.47640046,5.28254507 L4.52730706,5.40905454 L4.57953534,5.54194727 L4.63362096,5.68254311 L4.69009956,5.83216193 L4.77005261,6.04797065 L4.81237834,6.16374789 L4.90252177,6.41300351 L5.05384751,6.83700574 L5.18004152,7.19267461 L5.28170153,7.47599169 L5.36507673,7.70518639 L5.42203446,7.85977345 L5.47724458,8.00782128 L5.53078503,8.14946901 L5.58273377,8.28485578 L5.63316874,8.41412071 L5.68216789,8.53740293 L5.72980916,8.65484159 L5.77617051,8.76657581 L5.82132987,8.87274471 C5.82876059,8.88998349 5.83614448,8.90699617 5.84348316,8.92378566 L5.8869857,9.02186748 C5.9084787,9.06959242 5.92959403,9.11536082 5.95037553,9.15925092 L5.99150644,9.24454992 C6.01864763,9.29977724 6.04524733,9.35175814 6.07140945,9.40067811 L6.11033746,9.47178556 C6.28415361,9.78164719 6.44040066,9.95627168 6.61104171,10.0527119 C6.91151102,10.2225263 7.24766262,10.0487961 7.63930808,9.43395113 L7.68754337,9.3564527 C7.78457404,9.19702 7.88498146,9.0108926 7.9890566,8.7966374 L8.04140116,8.68715918 C8.05895213,8.64988069 8.07660633,8.61181423 8.0943651,8.57295318 L8.14795649,8.45397961 L8.20218341,8.33019865 C8.22945711,8.26709968 8.25697216,8.20218302 8.28473312,8.13542626 L8.34058425,7.99945266 L8.39709921,7.85853235 L8.45428608,7.71262551 L8.51215294,7.56169233 L8.57070788,7.40569301 C8.58052483,7.37926874 8.59037078,7.35263171 8.60024591,7.32578111 L8.65984812,7.16210794 C8.66984062,7.13439972 8.67986263,7.10647625 8.68991433,7.07833672 L8.75419133,6.89757893 Z" id="A-Wave" fill="#000000" fill-rule="nonzero"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/la.svg b/images/la.svg
new file mode 100644 (file)
index 0000000..23f8167
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Low Amplitude</title>
+    <g id="Low-Amplitude" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <path d="M8.75419133,7.55903157 L8.82201413,7.4839067 C9.90460972,6.29711902 10.8644886,5.83998749 11.7886359,6.04890537 C12.1936639,6.14046824 12.5657167,6.35361952 12.9431035,6.67982068 L12.9954365,6.72557708 L13.0464129,6.77124707 L13.0965683,6.8173586 L13.1298137,6.84860563 L13.1630911,6.88043996 L13.1965591,6.91301803 L13.2474657,6.96362182 L13.299694,7.01677891 L13.3537796,7.07301725 L13.4102582,7.13286477 L13.4902112,7.21918826 L13.532537,7.26549916 L13.6226804,7.3652014 L13.7740061,7.5348023 L13.9207734,7.70028568 L14.0061178,7.79570237 L14.1057403,7.90579953 L14.190429,7.9978881 L14.2692491,8.08190589 L14.3189116,8.13378094 L14.36653,8.18258942 L14.41233,8.2285395 L14.4565374,8.27183936 C14.4710328,8.28585408 14.4853004,8.29946179 14.4993778,8.31269718 L14.5410768,8.35132114 C14.5685459,8.37637208 14.5954046,8.40007256 14.6219539,8.42270015 L14.6615832,8.45587157 L14.700974,8.48764183 L14.7403519,8.51821911 C14.7535008,8.52823584 14.7666852,8.53808843 14.7799427,8.54781159 L14.8199721,8.57662743 C14.8266927,8.58137692 14.833441,8.58610273 14.8402217,8.59080918 C15.1565445,8.81036754 15.4436586,8.88763571 15.7626065,8.82483576 L15.792603,8.81853797 L16,9.94918143 C15.3869529,10.085833 14.8237767,9.94253746 14.2826645,9.56695364 L14.2420718,9.53844316 L14.2022624,9.50979059 C14.1956861,9.50499743 14.1891374,9.50019393 14.1826139,9.49537788 L14.1437544,9.46631335 L14.1053783,9.43684161 L14.0673656,9.40685663 L14.0295964,9.37625234 L13.9919506,9.34492271 L13.9543084,9.31276167 C13.9480283,9.30732633 13.9417434,9.30185192 13.9354512,9.29633624 L13.8975893,9.26272928 L13.8594312,9.22802579 C13.8530401,9.22214461 13.8466317,9.21621332 13.8402035,9.21022971 L13.8013759,9.17368261 L13.761952,9.13577387 L13.721812,9.09639744 L13.6599967,9.03434891 L13.6175413,8.9908393 L13.5516908,8.92209389 L13.4828793,8.84885315 L13.410702,8.77075915 L13.334754,8.68745398 L13.226925,8.56765555 L12.9749333,8.28432138 L12.9077568,8.20960333 L12.8243816,8.11792544 L12.7674239,8.05609062 L12.7122138,7.99687149 L12.6586733,7.94021239 L12.6067246,7.88605769 L12.5562896,7.83435172 L12.5072905,7.78503883 L12.4596492,7.73806336 L12.4132878,7.69336968 L12.3681285,7.65090211 C12.3606978,7.64400661 12.3533139,7.63720153 12.3459752,7.63048574 L12.3024726,7.59125301 C12.2809796,7.57216303 12.2598643,7.55385567 12.2390828,7.53629963 L12.1979519,7.50218003 C12.1708107,7.4800891 12.144211,7.45929675 12.1180489,7.43972876 L12.0791209,7.41128578 C11.9053047,7.28734112 11.7490577,7.21749133 11.5784166,7.17891526 C11.2779473,7.1109895 10.9417957,7.18048158 10.5501503,7.42641955 L10.501915,7.45741892 C10.4048843,7.521192 10.3044769,7.59564296 10.2004017,7.68134504 L10.1480572,7.72513633 C10.1305062,7.74004773 10.112852,7.75527431 10.0950933,7.77081873 L10.0415019,7.81840816 L9.98727494,7.86792054 C9.96000124,7.89316013 9.93248619,7.91912679 9.90472523,7.94582949 L9.8488741,8.00021893 L9.79235914,8.05658706 L9.73517227,8.1149498 L9.67730541,8.17532307 L9.61875047,8.23772279 C9.60893352,8.2482925 9.59908757,8.25894731 9.58921244,8.26968755 L9.52961023,8.33515682 C9.51961773,8.34624011 9.50959572,8.3574095 9.49954402,8.36866531 L9.43526702,8.44096843 L9.36744422,8.5160933 C8.28484863,9.70288098 7.32496974,10.1600125 6.40082246,9.95109463 C5.9957945,9.85953176 5.62374161,9.64638048 5.24635485,9.32017932 L5.19402189,9.27442292 L5.1430455,9.22875293 L5.09289004,9.1826414 L5.05964461,9.15139437 L5.02636726,9.11956004 L4.99289928,9.08698197 L4.94199268,9.03637818 L4.8897644,8.98322109 L4.83567878,8.92698275 L4.77920017,8.86713523 L4.69924713,8.78081174 L4.6569214,8.73450084 L4.56677797,8.6347986 L4.41545223,8.4651977 L4.28925821,8.32293015 L4.1875982,8.20960333 L4.104223,8.11792544 L4.04726528,8.05609062 L3.99205516,7.99687149 L3.9385147,7.94021239 L3.88656596,7.88605769 L3.83613099,7.83435172 L3.78713185,7.78503883 L3.73949057,7.73806336 L3.69312923,7.69336968 L3.64796987,7.65090211 C3.64053914,7.64400661 3.63315525,7.63720153 3.62581657,7.63048574 L3.58231403,7.59125301 C3.56082103,7.57216303 3.53970571,7.55385567 3.5189242,7.53629963 L3.4777933,7.50218003 C3.4506521,7.4800891 3.42405241,7.45929675 3.39789028,7.43972876 L3.35896228,7.41128578 C3.18514613,7.28734112 3.02889908,7.21749133 2.85825802,7.17891526 C2.55778871,7.1109895 2.22163711,7.18048158 1.82999165,7.42641955 L1.78175636,7.45741892 C1.68472569,7.521192 1.58431828,7.59564296 1.48024313,7.68134504 L1.42789857,7.72513633 C1.4103476,7.74004773 1.39269341,7.75527431 1.37493464,7.77081873 L1.32134325,7.81840816 L1.26711632,7.86792054 C1.23984263,7.89316013 1.21232758,7.91912679 1.18456662,7.94582949 L1.12871549,8.00021893 L1.07220053,8.05658706 L1.01501366,8.1149498 L0.957146793,8.17532307 L0.898591852,8.23772279 C0.888774906,8.2482925 0.878928954,8.25894731 0.869053827,8.26968755 L0.809451615,8.33515682 C0.799459114,8.34624011 0.789437102,8.3574095 0.779385408,8.36866531 L0.74914112,8.40269235 L0,7.59730765 L0.0340327145,7.55903157 L0.101855513,7.4839067 C1.1844511,6.29711902 2.14432999,5.83998749 3.06847728,6.04890537 C3.47350524,6.14046824 3.84555812,6.35361952 4.22294488,6.67982068 L4.27527785,6.72557708 L4.32625424,6.77124707 L4.3764097,6.8173586 L4.40965512,6.84860563 L4.44293247,6.88043996 L4.47640046,6.91301803 L4.52730706,6.96362182 L4.57953534,7.01677891 L4.63362096,7.07301725 L4.69009956,7.13286477 L4.77005261,7.21918826 L4.81237834,7.26549916 L4.90252177,7.3652014 L5.05384751,7.5348023 L5.18004152,7.67706985 L5.28170153,7.79039667 L5.36507673,7.88207456 L5.42203446,7.94390938 L5.47724458,8.00312851 L5.53078503,8.05978761 L5.58273377,8.11394231 L5.63316874,8.16564828 L5.68216789,8.21496117 L5.72980916,8.26193664 L5.77617051,8.30663032 L5.82132987,8.34909789 C5.82876059,8.35599339 5.83614448,8.36279847 5.84348316,8.36951426 L5.8869857,8.40874699 C5.9084787,8.42783697 5.92959403,8.44614433 5.95037553,8.46370037 L5.99150644,8.49781997 C6.01864763,8.5199109 6.04524733,8.54070325 6.07140945,8.56027124 L6.11033746,8.58871422 C6.28415361,8.71265888 6.44040066,8.78250867 6.61104171,8.82108474 C6.91151102,8.8890105 7.24766262,8.81951842 7.63930808,8.57358045 L7.68754337,8.54258108 C7.78457404,8.478808 7.88498146,8.40435704 7.9890566,8.31865496 L8.04140116,8.27486367 C8.05895213,8.25995227 8.07660633,8.24472569 8.0943651,8.22918127 L8.14795649,8.18159184 L8.20218341,8.13207946 C8.22945711,8.10683987 8.25697216,8.08087321 8.28473312,8.05417051 L8.34058425,7.99978107 L8.39709921,7.94341294 L8.45428608,7.8850502 L8.51215294,7.82467693 L8.57070788,7.76227721 C8.58052483,7.7517075 8.59037078,7.74105269 8.60024591,7.73031245 L8.65984812,7.66484318 C8.66984062,7.65375989 8.67986263,7.6425905 8.68991433,7.63133469 L8.75419133,7.55903157 Z" id="A-Wave-Copy" fill="#000000" fill-rule="nonzero"></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/lf.svg b/images/lf.svg
new file mode 100644 (file)
index 0000000..99a49e3
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Low Frequency</title>
+    <g id="Low-Frequency" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M-2.07542017,7.33654131 L-1.89387123,7.15472197 C1.00403461,4.28242508 3.57345079,3.17606251 6.04722016,3.68169142 C7.13140423,3.90329446 8.12732019,4.41916919 9.13751393,5.20865041 L9.27759948,5.31939133 L9.41405373,5.42992312 L9.54831052,5.54152353 L9.6373023,5.61714847 L9.72637954,5.69419482 L9.81596707,5.77304117 L9.95223451,5.89551388 L10.0920399,6.02416617 L10.2368169,6.16027577 L10.3879996,6.30512044 L10.6020189,6.51404297 L10.715317,6.62612591 L10.9566141,6.86742808 L11.3616848,7.27790094 L11.7545533,7.67840847 L11.9830044,7.90933863 L12.2496751,8.17579886 L12.476371,8.39867422 L12.6873575,8.60201643 L12.8202947,8.72756589 L12.9477604,8.84569354 L13.0703585,8.95690322 L13.1886933,9.06169876 C13.2274949,9.09561756 13.2656865,9.12855132 13.303369,9.160584 L13.4149896,9.25406277 C13.4885192,9.31469177 13.5604149,9.37205234 13.6314822,9.42681628 L13.7375625,9.50709868 L13.8430043,9.58398995 L13.9484117,9.65799394 C13.9836089,9.68223671 14.018901,9.70608223 14.0543888,9.72961448 L14.1615399,9.79935541 C14.1795298,9.81085026 14.1975938,9.82228778 14.2157445,9.83367847 C15.0624813,10.3650598 15.8310322,10.5520664 16.684796,10.4000762 L16.765091,10.3848341 L17.3202539,13.1212492 C15.6792414,13.4519772 14.1717242,13.1051693 12.723268,12.1961707 L12.614609,12.1271689 L12.5080466,12.0578231 C12.4904431,12.0462226 12.4729136,12.034597 12.4554513,12.0229411 L12.3514318,11.9525983 L12.2487062,11.8812699 L12.1469533,11.8086994 L12.0458521,11.7346301 L11.9450815,11.6588052 L11.8443203,11.5809682 C11.8275097,11.5678134 11.8106862,11.5545641 11.7938429,11.5412149 L11.6924939,11.4598784 L11.5903517,11.375888 C11.573244,11.3616542 11.5560899,11.3472991 11.5388827,11.3328175 L11.4349485,11.2443651 L11.3294182,11.1526173 L11.2219708,11.0573174 L11.0565028,10.9071458 L10.9428578,10.8018426 L10.7665882,10.635463 L10.5823927,10.4582037 L10.3891876,10.2691984 L10.1858892,10.0675809 L9.89725089,9.77764142 L9.22271624,9.09190808 L9.04289748,8.91107335 L8.81971767,8.68919196 L8.66725249,8.53953758 L8.51946533,8.39621377 L8.37614755,8.25908584 L8.2370905,8.1280191 L8.10208552,8.00287886 L7.97092397,7.88353042 L7.8433972,7.76983908 L7.71929656,7.66167017 L7.59841339,7.55888898 C7.57852273,7.54220027 7.55875744,7.52573044 7.53911316,7.50947669 L7.422665,7.41452453 C7.36513227,7.36832243 7.30861049,7.32401445 7.25298229,7.28152483 L7.14288255,7.19894761 C7.07023065,7.14548253 6.99902825,7.09516028 6.92899713,7.04780128 L6.82479414,6.97896278 C6.35952084,6.67898845 5.94127672,6.50993601 5.48450254,6.41657313 C4.68020242,6.25217729 3.78038748,6.42036397 2.73202586,7.01558996 L2.60290902,7.09061551 C2.34317607,7.24496091 2.07440421,7.42514922 1.79581452,7.6325678 L1.65569792,7.73855271 C1.60871727,7.77464169 1.56146029,7.81149349 1.51392338,7.84911453 L1.37046926,7.96429181 L1.22531393,8.08412307 C1.15230735,8.14520863 1.0786547,8.20805388 1.00434382,8.27268051 L0.854840787,8.40431536 L0.703560813,8.54073908 L0.550482261,8.68199022 L0.395583493,8.8281073 L0.238842873,8.97912888 C0.212564746,9.00470999 0.186208974,9.03049706 0.159775106,9.05649089 L0.000231138481,9.21494145 C-0.0265169171,9.24176554 -0.0533439704,9.268798 -0.0802504721,9.29603963 L-0.252307982,9.47102967 L-0.433856924,9.65284901 C-3.33176276,12.5251459 -5.90117894,13.6315085 -8.37494831,13.1258796 C-9.45913238,12.9042765 -10.4550483,12.3884018 -11.4652421,11.5989206 L-11.6053276,11.4881797 L-11.7417819,11.3776479 L-11.8760387,11.2660474 L-11.9650305,11.1904225 L-12.0541077,11.1133762 L-12.1436952,11.0345298 L-12.2799627,10.9120571 L-12.419768,10.7834048 L-12.5645451,10.6472952 L-12.7157277,10.5024505 L-12.9297471,10.293528 L-13.0430452,10.1814451 L-13.2843422,9.94014289 L-13.6894129,9.52967004 L-14.0272107,9.18535012 L-14.2993355,8.91107335 L-14.5225153,8.68919196 L-14.6749805,8.53953758 L-14.8227677,8.39621377 L-14.9660855,8.25908584 L-15.1051425,8.1280191 L-15.2401475,8.00287886 L-15.371309,7.88353042 L-15.4988358,7.76983908 L-15.6229365,7.66167017 L-15.7438196,7.55888898 C-15.7637103,7.54220027 -15.7834756,7.52573044 -15.8031198,7.50947669 L-15.919568,7.41452453 C-15.9771007,7.36832243 -16.0336225,7.32401445 -16.0892507,7.28152483 L-16.1993505,7.19894761 C-16.2720024,7.14548253 -16.3432048,7.09516028 -16.4132359,7.04780128 L-16.5174389,6.97896278 C-16.9827122,6.67898845 -17.4009563,6.50993601 -17.8577305,6.41657313 C-18.6620306,6.25217729 -19.5618455,6.42036397 -20.6102071,7.01558996 L-20.739324,7.09061551 C-20.9990569,7.24496091 -21.2678288,7.42514922 -21.5464185,7.6325678 L-21.6865351,7.73855271 C-21.7335157,7.77464169 -21.7807727,7.81149349 -21.8283096,7.84911453 L-21.9717637,7.96429181 L-22.1169191,8.08412307 C-22.1899257,8.14520863 -22.2635783,8.20805388 -22.3378892,8.27268051 L-22.4873922,8.40431536 L-22.6386722,8.54073908 L-22.7917507,8.68199022 L-22.9466495,8.8281073 L-23.1033901,8.97912888 C-23.1296683,9.00470999 -23.156024,9.03049706 -23.1824579,9.05649089 L-23.3420019,9.21494145 C-23.3687499,9.24176554 -23.395577,9.268798 -23.4224835,9.29603963 L-23.5034418,9.37839284 L-25.5087524,7.42917813 L-25.4176532,7.33654131 L-25.2361042,7.15472197 C-22.3381984,4.28242508 -19.7687822,3.17606251 -17.2950129,3.68169142 C-16.2108288,3.90329446 -15.2149128,4.41916919 -14.2047191,5.20865041 L-14.0646335,5.31939133 L-13.9281793,5.42992312 L-13.7939225,5.54152353 L-13.7049307,5.61714847 L-13.6158535,5.69419482 L-13.5262659,5.77304117 L-13.3899985,5.89551388 L-13.2501932,6.02416617 L-13.1054161,6.16027577 L-12.9542334,6.30512044 L-12.7402141,6.51404297 L-12.626916,6.62612591 L-12.3856189,6.86742808 L-11.9805482,7.27790094 L-11.6427505,7.62222085 L-11.3706256,7.89649763 L-11.1474458,8.11837902 L-10.9949806,8.2680334 L-10.8471935,8.4113572 L-10.7038757,8.54848513 L-10.5648187,8.67955187 L-10.4298137,8.80469212 L-10.2986521,8.92404056 L-10.1711254,9.0377319 L-10.0470247,9.14590081 L-9.92614155,9.248682 C-9.90625089,9.26537071 -9.88648559,9.28184053 -9.86684132,9.29809429 L-9.75039316,9.39304644 C-9.69286043,9.43924855 -9.63633865,9.48355653 -9.58071044,9.52604615 L-9.47061071,9.60862336 C-9.39795881,9.66208845 -9.3267564,9.7124107 -9.25672528,9.7597697 L-9.1525223,9.8286082 C-8.687249,10.1285825 -8.26900488,10.297635 -7.81223069,10.3909979 C-7.00793058,10.5553937 -6.10811563,10.387207 -5.05975402,9.79198102 L-4.93063718,9.71695547 C-4.67090423,9.56261007 -4.40213237,9.38242176 -4.12354268,9.17500317 L-3.98342608,9.06901827 C-3.93644542,9.03292929 -3.88918844,8.99607749 -3.84165154,8.95845645 L-3.69819742,8.84327917 L-3.55304208,8.72344791 C-3.4800355,8.66236235 -3.40638286,8.5995171 -3.33207197,8.53489047 L-3.18256894,8.40325562 L-3.03128897,8.26683189 L-2.87821042,8.12558076 L-2.72331165,7.97946367 L-2.56657103,7.8284421 C-2.5402929,7.80286099 -2.51393713,7.77707392 -2.48750326,7.75108009 L-2.32795929,7.59262953 C-2.30121124,7.56580544 -2.27438418,7.53877298 -2.24747768,7.51153134 L-2.07542017,7.33654131 Z" id="Path" fill="#000000" fill-rule="nonzero"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/long.svg b/images/long.svg
new file mode 100644 (file)
index 0000000..4f816b1
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Long</title>
+    <g id="Long" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M8.75419133,6.89757893 L8.82201413,6.70976674 C9.90460972,3.74279755 10.8644886,2.59996873 11.7886359,3.12226341 C12.1936639,3.3511706 12.5657167,3.88404881 12.9431035,4.69955171 L12.9954365,4.8139427 L13.0464129,4.92811768 L13.0965683,5.0433965 L13.1298137,5.12151407 L13.1630911,5.2010999 L13.1965591,5.28254507 L13.2474657,5.40905454 L13.299694,5.54194727 L13.3537796,5.68254311 L13.4102582,5.83216193 L13.4902112,6.04797065 L13.532537,6.16374789 L13.6226804,6.41300351 L13.7740061,6.83700574 L13.9207734,7.2507142 L14.0061178,7.48925593 L14.1057403,7.76449882 L14.190429,7.99472026 L14.2692491,8.20476473 L14.3189116,8.33445236 L14.36653,8.45647355 L14.41233,8.57134876 L14.4565374,8.67959841 C14.4710328,8.7146352 14.4853004,8.74865446 14.4993778,8.78174296 L14.5410768,8.87830284 C14.5685459,8.94093019 14.5954046,9.0001814 14.6219539,9.05675038 L14.6615832,9.13967893 L14.700974,9.21910458 L14.7403519,9.29554778 C14.7535008,9.32058959 14.7666852,9.34522108 14.7799427,9.36952896 L14.8199721,9.44156859 C14.8266927,9.45344231 14.833441,9.46525681 14.8402217,9.47702295 C15.1565445,10.0259189 15.4436586,10.2190893 15.7626065,10.0620894 L15.792603,10.0463449 L16,12.8729536 C15.3869529,13.2145826 14.8237767,12.8563437 14.2826645,11.9173841 L14.2420718,11.8461079 L14.2022624,11.7744765 C14.1956861,11.7624936 14.1891374,11.7504848 14.1826139,11.7384447 L14.1437544,11.6657834 L14.1053783,11.592104 L14.0673656,11.5171416 L14.0295964,11.4406309 L13.9919506,11.3623068 L13.9543084,11.2819042 C13.9480283,11.2683158 13.9417434,11.2546298 13.9354512,11.2408406 L13.8975893,11.1568232 L13.8594312,11.0700645 C13.8530401,11.0553615 13.8466317,11.0405333 13.8402035,11.0255743 L13.8013759,10.9342065 L13.761952,10.8394347 L13.721812,10.7409936 L13.6599967,10.5858723 L13.6175413,10.4770982 L13.5516908,10.3052347 L13.4828793,10.1221329 L13.410702,9.92689787 L13.334754,9.71863496 L13.226925,9.41913889 L12.9749333,8.71080345 L12.9077568,8.52400831 L12.8243816,8.29481361 L12.7674239,8.14022655 L12.7122138,7.99217872 L12.6586733,7.85053099 L12.6067246,7.71514422 L12.5562896,7.58587929 L12.5072905,7.46259707 L12.4596492,7.34515841 L12.4132878,7.23342419 L12.3681285,7.12725529 C12.3606978,7.11001651 12.3533139,7.09300383 12.3459752,7.07621434 L12.3024726,6.97813252 C12.2809796,6.93040758 12.2598643,6.88463918 12.2390828,6.84074908 L12.1979519,6.75545008 C12.1708107,6.70022276 12.144211,6.64824186 12.1180489,6.59932189 L12.0791209,6.52821444 C11.9053047,6.21835281 11.7490577,6.04372832 11.5784166,5.94728815 C11.2779473,5.77747374 10.9417957,5.95120394 10.5501503,6.56604887 L10.501915,6.6435473 C10.4048843,6.80298 10.3044769,6.9891074 10.2004017,7.2033626 L10.1480572,7.31284082 C10.1305062,7.35011931 10.112852,7.38818577 10.0950933,7.42704682 L10.0415019,7.54602039 L9.98727494,7.66980135 C9.96000124,7.73290032 9.93248619,7.79781698 9.90472523,7.86457374 L9.8488741,8.00054734 L9.79235914,8.14146765 L9.73517227,8.28737449 L9.67730541,8.43830767 L9.61875047,8.59430699 C9.60893352,8.62073126 9.59908757,8.64736829 9.58921244,8.67421889 L9.52961023,8.83789206 C9.51961773,8.86560028 9.50959572,8.89352375 9.49954402,8.92166328 L9.43526702,9.10242107 L9.36744422,9.29023326 C8.28484863,12.2572025 7.32496974,13.4000313 6.40082246,12.8777366 C5.9957945,12.6488294 5.62374161,12.1159512 5.24635485,11.3004483 L5.19402189,11.1860573 L5.1430455,11.0718823 L5.09289004,10.9566035 L5.05964461,10.8784859 L5.02636726,10.7989001 L4.99289928,10.7174549 L4.94199268,10.5909455 L4.8897644,10.4580527 L4.83567878,10.3174569 L4.77920017,10.1678381 L4.69924713,9.95202935 L4.6569214,9.83625211 L4.56677797,9.58699649 L4.41545223,9.16299426 L4.28925821,8.80732539 L4.1875982,8.52400831 L4.104223,8.29481361 L4.04726528,8.14022655 L3.99205516,7.99217872 L3.9385147,7.85053099 L3.88656596,7.71514422 L3.83613099,7.58587929 L3.78713185,7.46259707 L3.73949057,7.34515841 L3.69312923,7.23342419 L3.64796987,7.12725529 C3.64053914,7.11001651 3.63315525,7.09300383 3.62581657,7.07621434 L3.58231403,6.97813252 C3.56082103,6.93040758 3.53970571,6.88463918 3.5189242,6.84074908 L3.4777933,6.75545008 C3.4506521,6.70022276 3.42405241,6.64824186 3.39789028,6.59932189 L3.35896228,6.52821444 C3.18514613,6.21835281 3.02889908,6.04372832 2.85825802,5.94728815 C2.55778871,5.77747374 2.22163711,5.95120394 1.82999165,6.56604887 L1.78175636,6.6435473 C1.68472569,6.80298 1.58431828,6.9891074 1.48024313,7.2033626 L1.42789857,7.31284082 C1.4103476,7.35011931 1.39269341,7.38818577 1.37493464,7.42704682 L1.32134325,7.54602039 L1.26711632,7.66980135 C1.23984263,7.73290032 1.21232758,7.79781698 1.18456662,7.86457374 L1.12871549,8.00054734 L1.07220053,8.14146765 L1.01501366,8.28737449 L0.957146793,8.43830767 L0.898591852,8.59430699 C0.888774906,8.62073126 0.878928954,8.64736829 0.869053827,8.67421889 L0.809451615,8.83789206 C0.799459114,8.86560028 0.789437102,8.89352375 0.779385408,8.92166328 L0.74914112,9.00673089 L0,6.99326911 L0.0340327145,6.89757893 L0.101855513,6.70976674 C1.1844511,3.74279755 2.14432999,2.59996873 3.06847728,3.12226341 C3.47350524,3.3511706 3.84555812,3.88404881 4.22294488,4.69955171 L4.27527785,4.8139427 L4.32625424,4.92811768 L4.3764097,5.0433965 L4.40965512,5.12151407 L4.44293247,5.2010999 L4.47640046,5.28254507 L4.52730706,5.40905454 L4.57953534,5.54194727 L4.63362096,5.68254311 L4.69009956,5.83216193 L4.77005261,6.04797065 L4.81237834,6.16374789 L4.90252177,6.41300351 L5.05384751,6.83700574 L5.18004152,7.19267461 L5.28170153,7.47599169 L5.36507673,7.70518639 L5.42203446,7.85977345 L5.47724458,8.00782128 L5.53078503,8.14946901 L5.58273377,8.28485578 L5.63316874,8.41412071 L5.68216789,8.53740293 L5.72980916,8.65484159 L5.77617051,8.76657581 L5.82132987,8.87274471 C5.82876059,8.88998349 5.83614448,8.90699617 5.84348316,8.92378566 L5.8869857,9.02186748 C5.9084787,9.06959242 5.92959403,9.11536082 5.95037553,9.15925092 L5.99150644,9.24454992 C6.01864763,9.29977724 6.04524733,9.35175814 6.07140945,9.40067811 L6.11033746,9.47178556 C6.28415361,9.78164719 6.44040066,9.95627168 6.61104171,10.0527119 C6.91151102,10.2225263 7.24766262,10.0487961 7.63930808,9.43395113 L7.68754337,9.3564527 C7.78457404,9.19702 7.88498146,9.0108926 7.9890566,8.7966374 L8.04140116,8.68715918 C8.05895213,8.64988069 8.07660633,8.61181423 8.0943651,8.57295318 L8.14795649,8.45397961 L8.20218341,8.33019865 C8.22945711,8.26709968 8.25697216,8.20218302 8.28473312,8.13542626 L8.34058425,7.99945266 L8.39709921,7.85853235 L8.45428608,7.71262551 L8.51215294,7.56169233 L8.57070788,7.40569301 C8.58052483,7.37926874 8.59037078,7.35263171 8.60024591,7.32578111 L8.65984812,7.16210794 C8.66984062,7.13439972 8.67986263,7.10647625 8.68991433,7.07833672 L8.75419133,6.89757893 Z" id="Path" fill="#000000" fill-rule="nonzero"></path>
+        <path d="M11.7541913,6.89757893 L11.8220141,6.70976674 C12.9046097,3.74279755 13.8644886,2.59996873 14.7886359,3.12226341 C15.1936639,3.3511706 15.5657167,3.88404881 15.9431035,4.69955171 L15.9954365,4.8139427 L16.0464129,4.92811768 L16.0965683,5.0433965 L16.1298137,5.12151407 L16.1630911,5.2010999 L16.1965591,5.28254507 L16.2474657,5.40905454 L16.299694,5.54194727 L16.3537796,5.68254311 L16.4102582,5.83216193 L16.4902112,6.04797065 L16.532537,6.16374789 L16.6226804,6.41300351 L16.7740061,6.83700574 L16.9207734,7.2507142 L17.0061178,7.48925593 L17.1057403,7.76449882 L17.190429,7.99472026 L17.2692491,8.20476473 L17.3189116,8.33445236 L17.36653,8.45647355 L17.41233,8.57134876 L17.4565374,8.67959841 C17.4710328,8.7146352 17.4853004,8.74865446 17.4993778,8.78174296 L17.5410768,8.87830284 C17.5685459,8.94093019 17.5954046,9.0001814 17.6219539,9.05675038 L17.6615832,9.13967893 L17.700974,9.21910458 L17.7403519,9.29554778 C17.7535008,9.32058959 17.7666852,9.34522108 17.7799427,9.36952896 L17.8199721,9.44156859 C17.8266927,9.45344231 17.833441,9.46525681 17.8402217,9.47702295 C18.1565445,10.0259189 18.4436586,10.2190893 18.7626065,10.0620894 L18.792603,10.0463449 L19,12.8729536 C18.3869529,13.2145826 17.8237767,12.8563437 17.2826645,11.9173841 L17.2420718,11.8461079 L17.2022624,11.7744765 C17.1956861,11.7624936 17.1891374,11.7504848 17.1826139,11.7384447 L17.1437544,11.6657834 L17.1053783,11.592104 L17.0673656,11.5171416 L17.0295964,11.4406309 L16.9919506,11.3623068 L16.9543084,11.2819042 C16.9480283,11.2683158 16.9417434,11.2546298 16.9354512,11.2408406 L16.8975893,11.1568232 L16.8594312,11.0700645 C16.8530401,11.0553615 16.8466317,11.0405333 16.8402035,11.0255743 L16.8013759,10.9342065 L16.761952,10.8394347 L16.721812,10.7409936 L16.6599967,10.5858723 L16.6175413,10.4770982 L16.5516908,10.3052347 L16.4828793,10.1221329 L16.410702,9.92689787 L16.334754,9.71863496 L16.226925,9.41913889 L15.9749333,8.71080345 L15.9077568,8.52400831 L15.8243816,8.29481361 L15.7674239,8.14022655 L15.7122138,7.99217872 L15.6586733,7.85053099 L15.6067246,7.71514422 L15.5562896,7.58587929 L15.5072905,7.46259707 L15.4596492,7.34515841 L15.4132878,7.23342419 L15.3681285,7.12725529 C15.3606978,7.11001651 15.3533139,7.09300383 15.3459752,7.07621434 L15.3024726,6.97813252 C15.2809796,6.93040758 15.2598643,6.88463918 15.2390828,6.84074908 L15.1979519,6.75545008 C15.1708107,6.70022276 15.144211,6.64824186 15.1180489,6.59932189 L15.0791209,6.52821444 C14.9053047,6.21835281 14.7490577,6.04372832 14.5784166,5.94728815 C14.2779473,5.77747374 13.9417957,5.95120394 13.5501503,6.56604887 L13.501915,6.6435473 C13.4048843,6.80298 13.3044769,6.9891074 13.2004017,7.2033626 L13.1480572,7.31284082 C13.1305062,7.35011931 13.112852,7.38818577 13.0950933,7.42704682 L13.0415019,7.54602039 L12.9872749,7.66980135 C12.9600012,7.73290032 12.9324862,7.79781698 12.9047252,7.86457374 L12.8488741,8.00054734 L12.7923591,8.14146765 L12.7351723,8.28737449 L12.6773054,8.43830767 L12.6187505,8.59430699 C12.6089335,8.62073126 12.5990876,8.64736829 12.5892124,8.67421889 L12.5296102,8.83789206 C12.5196177,8.86560028 12.5095957,8.89352375 12.499544,8.92166328 L12.435267,9.10242107 L12.3674442,9.29023326 C11.2848486,12.2572025 10.3249697,13.4000313 9.40082246,12.8777366 C8.9957945,12.6488294 8.62374161,12.1159512 8.24635485,11.3004483 L8.19402189,11.1860573 L8.1430455,11.0718823 L8.09289004,10.9566035 L8.05964461,10.8784859 L8.02636726,10.7989001 L7.99289928,10.7174549 L7.94199268,10.5909455 L7.8897644,10.4580527 L7.83567878,10.3174569 L7.77920017,10.1678381 L7.69924713,9.95202935 L7.6569214,9.83625211 L7.56677797,9.58699649 L7.41545223,9.16299426 L7.28925821,8.80732539 L7.1875982,8.52400831 L7.104223,8.29481361 L7.04726528,8.14022655 L6.99205516,7.99217872 L6.9385147,7.85053099 L6.88656596,7.71514422 L6.83613099,7.58587929 L6.78713185,7.46259707 L6.73949057,7.34515841 L6.69312923,7.23342419 L6.64796987,7.12725529 C6.64053914,7.11001651 6.63315525,7.09300383 6.62581657,7.07621434 L6.58231403,6.97813252 C6.56082103,6.93040758 6.53970571,6.88463918 6.5189242,6.84074908 L6.4777933,6.75545008 C6.4506521,6.70022276 6.42405241,6.64824186 6.39789028,6.59932189 L6.35896228,6.52821444 C6.18514613,6.21835281 6.02889908,6.04372832 5.85825802,5.94728815 C5.55778871,5.77747374 5.22163711,5.95120394 4.82999165,6.56604887 L4.78175636,6.6435473 C4.68472569,6.80298 4.58431828,6.9891074 4.48024313,7.2033626 L4.42789857,7.31284082 C4.4103476,7.35011931 4.39269341,7.38818577 4.37493464,7.42704682 L4.32134325,7.54602039 L4.26711632,7.66980135 C4.23984263,7.73290032 4.21232758,7.79781698 4.18456662,7.86457374 L4.12871549,8.00054734 L4.07220053,8.14146765 L4.01501366,8.28737449 L3.95714679,8.43830767 L3.89859185,8.59430699 C3.88877491,8.62073126 3.87892895,8.64736829 3.86905383,8.67421889 L3.80945162,8.83789206 C3.79945911,8.86560028 3.7894371,8.89352375 3.77938541,8.92166328 L3.74914112,9.00673089 L3,6.99326911 L3.03403271,6.89757893 L3.10185551,6.70976674 C4.1844511,3.74279755 5.14432999,2.59996873 6.06847728,3.12226341 C6.47350524,3.3511706 6.84555812,3.88404881 7.22294488,4.69955171 L7.27527785,4.8139427 L7.32625424,4.92811768 L7.3764097,5.0433965 L7.40965512,5.12151407 L7.44293247,5.2010999 L7.47640046,5.28254507 L7.52730706,5.40905454 L7.57953534,5.54194727 L7.63362096,5.68254311 L7.69009956,5.83216193 L7.77005261,6.04797065 L7.81237834,6.16374789 L7.90252177,6.41300351 L8.05384751,6.83700574 L8.18004152,7.19267461 L8.28170153,7.47599169 L8.36507673,7.70518639 L8.42203446,7.85977345 L8.47724458,8.00782128 L8.53078503,8.14946901 L8.58273377,8.28485578 L8.63316874,8.41412071 L8.68216789,8.53740293 L8.72980916,8.65484159 L8.77617051,8.76657581 L8.82132987,8.87274471 C8.82876059,8.88998349 8.83614448,8.90699617 8.84348316,8.92378566 L8.8869857,9.02186748 C8.9084787,9.06959242 8.92959403,9.11536082 8.95037553,9.15925092 L8.99150644,9.24454992 C9.01864763,9.29977724 9.04524733,9.35175814 9.07140945,9.40067811 L9.11033746,9.47178556 C9.28415361,9.78164719 9.44040066,9.95627168 9.61104171,10.0527119 C9.91151102,10.2225263 10.2476626,10.0487961 10.6393081,9.43395113 L10.6875434,9.3564527 C10.784574,9.19702 10.8849815,9.0108926 10.9890566,8.7966374 L11.0414012,8.68715918 C11.0589521,8.64988069 11.0766063,8.61181423 11.0943651,8.57295318 L11.1479565,8.45397961 L11.2021834,8.33019865 C11.2294571,8.26709968 11.2569722,8.20218302 11.2847331,8.13542626 L11.3405842,7.99945266 L11.3970992,7.85853235 L11.4542861,7.71262551 L11.5121529,7.56169233 L11.5707079,7.40569301 C11.5805248,7.37926874 11.5903708,7.35263171 11.6002459,7.32578111 L11.6598481,7.16210794 C11.6698406,7.13439972 11.6798626,7.10647625 11.6899143,7.07833672 L11.7541913,6.89757893 Z" id="Path" fill="#000000" fill-rule="nonzero" transform="translate(11, 8) scale(-1, 1) translate(-11, -8)"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/near.svg b/images/near.svg
new file mode 100644 (file)
index 0000000..7bd56b5
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Near</title>
+    <g id="Near" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M5.40819555,7.08088021 L5.63323501,6.85550557 C9.22534168,3.29514254 12.4102681,1.92374796 15.4766354,2.55050159 C16.8205385,2.82519021 18.0550287,3.46464406 19.307217,4.44324754 L19.4808604,4.58051673 L19.6500026,4.71752671 L19.816421,4.85586129 L19.926731,4.94960237 L20.0371469,5.04510537 L20.1481953,5.14283958 L20.317106,5.29465094 L20.4904021,5.45412221 L20.6698609,5.62283722 L20.8572597,5.8023798 L21.122548,6.06135027 L21.2629869,6.20028296 L21.5620873,6.4993897 L22.0641937,7.00819238 L22.5511749,7.50464253 L22.8343521,7.79089261 L23.1649044,8.12118407 L23.4459059,8.3974498 L23.7074348,8.64950317 L23.8722175,8.80512832 L24.0302178,8.95155375 L24.1821846,9.089404 L24.3288669,9.21930358 C24.3769634,9.26134772 24.424304,9.30217085 24.4710134,9.34187704 L24.609373,9.4577489 C24.7005169,9.53290172 24.7896354,9.60400317 24.877727,9.67188595 L25.0092191,9.7714002 L25.1399197,9.86671098 L25.2705777,9.95844282 C25.3142065,9.988493 25.357953,10.0180508 25.401942,10.0472202 L25.5347614,10.1336678 C25.5570608,10.1479163 25.5794521,10.1620937 25.6019508,10.176213 C26.6515256,10.8348881 27.6041848,11.0666926 28.66247,10.8782928 L28.7619999,10.8593994 L29.4501535,14.2513298 C27.4160322,14.6612846 25.5473854,14.2313979 23.7519478,13.1046464 L23.6172592,13.019115 L23.4851695,12.9331573 C23.463349,12.9187778 23.4416202,12.9043673 23.4199749,12.8899191 L23.2910372,12.8027255 L23.1637034,12.7143103 L23.0375753,12.6243554 L22.9122551,12.5325425 L22.7873446,12.4385536 L22.6624458,12.3420705 C22.6416082,12.3257645 22.6207545,12.3093412 22.5998765,12.2927942 L22.4742491,12.1919733 L22.3476383,12.0878629 C22.3264325,12.0702193 22.3051691,12.0524254 22.2838399,12.0344746 L22.1550079,11.9248333 L22.0241976,11.8111071 L21.8910109,11.6929778 L21.6859046,11.5068322 L21.5450356,11.3763034 L21.3265402,11.1700672 L21.0982201,10.9503449 L20.8587323,10.7160629 L20.6067332,10.4661474 L20.2489509,10.1067522 L19.4128297,9.25674963 L19.1899349,9.03259546 L18.9132918,8.75756182 L18.7243032,8.57205735 L18.5411133,8.39439995 L18.3634633,8.22442267 L18.1910948,8.06195855 L18.023749,7.90684064 L17.8611674,7.75890197 L17.7030913,7.61797558 L17.549262,7.48389452 L17.3994209,7.35649183 C17.3747654,7.33580531 17.3502653,7.31539009 17.3259152,7.2952427 L17.1815716,7.17754452 C17.1102567,7.12027458 17.040195,7.06535251 16.9712409,7.01268439 L16.8347665,6.91032559 C16.7447107,6.8440528 16.6564515,6.78167573 16.5696443,6.72297176 L16.4404792,6.63764282 C15.8637485,6.26580886 15.3453129,6.05625947 14.7791173,5.94053127 C13.782145,5.73675398 12.6667771,5.94523021 11.3672777,6.68304414 L11.2072306,6.77604225 C10.8852779,6.96736148 10.5521211,7.19071437 10.2067945,7.44782061 L10.0331126,7.57919448 C9.97487763,7.62392867 9.91630012,7.66960841 9.85737563,7.71624167 L9.6795567,7.85900996 L9.49962903,8.00754711 C9.40913354,8.08326587 9.31783721,8.16116587 9.22572496,8.24127397 L9.04040809,8.40444229 L8.85288861,8.57354667 L8.6631397,8.74863488 L8.47113453,8.92975469 L8.27684629,9.11695387 C8.24427317,9.148663 8.2116038,9.18062743 8.17883763,9.21284815 L7.9810745,9.40925596 C7.94791887,9.44250583 7.91466533,9.47601399 7.88131331,9.50978142 L7.66803898,9.72669077 L7.44299952,9.9520654 C3.85089285,13.5124284 0.66596638,14.883823 -2.40040083,14.2570694 C-3.744304,13.9823808 -4.9787942,13.3429269 -6.23098247,12.3643234 L-6.40462588,12.2270542 L-6.5737681,12.0900443 L-6.74018644,11.9517097 L-6.85049644,11.8579686 L-6.96091236,11.7624656 L-7.07196081,11.6647314 L-7.24087147,11.51292 L-7.41416754,11.3534488 L-7.59362635,11.1847338 L-7.7810252,11.0051912 L-8.04631344,10.7462207 L-8.18675234,10.607288 L-8.48585278,10.3081813 L-8.9879592,9.7993786 L-9.40667729,9.37257595 L-9.74399032,9.03259546 L-10.0206334,8.75756182 L-10.209622,8.57205735 L-10.392812,8.39439995 L-10.5704619,8.22442267 L-10.7428304,8.06195855 L-10.9101762,7.90684064 L-11.0727579,7.75890197 L-11.230834,7.61797558 L-11.3846633,7.48389452 L-11.5345043,7.35649183 C-11.5591598,7.33580531 -11.5836599,7.31539009 -11.6080101,7.2952427 L-11.7523537,7.17754452 C-11.8236685,7.12027458 -11.8937302,7.06535251 -11.9626843,7.01268439 L-12.0991587,6.91032559 C-12.1892146,6.8440528 -12.2774737,6.78167573 -12.364281,6.72297176 L-12.493446,6.63764282 C-13.0701768,6.26580886 -13.5886123,6.05625947 -14.154808,5.94053127 C-15.1517803,5.73675398 -16.2671482,5.94523021 -17.5666475,6.68304414 L-17.7266947,6.77604225 C-18.0486473,6.96736148 -18.3818042,7.19071437 -18.7271307,7.44782061 L-18.9008126,7.57919448 C-18.9590476,7.62392867 -19.0176251,7.66960841 -19.0765496,7.71624167 L-19.2543685,7.85900996 L-19.4342962,8.00754711 C-19.5247917,8.08326587 -19.616088,8.16116587 -19.7082003,8.24127397 L-19.8935172,8.40444229 L-20.0810366,8.57354667 L-20.2707855,8.74863488 L-20.4627907,8.92975469 L-20.657079,9.11695387 C-20.6896521,9.148663 -20.7223214,9.18062743 -20.7550876,9.21284815 L-20.9528508,9.40925596 C-20.9860064,9.44250583 -21.0192599,9.47601399 -21.0526119,9.50978142 L-21.152964,9.61186255 L-23.638652,7.19570842 L-23.5257297,7.08088021 L-23.3006902,6.85550557 C-19.7085836,3.29514254 -16.5236571,1.92374796 -13.4572899,2.55050159 C-12.1133867,2.82519021 -10.8788965,3.46464406 -9.62670825,4.44324754 L-9.45306484,4.58051673 L-9.28392263,4.71752671 L-9.11750428,4.85586129 L-9.00719428,4.94960237 L-8.89677837,5.04510537 L-8.78572991,5.14283958 L-8.61681925,5.29465094 L-8.44352318,5.45412221 L-8.26406438,5.62283722 L-8.07666552,5.8023798 L-7.81137728,6.06135027 L-7.67093838,6.20028296 L-7.37183795,6.4993897 L-6.86973152,7.00819238 L-6.45101343,7.43499502 L-6.1137004,7.77497551 L-5.83705729,8.05000916 L-5.64806868,8.23551363 L-5.46487873,8.41317103 L-5.28722881,8.58314831 L-5.11486028,8.74561242 L-4.94751451,8.90073034 L-4.78493287,9.04866901 L-4.62685673,9.1895954 L-4.47302746,9.32367646 L-4.32318642,9.45107915 C-4.2985309,9.47176567 -4.27403078,9.49218089 -4.24968067,9.51232828 L-4.10533705,9.63002646 C-4.0340222,9.6872964 -3.96396048,9.74221847 -3.8950064,9.79488659 L-3.75853199,9.89724539 C-3.66847614,9.96351818 -3.58021701,10.0258953 -3.49340976,10.0845992 L-3.36424468,10.1699282 C-2.78751396,10.5417621 -2.26907837,10.7513115 -1.70288276,10.8670397 C-0.705910471,11.070817 0.409457471,10.8623408 1.70895682,10.1245268 L1.86900393,10.0315287 C2.19095658,9.84020949 2.52411344,9.61685661 2.86944,9.35975036 L3.0431219,9.2283765 C3.10135689,9.18364231 3.1599344,9.13796257 3.2188589,9.09132931 L3.39667782,8.94856102 L3.57660549,8.80002386 C3.66710098,8.72430511 3.75839731,8.64640511 3.85050956,8.566297 L4.03582643,8.40312869 L4.22334591,8.23402431 L4.41309483,8.0589361 L4.6051,7.87781629 L4.79938824,7.69061711 C4.83196136,7.65890797 4.86463072,7.62694355 4.89739689,7.59472282 L5.09516003,7.39831502 C5.12831565,7.36506515 5.16156919,7.33155699 5.19492122,7.29778956 L5.40819555,7.08088021 Z" id="Path" fill="#000000" fill-rule="nonzero"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/no-pitch.svg b/images/no-pitch.svg
new file mode 100644 (file)
index 0000000..cb62dd4
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>No Pitch</title>
+    <g id="No-Pitch" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <line x1="-6.44908963e-17" y1="8" x2="16" y2="8" id="Line" stroke="#000000" stroke-linecap="square"></line>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/no-roll.svg b/images/no-roll.svg
new file mode 100644 (file)
index 0000000..ded155e
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>No Roll</title>
+    <g id="No-Roll" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <line x1="8" y1="7.5" x2="8" y2="8.5" id="Line" stroke="#000000" stroke-linecap="square"></line>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/no-yaw.svg b/images/no-yaw.svg
new file mode 100644 (file)
index 0000000..0acf9f1
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>No Yaw</title>
+    <g id="No-Yaw" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+        <line x1="8" y1="-2.57571742e-14" x2="8" y2="16" id="Line" stroke="#000000" stroke-linecap="square"></line>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/images/short.svg b/images/short.svg
new file mode 100644 (file)
index 0000000..a5dc1d8
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Short</title>
+    <g id="Short" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M6.04722016,3.68169142 C7.13140423,3.90329446 8.12732019,4.41916919 9.13751393,5.20865041 L9.27759948,5.31939133 L9.41405373,5.42992312 L9.54831052,5.54152353 L9.6373023,5.61714847 L9.72637954,5.69419482 L9.81596707,5.77304117 L9.95223451,5.89551388 L10.0920399,6.02416617 L10.2368169,6.16027577 L10.3879996,6.30512044 L10.6020189,6.51404297 L10.715317,6.62612591 L10.9566141,6.86742808 L11.3616848,7.27790094 L11.7545533,7.67840847 L11.9830044,7.90933863 L12.2496751,8.17579886 L10.1858892,10.0675809 L9.89725089,9.77764142 L9.22271624,9.09190808 L9.04289748,8.91107335 L8.81971767,8.68919196 L8.66725249,8.53953758 L8.51946533,8.39621377 L8.37614755,8.25908584 L8.2370905,8.1280191 L8.10208552,8.00287886 L7.97092397,7.88353042 L7.8433972,7.76983908 L7.71929656,7.66167017 L7.59841339,7.55888898 C7.57852273,7.54220027 7.55875744,7.52573044 7.53911316,7.50947669 L7.422665,7.41452453 C7.36513227,7.36832243 7.30861049,7.32401445 7.25298229,7.28152483 L7.14288255,7.19894761 C7.07023065,7.14548253 6.99902825,7.09516028 6.92899713,7.04780128 L6.82479414,6.97896278 C6.35952084,6.67898845 5.94127672,6.50993601 5.48450254,6.41657313 C4.68020242,6.25217729 3.78038748,6.42036397 2.73202586,7.01558996 L2.60290902,7.09061551 C2.42975372,7.19351244 2.56211738,6.16330727 3,4 C2.95301934,4.03608898 3.57345079,3.17606251 6.04722016,3.68169142 Z" id="Path" fill="#000000" fill-rule="nonzero"></path>
+        <rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="15" height="15"></rect>
+    </g>
+</svg>
\ No newline at end of file
index 6c7faf599bc18ee33120201ccbc5ba58ef7ed6a7..db8e5b4e314fb44a16b34962bdee0711e20b2d6c 100644 (file)
@@ -15,6 +15,9 @@
   </head>
   <body>
     <main>
+      <aside id="settings">
+        <h1>Settings</h1>
+      </aside>
       <canvas id="lissajous"></canvas>
     </main>
   </body>
diff --git a/lib/components/configuration.js b/lib/components/configuration.js
new file mode 100644 (file)
index 0000000..ddb4a27
--- /dev/null
@@ -0,0 +1,9 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Configuration extends Component {
+  constructor(config) {
+
+    super(config);
+    this.lineLength = this.lineLength || 1000;
+  }
+};
diff --git a/lib/components/radius.js b/lib/components/radius.js
new file mode 100644 (file)
index 0000000..2aa29a8
--- /dev/null
@@ -0,0 +1,9 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Radius extends Component {
+  constructor(config) {
+
+    super(config);
+    this.radius = this.radius || 5;
+  }
+};
diff --git a/lib/components/triple_amplitude.js b/lib/components/triple_amplitude.js
new file mode 100644 (file)
index 0000000..376c321
--- /dev/null
@@ -0,0 +1,12 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class TripleAmplitude extends Component {
+  constructor(config) {
+
+    super(config);
+
+    this.a = this.a || Math.random();
+    this.b = this.b || Math.random();
+    this.c = this.c || Math.random();
+  }
+};
index aa768e39cff7d1526c71676157b4f00e2a2baaaa..1f118b90ebb30eac84eeaa3fdef32a9986012162 100644 (file)
@@ -5,8 +5,8 @@ export default class TripleFrequency extends Component {
 
     super(config);
 
-    this.a = Math.random();
-    this.b = Math.random();
-    this.c = Math.random();
+    this.a = this.a || Math.random();
+    this.b = this.b || Math.random();
+    this.c = this.c || Math.random();
   }
 };
diff --git a/lib/components/up.js b/lib/components/up.js
new file mode 100644 (file)
index 0000000..f28cc20
--- /dev/null
@@ -0,0 +1,12 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Up extends Component {
+  constructor(config) {
+
+    super(config);
+
+    this.x = this.x || 0;
+    this.y = this.y || 1;
+    this.z = this.z || 0;
+  }
+};
index bb8e11e71242e903099e25bccefcb95a6ecc8d77..e8fa3c9c7eb54e61c088358aded7cefaaf6fc6ed 100644 (file)
@@ -1,3 +1,4 @@
 export const canvasId = 'lissajous';
+export const settingsId = 'settings';
 
 export const fps = 30;
index d9f0f472ddd617c2457cb64ce09f7f016332b174..99bc588698965a279661c6a2cc005b0a3df76c9e 100644 (file)
@@ -1,6 +1,7 @@
 import { Entity } from '@serpentity/serpentity';
 import Position from '@serpentity/components.position';
 import TripleFrequency from '../components/triple_frequency';
+import TripleAmplitude from '../components/triple_amplitude';
 import Color from '../components/color';
 
 export function lissajousCurve() {
@@ -8,6 +9,7 @@ export function lissajousCurve() {
   const entity = new Entity();
   entity.addComponent(new Position());
   entity.addComponent(new TripleFrequency());
+  entity.addComponent(new TripleAmplitude());
   entity.addComponent(new Color());
 
   return entity;
diff --git a/lib/factories/global.js b/lib/factories/global.js
new file mode 100644 (file)
index 0000000..c601bdb
--- /dev/null
@@ -0,0 +1,33 @@
+import { Entity } from '@serpentity/serpentity';
+import Position from '@serpentity/components.position';
+import EulerAngle from '@serpentity/components.euler_angle';
+import Velocity from '@serpentity/components.velocity';
+import Radius from '../components/radius';
+import Up from '../components/up';
+import Configuration from '../components/configuration';
+
+export function configuration() {
+
+  const entity = new Entity();
+  entity.addComponent(new Configuration());
+
+  return entity;
+}
+
+export function camera() {
+
+  const entity = new Entity();
+  entity.addComponent(new Position());
+  entity.addComponent(new EulerAngle());
+  entity.addComponent(new Velocity({
+    x: 0,
+    y: Math.PI / 180,
+    z: 0,
+  }));
+  entity.addComponent(new Radius({
+    radius: 5
+  }));
+  entity.addComponent(new Up());
+
+  return entity;
+}
diff --git a/lib/factories/ui.js b/lib/factories/ui.js
new file mode 100644 (file)
index 0000000..27ff6eb
--- /dev/null
@@ -0,0 +1,28 @@
+export function settingsContainer({id, label, level=2}) {
+
+  const container = document.createElement('div');
+  container.id = id;
+  container.innerHTML = `<h${level}>${label}</h${level}>`;
+  return container;
+};
+
+export function slider({min, max, step, get, set, label = '', className}) {
+
+  const sliderContainer = document.createElement('div');
+  sliderContainer.classList.add('slider');
+  sliderContainer.classList.add(className);
+  const labelElement = document.createElement('label');
+  labelElement.innerHTML = label;
+  const slider = document.createElement('input');
+  slider.type = 'range';
+  slider.min = min;
+  slider.max = max;
+  slider.step = step;
+  slider.value = get().toString();
+
+  slider.addEventListener('input', () => set(slider.value));
+
+  sliderContainer.appendChild(labelElement);
+  sliderContainer.appendChild(slider);
+  return sliderContainer;
+}
index 6a2ea8f9cc427f38372ec488f8298be956c4c6a7..60189578f817e292f9d1745c9a8e7ecb60d15607 100644 (file)
@@ -1,8 +1,18 @@
 import Serpentity from '@serpentity/serpentity';
+import { canvasId, settingsId, fps } from './config';
+
+// Systems
+import CameraRotator from './systems/camera_rotator';
 import WebGLRenderer from './systems/webgl_renderer';
 import LissajousPositionUpdater from './systems/lissajous_position_updater';
+import CameraAdjuster from './systems/camera_adjuster';
+import FrequencyAdjuster from './systems/frequency_adjuster';
+import AmplitudeAdjuster from './systems/amplitude_adjuster';
+import GlobalAdjuster from './systems/global_adjuster';
+
+// Factories
 import { lissajousCurve } from './factories/curves';
-import { canvasId, fps } from './config';
+import { camera, configuration } from './factories/global';
 
 const internals = {
 
@@ -16,12 +26,26 @@ const internals = {
     // Create entities
     const engine = new Serpentity();
 
-    // Add Systems
-    engine.addSystem(new LissajousPositionUpdater(canvasId));
-    engine.addSystem(new WebGLRenderer(canvasId));
-
     // Add Entities
+    engine.addEntity(camera());
     engine.addEntity(lissajousCurve());
+    engine.addEntity(configuration());
+
+    // Select elements
+    const canvas = document.getElementById(canvasId);
+    const settings = document.getElementById(settingsId);
+
+    // Add Systems
+    engine.addSystem(new CameraRotator());
+    engine.addSystem(new LissajousPositionUpdater());
+    engine.addSystem(new WebGLRenderer(canvas));
+
+    // IMPROVEMENT NOTE: I believe adjusters can be generalized further.
+    // We could potentially have a factory for this.
+    engine.addSystem(new GlobalAdjuster(settings));
+    engine.addSystem(new CameraAdjuster(settings));
+    engine.addSystem(new FrequencyAdjuster(settings));
+    engine.addSystem(new AmplitudeAdjuster(settings));
 
     internals.engine = engine;
   },
diff --git a/lib/nodes/ample.js b/lib/nodes/ample.js
new file mode 100644 (file)
index 0000000..e48d250
--- /dev/null
@@ -0,0 +1,7 @@
+import TripleAmplitude from '../components/triple_amplitude';
+import { Node } from '@serpentity/serpentity';
+
+export default class Ample extends Node {};
+Ample.types = {
+  amplitude: TripleAmplitude
+}
diff --git a/lib/nodes/cameras.js b/lib/nodes/cameras.js
new file mode 100644 (file)
index 0000000..56072d3
--- /dev/null
@@ -0,0 +1,15 @@
+import Position from '@serpentity/components.position';
+import EulerAngle from '@serpentity/components.euler_angle';
+import Velocity from '@serpentity/components.velocity';
+import Radius from '../components/radius';
+import Up from '../components/up';
+import { Node } from '@serpentity/serpentity';
+
+export default class Cameras extends Node {};
+Cameras.types = {
+  position: Position,
+  angle: EulerAngle,
+  velocity: Velocity,
+  radius: Radius,
+  up: Up
+}
diff --git a/lib/nodes/configurable.js b/lib/nodes/configurable.js
new file mode 100644 (file)
index 0000000..650f131
--- /dev/null
@@ -0,0 +1,7 @@
+import Configuration from '../components/configuration';
+import { Node } from '@serpentity/serpentity';
+
+export default class Configurable extends Node {};
+Configurable.types = {
+  configuration: Configuration,
+}
diff --git a/lib/nodes/frequent.js b/lib/nodes/frequent.js
new file mode 100644 (file)
index 0000000..de8de92
--- /dev/null
@@ -0,0 +1,7 @@
+import TripleFrequency from '../components/triple_frequency';
+import { Node } from '@serpentity/serpentity';
+
+export default class Frequent extends Node {};
+Frequent.types = {
+  frequency: TripleFrequency
+}
index 402aea6af9e9f3b7561b90bbe5e1d67ad67b63d7..55179e88f7f666e3153b2deaf167810df099f328 100644 (file)
@@ -1,9 +1,11 @@
 import Position from '@serpentity/components.position';
 import TripleFrequency from '../components/triple_frequency';
+import TripleAmplitude from '../components/triple_amplitude';
 import { Node } from '@serpentity/serpentity';
 
 export default class LissajousCurve extends Node {};
 LissajousCurve.types = {
   position: Position,
-  frequency: TripleFrequency
+  frequency: TripleFrequency,
+  amplitude: TripleAmplitude
 }
diff --git a/lib/systems/amplitude_adjuster.js b/lib/systems/amplitude_adjuster.js
new file mode 100644 (file)
index 0000000..71f7277
--- /dev/null
@@ -0,0 +1,64 @@
+import { System } from '@serpentity/serpentity';
+import Ample from '../nodes/ample';
+import { settingsContainer, slider } from '../factories/ui';
+
+const internals = {
+  symbols: {
+    a: '𝛢',
+    b: '𝛣',
+    c: '𝛧'
+  }
+};
+
+export default class AmplitudeAdjuster extends System {
+
+  constructor(container) {
+
+    super();
+    this.container = container;
+  }
+
+  added(engine){
+
+    this.nodes = engine.getNodes(Ample);
+    this.adjusterContainer = settingsContainer({
+      id: 'amplitude-adjuster',
+      label: 'Amplitude'
+    });
+
+    let i = 0;
+    for (const node of this.nodes) {
+      const nodeElement = settingsContainer({
+        id: `amplitude-adjuster-${i}`,
+        label: `ɣ${i + 1}`,
+        level: 3
+      });
+
+      ['a', 'b', 'c'].forEach(key => {
+        nodeElement.appendChild(slider({
+          min: '0',
+          max: '1',
+          step: '0.01',
+          label: internals.symbols[key],
+          className: `amplitude`,
+          get: () => node.amplitude[key].toString(),
+          set: (value) => (node.amplitude[key] = parseFloat(value))
+        }));
+      });
+
+      this.adjusterContainer.appendChild(nodeElement);
+      ++i;
+    }
+
+    this.container.appendChild(this.adjusterContainer);
+  }
+
+  removed(){
+
+    this.container.removeChild(this.adjusterContainer);
+    delete this.adjusterContainer;
+    delete this.nodes;
+  }
+
+  update(){}
+};
diff --git a/lib/systems/camera_adjuster.js b/lib/systems/camera_adjuster.js
new file mode 100644 (file)
index 0000000..3b6ea41
--- /dev/null
@@ -0,0 +1,74 @@
+import { System } from '@serpentity/serpentity';
+import Cameras from '../nodes/cameras';
+import { settingsContainer, slider } from '../factories/ui';
+
+const internals = {
+  symbols: {
+    x: '𝜃',
+    y: '𝜓',
+    z: '𝜙'
+  }
+};
+
+export default class CameraAdjuster extends System {
+
+  constructor(container) {
+
+    super();
+    this.container = container;
+  }
+
+  added(engine){
+
+    this.nodes = engine.getNodes(Cameras);
+    this.adjusterContainer = settingsContainer({
+      id: 'camera-adjuster',
+      label: 'Camera'
+    })
+
+    let i = 0;
+    for (const node of this.nodes) {
+      const nodeElement = settingsContainer({
+        id: `camera-adjuster-${i}`,
+        label: `C${i + 1}`,
+        level: 3
+      });
+
+      nodeElement.appendChild(slider({
+        min: '2',
+        max: '10',
+        step: '0.1',
+        label: '𝑟',
+        className: 'radius',
+        get: () => node.radius.radius.toString(),
+        set: (value) => (node.radius.radius = parseFloat(value))
+      }));
+
+      ['x', 'y', 'z'].forEach(key => {
+        nodeElement.appendChild(slider({
+          min: '0',
+          max: '0.5',
+          step: '0.001',
+          label: internals.symbols[key],
+          className: `rotation-${key}`,
+          get: () => node.velocity[key].toString(),
+          set: (value) => (node.velocity[key] = parseFloat(value))
+        }));
+      });
+
+      this.adjusterContainer.appendChild(nodeElement);
+      ++i;
+    }
+
+    this.container.appendChild(this.adjusterContainer);
+  }
+
+  removed(){
+
+    this.container.removeChild(this.adjusterContainer);
+    delete this.adjusterContainer;
+    delete this.nodes;
+  }
+
+  update(){}
+};
diff --git a/lib/systems/camera_rotator.js b/lib/systems/camera_rotator.js
new file mode 100644 (file)
index 0000000..3520f71
--- /dev/null
@@ -0,0 +1,49 @@
+import { mat4, vec3 } from 'gl-matrix';
+import { System } from '@serpentity/serpentity';
+import Cameras from '../nodes/cameras';
+
+export default class CameraRotator extends System {
+
+  constructor() {
+
+    super();
+  }
+
+  added(engine){
+
+    this.cameras = engine.getNodes(Cameras);
+  }
+
+  removed(){
+
+    delete this.cameras;
+  }
+
+  update(dt){
+
+    for (const camera of this.cameras) {
+
+      let rotationMatrix = mat4.create();
+      mat4.rotateY(rotationMatrix, rotationMatrix, camera.angle.yaw);
+      mat4.rotateX(rotationMatrix, rotationMatrix, camera.angle.pitch);
+      mat4.rotateZ(rotationMatrix, rotationMatrix, camera.angle.roll);
+
+      let eye = vec3.fromValues(0, 0, camera.radius.radius);
+      vec3.transformMat4(eye, eye, rotationMatrix);
+
+      camera.position.x = eye[0];
+      camera.position.y = eye[1];
+      camera.position.z = eye[2];
+
+      let up = vec3.fromValues(0, 1, 0);
+      vec3.transformMat4(up, up, rotationMatrix);
+      camera.up.x = up[0];
+      camera.up.y = up[1];
+      camera.up.z = up[2];
+
+      camera.angle.pitch = (camera.angle.pitch + camera.velocity.x * dt / 100 + 2 * Math.PI) % (2 * Math.PI);
+      camera.angle.yaw = (camera.angle.yaw + camera.velocity.y * dt / 100 + 2 * Math.PI) % (2 * Math.PI);
+      camera.angle.roll = (camera.angle.roll + camera.velocity.z * dt / 100 + 2 * Math.PI) % (2 * Math.PI);
+    }
+  }
+};
diff --git a/lib/systems/frequency_adjuster.js b/lib/systems/frequency_adjuster.js
new file mode 100644 (file)
index 0000000..6aa11d4
--- /dev/null
@@ -0,0 +1,64 @@
+import { System } from '@serpentity/serpentity';
+import Frequent from '../nodes/frequent';
+import { settingsContainer, slider } from '../factories/ui';
+
+const internals = {
+  symbols: {
+    a: '𝛼',
+    b: '𝛽',
+    c: '𝜁'
+  }
+};
+
+export default class FrequencyAdjuster extends System {
+
+  constructor(container) {
+
+    super();
+    this.container = container;
+  }
+
+  added(engine){
+
+    this.nodes = engine.getNodes(Frequent);
+    this.adjusterContainer = settingsContainer({
+      id: 'frequency-adjuster',
+      label: 'Frequency'
+    })
+
+    let i = 0;
+    for (const node of this.nodes) {
+      const nodeElement = settingsContainer({
+        id: `frequency-adjuster-${i}`,
+        label: `ɣ${i + 1}`,
+        level: 3
+      });
+
+      ['a', 'b', 'c'].forEach(key => {
+        nodeElement.appendChild(slider({
+          min: '0',
+          max: '1',
+          step: '0.01',
+          label: internals.symbols[key],
+          className: `frequency`,
+          get: () => node.frequency[key].toString(),
+          set: (value) => (node.frequency[key] = parseFloat(value))
+        }));
+      });
+
+      this.adjusterContainer.appendChild(nodeElement);
+      ++i;
+    }
+
+    this.container.appendChild(this.adjusterContainer);
+  }
+
+  removed(){
+
+    this.container.removeChild(this.adjusterContainer);
+    delete this.adjusterContainer;
+    delete this.nodes;
+  }
+
+  update(){}
+};
diff --git a/lib/systems/global_adjuster.js b/lib/systems/global_adjuster.js
new file mode 100644 (file)
index 0000000..7f6df91
--- /dev/null
@@ -0,0 +1,57 @@
+import { System } from '@serpentity/serpentity';
+import Frequent from '../nodes/configurable';
+import { settingsContainer, slider } from '../factories/ui';
+
+export default class GlobalAdjuster extends System {
+
+  constructor(container) {
+
+    super();
+    this.container = container;
+  }
+
+  added(engine){
+
+    this.nodes = engine.getNodes(Frequent);
+    const container = document.getElementById('settings');
+    this.adjusterContainer = settingsContainer({
+      id: 'global-adjuster',
+      label: 'Global'
+    });
+
+    let i = 0;
+    for (const node of this.nodes) {
+      const nodeElement = settingsContainer({
+        id: `global-adjuster-${i}`,
+        label: 'Line',
+        level: 3
+      });
+
+      nodeElement.appendChild(slider({
+        min: '5',
+        max: '2000',
+        step: '1',
+        label: '𝜆',
+        className: 'lineLength',
+        get: () => node.configuration.lineLength.toString(),
+        set: (value) => (node.configuration.lineLength = parseFloat(value))
+      }));
+
+      this.adjusterContainer.appendChild(nodeElement);
+      ++i;
+    }
+
+    this.container.appendChild(this.adjusterContainer);
+  }
+
+  removed(){
+
+    this.container.removeChild(this.adjusterContainer);
+    delete this.adjusterContainer;
+    delete this.nodes;
+  }
+
+  update(){}
+};
+
+
index 0f5792dffd26dcae307cb48d0b9e6d4ecd8c3689..202ddc75917a7e89489a1ceef8bd3c623fdb3d22 100644 (file)
@@ -2,7 +2,7 @@ import { System } from '@serpentity/serpentity';
 import LissajousCurve from '../nodes/lissajous_curve';
 
 const internals = {
-  kAmplitude: 0.8,
+  kAmplitude: 1,
   kPeriod: Math.PI * 12000000
 };
 
@@ -21,8 +21,8 @@ export default class WebGLRenderer extends System {
 
   removed(){
 
-    this.curves = undefined;
-    this.time = undefined;
+    delete this.curves;
+    delete this.time;
   }
 
   update(dt){
@@ -30,11 +30,10 @@ export default class WebGLRenderer extends System {
     this.time = (this.time + dt / 100) % internals.kPeriod;
 
     for (const curve of this.curves) {
-      curve.position.x = this._getPosition(internals.kAmplitude, curve.frequency.a, this.time, 0);
-      curve.position.y = this._getPosition(internals.kAmplitude, curve.frequency.b, this.time, 0);
-      curve.position.z = this._getPosition(internals.kAmplitude, curve.frequency.c, this.time, 0);
+      curve.position.x = this._getPosition(curve.amplitude.a, curve.frequency.a, this.time, 0);
+      curve.position.y = this._getPosition(curve.amplitude.b, curve.frequency.b, this.time, 0);
+      curve.position.z = this._getPosition(curve.amplitude.c, curve.frequency.c, this.time, 0);
     }
-    console.log('UP');
   }
 
   _getPosition(amplitude, frequency, time, phaseShift) {
index 8535fde64e6d073ec72d04f1f3d4e2c6bb7047a8..3a92552f0dc5e5d91ef7afd6d4b86ee737f0ba20 100644 (file)
@@ -1,16 +1,34 @@
+import { mat4, vec3 } from 'gl-matrix';
 import { System } from '@serpentity/serpentity';
 import Drawable from '../nodes/drawable';
+import Configurable from '../nodes/configurable';
+import Cameras from '../nodes/cameras';
 import { initializeShaderProgram, initializeBuffers } from '../webgl_utils';
 
 const internals = {
-  kWidthRatio: 2.76,
+  kDefaultLineLength: 1000,
+  kCameraRadius: 5,
+  kCameraAngularVelocity: Math.PI / 180,
+  kFieldOfView: 45, // degrees
+  kNearLimit: 0.1,
+  kFarLimit: 100,
+  kWidthRatio: 1.5,
   kHeightRatio: 1,
-  kTargetVerticalResolution: 32 + Math.round(Math.random() * 1024),
+  kTargetVerticalResolution: 1024,
   kVertexShader: `
 
-    attribute vec3 aVertexPosition;
+    attribute vec4 aVertexPosition;
+    attribute vec4 aColor;
+
+    varying vec4 vColor;
+
+    uniform mat4 uViewMatrix;
+    uniform mat4 uProjectionMatrix;
+
     void main() {
-        gl_Position = vec4(aVertexPosition.xy, 0.0, 1.0);
+
+        gl_Position = uProjectionMatrix * uViewMatrix * aVertexPosition;
+        vColor = aColor;
         gl_PointSize = 10.0; // Set the point size
     }
   `,
@@ -18,25 +36,27 @@ const internals = {
   kFragmentShader: `
 
     precision mediump float;
-    uniform vec4 uColor;
+    varying vec4 vColor;
+
     void main() {
-        gl_FragColor = uColor;
+
+        gl_FragColor = vColor;
     }
   `
 };
 
 export default class WebGLRenderer extends System {
 
-  constructor(canvasId) {
+  constructor(canvas) {
 
     super();
-    this.canvasId = canvasId;
+    this.canvas = canvas;
   }
 
   added(engine){
 
     // Set up canvas
-    const canvas = document.getElementById(this.canvasId);
+    const { canvas } = this;
     window.addEventListener('resize', () => this._resizeCanvas(canvas));
 
     // Set up WebGL
@@ -45,6 +65,12 @@ export default class WebGLRenderer extends System {
     });
     this.gl = gl;
 
+    gl.clearColor(0.05882, 0.14902, 0.12157, 1);
+    gl.enable(gl.DEPTH_TEST);
+    gl.depthFunc(gl.LEQUAL);
+
+    this.colorBuffer = gl.createBuffer();
+
     const shaderProgram = initializeShaderProgram(
       gl,
       internals.kVertexShader,
@@ -54,10 +80,12 @@ export default class WebGLRenderer extends System {
     this.programInfo = {
       program: shaderProgram,
       attribLocations: {
-        vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition')
+        vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
+        vertexColor: gl.getAttribLocation(shaderProgram, 'aColor')
       },
       uniformLocations: {
-        color: gl.getUniformLocation(shaderProgram, 'uColor')
+        projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
+        viewMatrix: gl.getUniformLocation(shaderProgram, 'uViewMatrix')
       }
     };
 
@@ -65,11 +93,23 @@ export default class WebGLRenderer extends System {
     this._resizeCanvas(canvas);
 
     this.points = engine.getNodes(Drawable);
+    this.positions = [];
+    this.colors = [];
+
+    this.configurations = engine.getNodes(Configurable);
+    this.cameras = engine.getNodes(Cameras);
   }
 
   removed(engine){
 
-    this.points = undefined;
+    delete this.gl;
+    delete this.points;
+    delete this.colorBuffer;
+    delete this.buffers;
+    delete this.positions;
+    delete this.colors;
+    delete this.configurations;
+    delete this.cameras;
   }
 
   update(){
@@ -78,31 +118,100 @@ export default class WebGLRenderer extends System {
 
     gl.useProgram(programInfo.program);
 
-    {
-      const color = [1, 1, 1, 1];
-      gl.uniform4fv(programInfo.uniformLocations.color, color);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+    const fieldOfView = internals.kFieldOfView * Math.PI / 180;
+    const aspectRatio = internals.kWidthRatio / internals.kHeightRatio;
+    const projectionMatrix = mat4.create();
+
+    mat4.perspective(
+      projectionMatrix,
+      fieldOfView,
+      aspectRatio,
+      internals.kNearLimit,
+      internals.kFarLimit
+    );
+
+    gl.uniformMatrix4fv(
+      programInfo.uniformLocations.projectionMatrix,
+      false,
+      projectionMatrix
+    );
+
+    // We only support one camera for now.
+    const camera = this.cameras.nodes[0];
+    if (camera != undefined) {
+      const eye = vec3.fromValues(camera.position.x, camera.position.y, camera.position.z);
+      const center = vec3.fromValues(0, 0, 0);
+      const up = vec3.fromValues(camera.up.x, camera.up.y, camera.up.z);
+      const viewMatrix = mat4.create();
+      mat4.lookAt(viewMatrix, eye, center, up);
+      gl.uniformMatrix4fv(
+        programInfo.uniformLocations.viewMatrix,
+        false,
+        viewMatrix
+      );
     }
 
-    const positions = [];
+    let i = 0;
     for (const point of this.points) {
-      positions.push(point.position.x, point.position.y, point.position.z);
-      positions.push(
+      this.positions[i] = this.positions[i] || [];
+      this.positions[i].push(point.position.x, point.position.y, point.position.z, 1);
+      this.positions[i].push(
         point.position.prevX || point.position.x,
         point.position.prevY || point.position.y,
-        point.position.prevZ || point.position.z
+        point.position.prevZ || point.position.z,
+        1
       );
       point.position.prevX = point.position.x;
       point.position.prevY = point.position.y;
       point.position.prevZ = point.position.z;
+
+      this.colors[i] = this.colors[i] || [];
+      this.colors[i].push(
+        0.5 + point.position.z / 4 + point.position.x / 4,
+        0.5 + point.position.z / 4 + point.position.y / 4,
+        0.75 + point.position.z / 4, 1,
+        0.5 + point.position.prevZ / 4 + point.position.prevX / 4,
+        0.5 + point.position.prevZ / 4 + point.position.prevY / 4,
+        0.75 + point.position.prevZ / 4,1);
+
+      ++i;
+    }
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
+    const colors = this.colors.flat();
+    gl.bufferData(gl.ARRAY_BUFFER,
+      new Float32Array(colors),
+      gl.STATIC_DRAW);
+
+    {
+      const numberOfComponents = 4;
+      const type = gl.FLOAT;
+      const normalize = false;
+      const stride = 0;
+      const offset = 0;
+
+      gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
+      gl.vertexAttribPointer(
+        programInfo.attribLocations.vertexColor,
+        numberOfComponents,
+        type,
+        normalize,
+        stride,
+        offset
+      );
+      gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
     }
 
     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
+    const positions = this.positions.flat();
     gl.bufferData(gl.ARRAY_BUFFER,
       new Float32Array(positions),
       gl.STATIC_DRAW);
 
     {
-      const numberOfComponents = 3;
+      const numberOfComponents = 4;
       const type = gl.FLOAT;
       const normalize = false;
       const stride = 0;
@@ -122,20 +231,20 @@ export default class WebGLRenderer extends System {
 
     {
       gl.lineWidth(2);
-      gl.drawArrays(gl.LINES, 0, this.points.nodes.length * 2);
+      gl.drawArrays(gl.LINES, 0, positions.length / 4);
     }
+
+    this._cullLines();
   }
 
   _resizeCanvas(canvas) {
 
-    console.log('Resizing Canvas');
     let width = window.innerWidth;
     let height = Math.round(width * internals.kHeightRatio / internals.kWidthRatio);
 
     if (window.innerHeight < height) {
       height = window.innerHeight;
       width = Math.round(height * internals.kWidthRatio / internals.kHeightRatio);
-      console.log(width, height);
     }
 
     canvas.style.width = `${width}px`;
@@ -146,4 +255,16 @@ export default class WebGLRenderer extends System {
 
     this.gl.viewport(0, 0, canvas.width, canvas.height);
   }
+
+  _cullLines() {
+    const lineLength = this.configurations.nodes[0]?.configuration.lineLength || internals.kDefaultLineLength;
+
+    for (const [i, position] of Object.entries(this.positions)) {
+      this.positions[i] = position.slice(-lineLength * 8);
+    }
+
+    for (const [i, color] of Object.entries(this.colors)) {
+      this.colors[i] = color.slice(-lineLength * 8);
+    }
+  }
 };
index 1386bf71bbbf18ce18303378a2f5234b207e0f35..42bd8b6630d2266fca2328da4084eeb3a575e560 100644 (file)
@@ -25,14 +25,14 @@ export function initializeShaderProgram(gl, vertexShaderSource, fragmentShaderSo
   */
 export function initializeBuffers(gl) {
 
-    const positionBuffer = gl.createBuffer();
+  const positionBuffer = gl.createBuffer();
 
-    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
 
-    return {
-      position: positionBuffer
-    };
-  }
+  return {
+    position: positionBuffer
+  };
+}
 
 
 function loadShader(gl, type, source) {
index 11d57ae800e681f456782abb6bdd96b02e5d8094..13fdaa8316a544ddd52d148c6d687db7b0b20c06 100644 (file)
@@ -6,13 +6,18 @@
   "scripts": {
     "dev": "vite",
     "build": "vite build",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "lint": "eslint ."
   },
   "devDependencies": {
+    "@hapi/eslint-plugin": "^6.0.0",
+    "eslint": "^9.2.0",
     "vite": "^5.2.0"
   },
   "dependencies": {
+    "@serpentity/components.euler_angle": "^4.0.0",
     "@serpentity/components.position": "^4.0.1",
+    "@serpentity/components.velocity": "^4.0.3",
     "@serpentity/serpentity": "^4.0.0",
     "gl-matrix": "3.4.0"
   }
index cf92fbf6f0192e566f2d7c6bb13d7ec18e0a147e..5cbcf02151c396fff648e5e5f0c087dc4cb49da7 100644 (file)
@@ -5,9 +5,15 @@ settings:
   excludeLinksFromLockfile: false
 
 dependencies:
+  '@serpentity/components.euler_angle':
+    specifier: ^4.0.0
+    version: 4.0.0(@serpentity/serpentity@4.0.0)
   '@serpentity/components.position':
     specifier: ^4.0.1
     version: 4.0.1(@serpentity/serpentity@4.0.0)
+  '@serpentity/components.velocity':
+    specifier: ^4.0.3
+    version: 4.0.3(@serpentity/serpentity@4.0.0)
   '@serpentity/serpentity':
     specifier: ^4.0.0
     version: 4.0.0
@@ -16,6 +22,12 @@ dependencies:
     version: 3.4.0
 
 devDependencies:
+  '@hapi/eslint-plugin':
+    specifier: ^6.0.0
+    version: 6.0.0
+  eslint:
+    specifier: ^9.2.0
+    version: 9.2.0
   vite:
     specifier: ^5.2.0
     version: 5.2.0
@@ -229,6 +241,101 @@ packages:
     dev: true
     optional: true
 
+  /@eslint-community/eslint-utils@4.4.0(eslint@9.2.0):
+    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+    dependencies:
+      eslint: 9.2.0
+      eslint-visitor-keys: 3.4.3
+    dev: true
+
+  /@eslint-community/regexpp@4.10.0:
+    resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+    dev: true
+
+  /@eslint/eslintrc@3.0.2:
+    resolution: {integrity: sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.4
+      espree: 10.0.1
+      globals: 14.0.0
+      ignore: 5.3.1
+      import-fresh: 3.3.0
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@eslint/js@9.2.0:
+    resolution: {integrity: sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    dev: true
+
+  /@hapi/eslint-plugin@6.0.0:
+    resolution: {integrity: sha512-6eN1fnBO742nTTSpQtYB35aFGAT9I7r4u2v/+R2c9ToYTQp4QBoPOBA5R3+C7+Az3I0ugs+lTIqAsMPDLpsRAg==}
+    peerDependencies:
+      '@babel/core': ^7.14.3
+      '@babel/eslint-parser': ^7.14.3
+    peerDependenciesMeta:
+      '@babel/core':
+        optional: true
+      '@babel/eslint-parser':
+        optional: true
+    dev: true
+
+  /@humanwhocodes/config-array@0.13.0:
+    resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@humanwhocodes/object-schema': 2.0.3
+      debug: 4.3.4
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/module-importer@1.0.1:
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+    dev: true
+
+  /@humanwhocodes/object-schema@2.0.3:
+    resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
+    dev: true
+
+  /@humanwhocodes/retry@0.2.4:
+    resolution: {integrity: sha512-Ttl/jHpxfS3st5sxwICYfk4pOH0WrLI1SpW283GgQL7sCWU7EHIOhX4b4fkIxr3tkfzwg8+FNojtzsIEE7Ecgg==}
+    engines: {node: '>=18.18'}
+    dev: true
+
+  /@nodelib/fs.scandir@2.1.5:
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+    dev: true
+
+  /@nodelib/fs.stat@2.0.5:
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /@nodelib/fs.walk@1.2.8:
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.17.1
+    dev: true
+
   /@rollup/rollup-android-arm-eabi@4.17.2:
     resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
     cpu: [arm]
@@ -357,6 +464,14 @@ packages:
     dev: true
     optional: true
 
+  /@serpentity/components.euler_angle@4.0.0(@serpentity/serpentity@4.0.0):
+    resolution: {integrity: sha512-DoWghww9eEBanuHAPlVtBGeGyIS2wQu/M90iChdMTHUWR9uITAWbFT1aFQmRyY++7lxUbAdrusizeQ6XlQIw8Q==}
+    peerDependencies:
+      '@serpentity/serpentity': ^4.0.0
+    dependencies:
+      '@serpentity/serpentity': 4.0.0
+    dev: false
+
   /@serpentity/components.position@4.0.1(@serpentity/serpentity@4.0.0):
     resolution: {integrity: sha512-G2Q9huGgf7R61Lxx6ly+fYMfHBO5O+AvcDAJM0dENM3vmJl6lj8KZHNcIEOk+Vre+fTjWNX/aiNDphK9sDnAYA==}
     peerDependencies:
@@ -365,6 +480,14 @@ packages:
       '@serpentity/serpentity': 4.0.0
     dev: false
 
+  /@serpentity/components.velocity@4.0.3(@serpentity/serpentity@4.0.0):
+    resolution: {integrity: sha512-bPQf6Yz8gjraS2SNQquqsfopPnl1QtHjVz52om4CZ7gRqrFQzawsxs98I74/MtGjW3LAK4LSQyrPujy85j188g==}
+    peerDependencies:
+      '@serpentity/serpentity': ^4.0.0
+    dependencies:
+      '@serpentity/serpentity': 4.0.0
+    dev: false
+
   /@serpentity/serpentity@4.0.0:
     resolution: {integrity: sha512-RlsG5lMFvho/15LW9SJ5uFcb8WjAH7uZaToTJPRJEzAUntMnyQxZpPl7ensAHjbuBasBkgg7IDipl5GBuf6S0Q==}
     engines: {node: '>= 20.0.0'}
@@ -374,6 +497,109 @@ packages:
     resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
     dev: true
 
+  /acorn-jsx@5.3.2(acorn@8.11.3):
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      acorn: 8.11.3
+    dev: true
+
+  /acorn@8.11.3:
+    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+    dev: true
+
+  /ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+    dev: true
+
+  /balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+    dev: true
+
+  /callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+    dev: true
+
+  /color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+    dev: true
+
+  /cross-spawn@7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+    dev: true
+
+  /debug@4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: true
+
+  /deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+    dev: true
+
   /esbuild@0.20.2:
     resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
     engines: {node: '>=12'}
@@ -405,6 +631,150 @@ packages:
       '@esbuild/win32-x64': 0.20.2
     dev: true
 
+  /escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /eslint-scope@8.0.1:
+    resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+    dev: true
+
+  /eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /eslint-visitor-keys@4.0.0:
+    resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    dev: true
+
+  /eslint@9.2.0:
+    resolution: {integrity: sha512-0n/I88vZpCOzO+PQpt0lbsqmn9AsnsJAQseIqhZFI8ibQT0U1AkEKRxA3EVMos0BoHSXDQvCXY25TUjB5tr8Og==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0)
+      '@eslint-community/regexpp': 4.10.0
+      '@eslint/eslintrc': 3.0.2
+      '@eslint/js': 9.2.0
+      '@humanwhocodes/config-array': 0.13.0
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.2.4
+      '@nodelib/fs.walk': 1.2.8
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.4
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.0.1
+      eslint-visitor-keys: 4.0.0
+      espree: 10.0.1
+      esquery: 1.5.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 8.0.0
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      ignore: 5.3.1
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      is-path-inside: 3.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+      strip-ansi: 6.0.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /espree@10.0.1:
+    resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    dependencies:
+      acorn: 8.11.3
+      acorn-jsx: 5.3.2(acorn@8.11.3)
+      eslint-visitor-keys: 4.0.0
+    dev: true
+
+  /esquery@1.5.0:
+    resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
+    engines: {node: '>=0.10'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+    dev: true
+
+  /fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+    dev: true
+
+  /fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+    dev: true
+
+  /fastq@1.17.1:
+    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+    dependencies:
+      reusify: 1.0.4
+    dev: true
+
+  /file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
+    dependencies:
+      flat-cache: 4.0.1
+    dev: true
+
+  /find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+    dev: true
+
+  /flat-cache@4.0.1:
+    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+    engines: {node: '>=16'}
+    dependencies:
+      flatted: 3.3.1
+      keyv: 4.5.4
+    dev: true
+
+  /flatted@3.3.1:
+    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+    dev: true
+
   /fsevents@2.3.3:
     resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -418,12 +788,169 @@ packages:
     deprecated: Broke various systems. Will investigate and likely republish as a new major version
     dev: false
 
+  /glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /globals@14.0.0:
+    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ignore@5.3.1:
+    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /import-fresh@3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+    dev: true
+
+  /imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+    dev: true
+
+  /is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+
+  /is-path-inside@3.0.3:
+    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+    dependencies:
+      argparse: 2.0.1
+    dev: true
+
+  /json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+    dev: true
+
+  /json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+    dev: true
+
+  /json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+    dev: true
+
+  /keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+    dependencies:
+      json-buffer: 3.0.1
+    dev: true
+
+  /levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+    dev: true
+
+  /locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-locate: 5.0.0
+    dev: true
+
+  /lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+    dev: true
+
+  /minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
+  /ms@2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+    dev: true
+
   /nanoid@3.3.7:
     resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
     dev: true
 
+  /natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+    dev: true
+
+  /optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.5
+    dev: true
+
+  /p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      yocto-queue: 0.1.0
+    dev: true
+
+  /p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-limit: 3.1.0
+    dev: true
+
+  /parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+    dependencies:
+      callsites: 3.1.0
+    dev: true
+
+  /path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+    dev: true
+
   /picocolors@1.0.0:
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
     dev: true
@@ -437,6 +964,30 @@ packages:
       source-map-js: 1.2.0
     dev: true
 
+  /prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+    dev: true
+
+  /resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+    dev: true
+
   /rollup@4.17.2:
     resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -463,11 +1014,65 @@ packages:
       fsevents: 2.3.3
     dev: true
 
+  /run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+    dependencies:
+      queue-microtask: 1.2.3
+    dev: true
+
+  /shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+    dependencies:
+      shebang-regex: 3.0.0
+    dev: true
+
+  /shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+    dev: true
+
   /source-map-js@1.2.0:
     resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: true
+
+  /strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /text-table@0.2.0:
+    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+    dev: true
+
+  /type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+    dev: true
+
+  /uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+    dependencies:
+      punycode: 2.3.1
+    dev: true
+
   /vite@5.2.0:
     resolution: {integrity: sha512-xMSLJNEjNk/3DJRgWlPADDwaU9AgYRodDH2t6oENhJnIlmU9Hx1Q6VpjyXua/JdMw1WJRbnAgHJ9xgET9gnIAg==}
     engines: {node: ^18.0.0 || >=20.0.0}
@@ -502,3 +1107,21 @@ packages:
     optionalDependencies:
       fsevents: 2.3.3
     dev: true
+
+  /which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+    dev: true
index c389fd83422290b11da00954d19a70c77e53636a..d961e981fec36dfea4426a09f9c1d2c3b56a9782 100644 (file)
--- a/style.css
+++ b/style.css
@@ -3,6 +3,152 @@
   margin: 0;
 }
 
-canvas, body {
+body {
+  color: #80FFCC;
   background-color: #0F261F;
+  font-family: 'IBM 3270 Narrow', 'IBM 3270', 'Andale Mono', Courier, monospace;
+  font-size: 12px;
+  overflow: hidden;
+}
+
+#settings {
+  position: absolute;
+  top: 5px;
+  right: 5px;
+}
+
+h1, h2, h3 {
+  font-weight: normal;
+}
+
+h1 {
+  font-size: 18px;
+}
+
+h2 {
+  font-size: 16px;
+}
+
+h3 {
+  font-size: 14px;
+}
+
+input[type="range"] {
+  -webkit-appearance: none;
+  background-color: transparent;
+  height: 16px;
+  display: inline-block;
+  margin: 0 4px;
+  width: 128px;
+}
+
+input[type="range"]:focus {
+ outline: none;
+}
+
+input[type="range"]::-webkit-slider-runnable-track {
+ background: #fff;
+ height: 2px;
+}
+
+input[type="range"]::-moz-range-track {
+ background: #fff;
+ height: 2px;
+}
+
+input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ height: 16px;
+ width: 16px;
+ background-color: #80FFCC;
+ margin-top: -8px;
+ border-radius: 50%;
+}
+
+input[type="range"]::-moz-range-thumb {
+ height: 16px;
+ width: 16px;
+ background-color: #80FFCC;
+ margin-top: -8px;
+ border-radius: 50%;
+}
+
+
+.slider::before,
+.slider::after {
+  content: '';
+  background-color: #80FFCC;
+  height: 16px;
+  width: 16px;
+  display: inline-block;
+}
+
+.slider {
+  margin: 4px 0;
+  position: relative;
+}
+
+label {
+  display: inline-block;
+  position: relative;
+  width: 0;
+  height: 16px;
+  right: 32px;
+  top: -5px;
+}
+
+.slider.lineLength::before {
+  mask-image: url('/images/short.svg');
+}
+
+.slider.lineLength::after {
+  mask-image: url('/images/long.svg');
+}
+
+.slider.radius::before {
+  mask-image: url('/images/near.svg');
+}
+
+.slider.radius::after {
+  mask-image: url('/images/far.svg');
+}
+
+.slider.rotation-x::before {
+  mask-image: url('/images/no-pitch.svg');
+}
+
+.slider.rotation-x::after {
+  mask-image: url('/images/fast-pitch.svg');
+}
+
+.slider.rotation-y::before {
+  mask-image: url('/images/no-yaw.svg');
+}
+
+.slider.rotation-y::after {
+  mask-image: url('/images/fast-yaw.svg');
+}
+
+.slider.rotation-z::before {
+  mask-image: url('/images/no-roll.svg');
+}
+
+.slider.rotation-z::after {
+  mask-image: url('/images/fast-roll.svg');
+}
+
+.slider.frequency::before {
+  mask-image: url('/images/lf.svg');
+}
+
+.slider.frequency::after {
+  mask-image: url('/images/hf.svg');
+}
+
+.slider.amplitude::before {
+  mask-image: url('/images/la.svg');
+}
+
+.slider.amplitude::after {
+  mask-image: url('/images/ha.svg');
 }