NoSloppy

fixStyleEditorSequence

Dec 30th, 2023 (edited)
24
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 336.92 KB | None | 0 0
  1. <!-- happy -->
  2. <!DOCTYPE html>
  3.  
  4. <html lang="en">
  5. <head>
  6. <meta charset="UTF-8">
  7. <!-- This should show up in search engine results. -->
  8. <meta name="description" content="ProffieOS Style Editor: Customize your Proffieboard saber blade animations with this browser based tool.">
  9. <link rel="shortcut icon" type="image/png" href="SE_Favicon.png">
  10. <title>ProffieOS Style Editor</title>
  11.  
  12. <style>
  13. /*
  14. * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
  15. * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
  16. * Copyright 2023 Fonticons, Inc.
  17. */
  18.  
  19. .fas,
  20. .fa-solid {
  21. -moz-osx-font-smoothing: grayscale;
  22. -webkit-font-smoothing: antialiased;
  23. display: var(--fa-display, inline-block);
  24. font-style: normal;
  25. font-variant: normal;
  26. line-height: 1;
  27. text-rendering: auto; }
  28.  
  29. .fas,
  30. .fa-solid {
  31. font-family: 'Font Awesome 6 Free'; }
  32.  
  33. .fa-right-from-bracket::before {
  34. content: "\f2f5"; }
  35.  
  36. .fa-copy::before {
  37. content: "\f0c5"; }
  38.  
  39. .fa-up-right-and-down-left-from-center::before {
  40. content: "\f424"; }
  41.  
  42. .fa-layer-group::before {
  43. content: "\f5fd"; }
  44.  
  45. .fa-list::before {
  46. content: "\f03a"; }
  47.  
  48. .fa-arrows-rotate::before {
  49. content: "\f021"; }
  50.  
  51. .fa-save::before {
  52. content: "\f0c7"; }
  53.  
  54. .fa-cog::before {
  55. content: "\f013"; }
  56.  
  57. .fa-solid {
  58. font-weight: 900; }
  59.  
  60. @font-face {
  61. font-family: 'Font Awesome 6 Free';
  62. font-style: normal;
  63. font-weight: 900;
  64. font-display: block;
  65. src: url("fontawesome-free-6.4.0-web/webfonts/fa-solid-900.woff2") format("woff2"), url("fontawesome-free-6.4.0-web/webfonts/fa-solid-900.ttf") format("truetype"); }
  66.  
  67. /* Make page elements like buttons and whitespace non-selectable with drag box */
  68. * {
  69. -webkit-user-select: none;
  70. -moz-user-select: none;
  71. -ms-user-select: none;
  72. user-select: none;
  73. }
  74.  
  75. /* Box sizing helps keep buttons uniform in
  76. size when padding and margins may change */
  77. * {
  78. font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
  79. box-sizing: border-box;
  80. }
  81.  
  82. /* Dark mode styles */
  83.  
  84. /* Whole page */
  85. .dark-mode {
  86. background-color: #202020;
  87. color: white !important;
  88. color-scheme: dark !important;
  89. }
  90.  
  91. /* Do Selected Effect, Expand, and Layerize */
  92. .dark-mode .page-left .button-off,
  93. .dark-mode .page-left .button-off:hover {
  94. background-color: #777;
  95. color: darkgray;
  96. box-shadow: 0 0 0 1px transparent;
  97. }
  98. .dark-mode .settings-panel button:hover{
  99. background-color: #f44336;
  100. }
  101.  
  102. /* Other buttons */
  103. .dark-mode button,
  104. .dark-mode input[type="button"] {
  105. background-color: #777;
  106. color: #fff;
  107. border: 1px solid transparent;
  108. }
  109. .dark-mode button:hover,
  110. .dark-mode input[type="button"]:hover {
  111. background-color: #9F9F9F;
  112. }
  113. .dark-mode button:active,
  114. .dark-mode .settings-panel button:active,
  115. .dark-mode input[type="button"]:active {
  116. background-color: steelblue;
  117. }
  118. .dark-mode button.submit-button {
  119. background-color: forestgreen;
  120. }
  121.  
  122. /* Power and Rotate latching buttons */
  123. .dark-mode .page-left .button-latched,
  124. .dark-mode .page-left .button-latched:hover {
  125. background-color: steelblue;
  126. }
  127.  
  128. /* Lockup and More Effects dropdown menus */
  129. .dark-mode select {
  130. background-color: #777;
  131. color: #fff;
  132. border: 1px solid transparent;
  133. padding: 1px;
  134. }
  135. .dark-mode select:hover {
  136. background-color: #9F9F9F;
  137. }
  138.  
  139. /* Variant and Alt adjusters */
  140. .dark-mode .variant-alt-container {
  141. background-color: steelblue;
  142. color: white;
  143. }
  144.  
  145. .dark-mode .textbox {
  146. background-color: #333;
  147. color: white;
  148. }
  149. .dark-mode .settings-panel {
  150. border: 3px solid #ccc;
  151. background: #777;
  152. color: black;
  153. }
  154. .dark-mode .settings-panel .settings-panel-arrow {
  155. border-color: transparent #777 transparent transparent;
  156. }
  157.  
  158. /* Tabs */
  159. .dark-mode .tab {
  160. background-color: #333;
  161. }
  162. .dark-mode .tab button:hover {
  163. background-color: #9F9F9F;
  164. box-shadow: 0 0 3px 1px transparent;
  165. }
  166. .dark-mode .tab button.active {
  167. background-color: steelblue;
  168. }
  169. .dark-mode .tab button.disabled {
  170. background-color: #777;
  171. color: #bbb;
  172. cursor: not-allowed;
  173. }
  174.  
  175. /* Structured View style arguments borders */
  176. .dark-mode .structured-view .selected-area-container {
  177. background-color: steelblue;
  178. }
  179. .dark-mode .pp-container {
  180. border-color: white;
  181. }
  182.  
  183. .dark-mode .pp-container .running {
  184. border-color: chartreuse;
  185. }
  186. /* Keep color palette background white for reference. */
  187. .dark-mode .rgb-tabcontent {
  188. background-color: white;
  189. }
  190.  
  191. /* Popup Window */
  192. .dark-mode .popup-window {
  193. border: 3px solid #ccc;
  194. background: #777;
  195. }
  196.  
  197. .dark-mode .footer-container {
  198. background-color: #333;
  199. }
  200.  
  201. /* Non-dark mode styles */
  202.  
  203. body {
  204. margin-top: -3px;
  205. margin-bottom: 0;
  206. }
  207.  
  208. .page-left {
  209. position: relative;
  210. vertical-align: top;
  211. width: 50%;
  212. padding-bottom: 30px;
  213. }
  214. .page-right {
  215. vertical-align: top;
  216. }
  217. .canvas-container {
  218. position: relative;
  219. }
  220. /* This keeps the More Effects menu and
  221. the Do Selected Effect button together */
  222. .more-menu-container {
  223. white-space: nowrap;
  224. }
  225.  
  226. /* Do Selected Effect, Expand, and Layerize */
  227. .page-left .button-off,
  228. .page-left .button-off:hover {
  229. background-color: lightgray;
  230. color: darkgray;
  231. box-shadow: 0 0 2px 1px transparent;
  232. cursor: not-allowed;
  233. }
  234.  
  235. /* Lockup and More Effects Drop Downs */
  236. select {
  237. background-color: #eee;
  238. color: black;
  239. border-radius: 5px;
  240. padding: .2%;
  241. font-size: .8em;
  242. cursor: pointer;
  243. }
  244. select:hover {
  245. /* background-color: #ddd;*/
  246. box-shadow: 0 0 2px 1px dodgerblue;
  247. }
  248. /* Settings Panel buttons
  249. need to be here to not override Other Buttons below */
  250. .settings-panel button {
  251. background-color: #f44336;
  252. color: black;
  253. margin-left: 46px;
  254. border: 1px solid black;
  255. }
  256. .settings-panel button.close-settings {
  257. position: absolute;
  258. top: 0px;
  259. left: -40px;
  260. padding: 0px 4px;
  261. font-weight: bold;
  262. }
  263. /* Other buttons */
  264. button,
  265. input[type="button"] {
  266. display: inline-block;
  267. background-color: #eee;
  268. color: black;
  269. border-radius: 2px;
  270. padding: 3px;
  271. margin-block: 5px;
  272. font-size: 14px;
  273. border: 1px solid #bbb;
  274. font-weight: normal;
  275. transition: box-shadow 0.1s ease;
  276. }
  277. button:hover,
  278. input[type="button"]:hover {
  279. box-shadow: 0 0 2px 1px dodgerblue;
  280. }
  281. button:active,
  282. input[type="button"]:active {
  283. background-color: dodgerblue;
  284. }
  285. button.submit-button {
  286. background-color: limegreen;
  287. border: 1px solid green;
  288. }
  289. button.submit-button:active {
  290. background-color: dodgerblue;
  291. }
  292. /*Keep from browser takeover of number inputs */
  293. input[type='number'] {
  294. width: 60px;
  295. height: 22px;
  296. }
  297. input[type='number']::-webkit-inner-spin-button,
  298. input[type='number']::-webkit-outer-spin-button {
  299. -webkit-appearance: none;
  300. margin: 0;
  301. }
  302. input[type='number'] {
  303. -moz-appearance: textfield;
  304. }
  305.  
  306. /* Power and Rotate latching buttons */
  307. .page-left .button-latched,
  308. .page-left .button-latched:hover {
  309. background-color: #A1D1F3;
  310. }
  311.  
  312. /* Variant and Alt adjusters */
  313. .variant-alt-container {
  314. background: #A1D1F3;
  315. border: 1px solid black;
  316. border-radius: 4px;
  317. display: inline-block;
  318. padding-inline: 4px;
  319. }
  320. .variant-label {
  321. margin-left: 3px;
  322. }
  323. .variant-alt-controls {
  324. display: inline-flex;
  325. align-items: center;
  326. }
  327. .variant-slider {
  328. -webkit-appearance: none;
  329. width: 65px;
  330. height: 10px;
  331. margin: 3px 3px;
  332. margin-top: 5px;
  333. cursor: pointer;
  334. }
  335. /* Thumb handle styling for Chrome, Safari, and other WebKit-based browsers */
  336. .variant-slider::-webkit-slider-thumb {
  337. -webkit-appearance: none;
  338. background: dodgerblue;
  339. width: 14px;
  340. height: 14px;
  341. border-radius: 20%;
  342. border: 1px solid #333;
  343. margin-top: -2px;
  344. }
  345. /* Thumb handle styling for Firefox */
  346. .variant-slider::-moz-range-thumb {
  347. background: dodgerblue;
  348. width: 14px;
  349. height: 14px;
  350. border-radius: 20%;
  351. border: 1px solid #333;
  352. }
  353. /* Thumb handle styling for Internet Explorer and Edge */
  354. .variant-slider::-ms-thumb {
  355. background: dodgerblue;
  356. width: 14px;
  357. height: 14px;
  358. border-radius: 20%;
  359. border: 1px solid #333;
  360. }
  361. .alt-label {
  362. margin-left: 8px;
  363. margin-right: 3px;
  364. }
  365.  
  366. .error-message {
  367. color: red;
  368. font-weight: bold;
  369. }
  370. /* Blade Style text box */
  371. .textbox {
  372. background-color: #eee;
  373. font-size: 14px;
  374. width: 100%;
  375. }
  376. /* History Tab content */
  377. div.MAGIC_CLASS_FUNCTION input[type=button].btn {
  378. background: green;
  379. color: black;
  380. padding: 2px 3px;
  381. }
  382. div.MAGIC_CLASS_COLOR input[type=button].btn {
  383. background: green;
  384. color: black;
  385. padding: 2px 3px;
  386. }
  387. div.MAGIC_CLASS_TRANSITION input[type=button].btn {
  388. background: green;
  389. color: black;
  390. padding: 2px 3px;
  391. }
  392. div.MAGIC_CLASS_INT input[type=button].btn {
  393. background: green;
  394. color: black;
  395. padding: 2px 3px;
  396. }
  397. div.MAGIC_CLASS_EFFECT input[type=button].btn {
  398. background: green;
  399. color: black;
  400. padding: 2px 3px;
  401. }
  402. div.MAGIC_INVISIBLE_CLASS_EFFECT {
  403. display: inline;
  404. }
  405. div.MAGIC_INVISIBLE_CLASS_TRANSITION {
  406. display: inline;
  407. }
  408. div.MAGIC_INVISIBLE_CLASS_FUNCTION {
  409. display: inline;
  410. }
  411. span.MAGIC_CLASS_FUNCTION input[type=button].btn {
  412. background: green;
  413. color: black;
  414. padding: 2px 3px;
  415. }
  416. span.MAGIC_CLASS_COLOR input[type=button].btn {
  417. background: green;
  418. color: black;
  419. padding: 2px 3px;
  420. }
  421. span.MAGIC_CLASS_TRANSITION input[type=button].btn {
  422. background: green;
  423. color: black;
  424. padding: 2px 3px;
  425. }
  426. span.MAGIC_CLASS_INT input[type=button].btn {
  427. background: green;
  428. color: black;
  429. padding: 2px 3px;
  430. }
  431. span.MAGIC_CLASS_EFFECT input[type=button].btn {
  432. background: green;
  433. color: black;
  434. padding: 2px 3px;
  435. }
  436.  
  437. .settings-panel.show {
  438. opacity: 1;
  439. pointer-events: auto;
  440. }
  441. .settings-panel {
  442. position: fixed;
  443. left: 50%;
  444. top: 50%;
  445. transform: translate(-50%, -50%);
  446. color: black;
  447. width: 287px;
  448. border: 3px solid #888;
  449. background: #ddd;
  450. padding: 36px;
  451. padding-left: 30px;
  452. border-radius: 9px;
  453. box-shadow: 6px 10px 10px rgba(0, 0, 0, 0.9);
  454. opacity: 0;
  455. pointer-events: none;
  456. transition: opacity 0.25s ease-in-out;
  457. z-index: 1000;
  458. }
  459. .settings-panel label {
  460. display: flex;
  461. align-items: center;
  462. }
  463. .settings-panel label input[type="checkbox"] {
  464. margin-right: 8px;
  465. }
  466. .settings-section {
  467. margin-bottom: 10px;
  468. }
  469. .settings-panel .settings-header-label {
  470. margin-inline-start: 2.8em;
  471. margin-block-start: -1.5em;
  472. color: #333;
  473. font-size: 22px;
  474. }
  475. .settings-section-label {
  476. font-weight: bold;
  477. color: #333;
  478. margin-bottom: 5px;
  479. border-bottom: 2px solid #888;
  480. padding-bottom: 5px;
  481. }
  482.  
  483. /* Align Wavlen Settings label */
  484. .wavlen-label {
  485. display: block;
  486. margin-top: 5px;
  487. }
  488. .wavlen-value {
  489. width: 80px;
  490. margin-left: 5px;
  491. }
  492.  
  493. /*.tabs-container {
  494. position: relative;
  495. }*/
  496. /* Style the tab */
  497. .tab {
  498. overflow: hidden;
  499. border: 1px solid #ccc;
  500. background-color: #f1f1f1;
  501. }
  502. /* Style the buttons that are used to open the tab content */
  503. .tab button {
  504. background-color: inherit;
  505. float: left;
  506. border: none;
  507. margin: unset;
  508. outline: none;
  509. cursor: pointer;
  510. padding: 8px;
  511. transition: 0.1s ease;
  512. }
  513. /* Change background color of buttons on hover */
  514. .tab button:hover {
  515. background-color: #ddd;
  516. box-shadow: 0 0 3px 1px transparent;
  517. }
  518. /* Create an active/current tablink class */
  519. .tab button.active {
  520. background-color: #A1D1F3;
  521. }
  522. .tab button.disabled {
  523. background-color: lightgray;
  524. color: darkgray;
  525. box-shadow: 0 0 0 1px transparent;
  526. cursor: not-allowed;
  527. }
  528.  
  529. /* Style the tab content */
  530. .tabcontent {
  531. display: none;
  532. padding: 6px 12px;
  533. border: 1px solid #ccc;
  534. border-top: none;
  535. }
  536. .tabcontent .btn {
  537. border-radius: 5px;
  538. }
  539. .rgb-tabcontent {
  540. color: black;
  541. }
  542. .custom-color {
  543. background: #ccc;
  544. text-align: center;
  545. position: relative;
  546. }
  547. .rgb-tabcontent .btn {
  548. border-radius: 16px;
  549. width: 125px;
  550. }
  551. .rgb-tabcontent .custom-color .color-picker {
  552. width: 100px;
  553. }
  554.  
  555. .structured-view {
  556. position: sticky;
  557. top: 0;
  558. width: 100%;
  559. height: 100vh;
  560. overflow-y: scroll;
  561. }
  562. .structured-view-label {
  563. display: block;
  564. font-size: 110%;
  565. padding-top: 9px;
  566. padding-bottom: 5px;
  567. }
  568. .structured-view .selected-area-container {
  569. background-color: lightblue;
  570. }
  571. /* Fat font in Structured View */
  572. .pp span {
  573. padding-left: 6px;
  574. font-weight: 600;
  575. }
  576. /* Structured View style arguments borders */
  577. .pp-container {
  578. border-color: gray;
  579. border-style: solid;
  580. border-width: 1px;
  581. padding: 2px;
  582. }
  583. .pp-content {
  584. margin-left: 1em;
  585. }
  586. /* Chartreuse highlighted borders */
  587. .running {
  588. border-color: chartreuse;
  589. border-width: 3px;
  590. padding: 0px;
  591. }
  592.  
  593. /* Structured View Layer buttons
  594. Add, Delete, Move Up, Move Down */
  595. .extra-buttons {
  596. margin: 3px;
  597. line-height: 1.0em;
  598. font-size: 16px;
  599. }
  600.  
  601. /* Footer */
  602. .footer-title {
  603. min-width: 180px;
  604. font-size: 16px;
  605. }
  606. .footer-container {
  607. position: absolute;
  608. bottom: 0;
  609. left: 0;
  610. width: 100%;
  611. display: flex;
  612. justify-content: space-between;
  613. align-items: center;
  614. background-color: #eee;
  615. padding: 2px 15px;
  616. }
  617. .other-sites {
  618. float: right;
  619. padding-right: 10px;
  620. margin-left: 35px;
  621. }
  622. .footer-links {
  623. display: flex;
  624. align-items: center;
  625. white-space: nowrap;
  626. overflow: hidden;
  627. text-overflow: ellipsis;
  628. min-width: 100px;
  629. }
  630. .footer-links a img {
  631. height: 16px;
  632. width: 16px;
  633. margin-right: 5px;
  634. }
  635. .footer-links a:hover {
  636. color: #000;
  637. }
  638. /* Popup Window */
  639. .popup-window.show {
  640. opacity: 1;
  641. pointer-events: auto;
  642. }
  643. .popup-window {
  644. font-weight: 500;
  645. position: fixed;
  646. top: 50%;
  647. left: 50%;
  648. transform: translate(-50%, -50%);
  649. width: 30%;
  650. color: black;
  651. border: 3px solid #888;
  652. background: #ddd;
  653. padding: 20px;
  654. text-align: center;
  655. border-radius: 9px;
  656. box-shadow: 6px 10px 10px rgba(0, 0, 0, 0.9);
  657. opacity: 0;
  658. pointer-events: none;
  659. transition: opacity 0.25s ease-in-out;
  660. z-index: 9999;
  661. }
  662.  
  663. .popup-window button {
  664. background-color: steelblue;
  665. color: black;
  666. border-radius: 5px;
  667. padding: 3px 35px;
  668. font-size: 20px;
  669. font-weight: bold;
  670. border: 1px solid black;
  671. cursor: pointer;
  672. }
  673. .overlay {
  674. display: none;
  675. position: fixed;
  676. top: 0;
  677. left: 0;
  678. width: 100%;
  679. height: 100%;
  680. background: rgba(0, 0, 0, 0.5);
  681. z-index: 9998;
  682. }
  683. .overlay.show {
  684. display: block;
  685. }
  686.  
  687. </style>
  688.  
  689.  
  690. <script id="vertex_shader" type="x-shader/x-vertex">
  691. precision highp float;
  692. attribute vec2 a_position;
  693. varying vec2 v_position;
  694. uniform float u_width;
  695. uniform float u_height;
  696.  
  697. void main() {
  698. v_position = a_position * vec2(u_width, u_height);
  699. gl_Position = vec4(a_position, 0, 1);
  700. }
  701. </script>
  702.  
  703. <script id="hilt_graflex" type="x-shader/x-fragment">
  704.  
  705. #define ITERATIONS 400
  706.  
  707. float Clamp(vec3 p) {
  708. p= vec3(p.x + 0.03, -p.z, p.y);
  709. float dist = 100000.0;
  710. // clamp
  711. dist = min(dist, sdCappedCylinder(p.yxz, 0.040, 0.051));
  712. dist = min(dist, sdBox(p.xyz + vec3(0.0, -0.038, 0.0), vec3(0.051, 0.020, 0.015)));
  713. return dist;
  714. }
  715.  
  716. float RedPill(vec3 p) {
  717. p -= vec3(0.143,0,0);
  718. if (p.y > 0.0) {
  719. p.x-=0.030;
  720. p.y = abs(p.y);
  721. }
  722. float dist = 1000000.0;
  723. // dist = min(dist, sdCappedCylinder(p, 0.011, 0.051));
  724. dist = min(dist,
  725. max(sdSphere(p, 0.053),
  726. sdCappedCylinder(p, 0.011, 0.08)));
  727. return dist;
  728. }
  729.  
  730. float Button(vec3 p) {
  731. float dist = 1000000.0;
  732. dist = min(dist, RedPill(p));
  733. p -= vec3(0.143,0,0);
  734. if (p.y > 0.0) {
  735. p.x-=0.030;
  736. p.y = abs(p.y);
  737. }
  738. dist = min(dist, sdCappedCylinder(p, 0.018, 0.050));
  739. // dist = min(dist, sdCappedCylinder(p, 0.011, 0.051));
  740. return dist;
  741. }
  742.  
  743. // May need to find a better way to model this.
  744. float Cut(vec3 p) {
  745. p.x -= 0.350;
  746. // p.y -= 0.008;
  747. p *= rotz(-0.60);
  748. p.x += sin(p.y*50.0)/55.0;
  749. return sdBox(p, vec3(0.1, 0.2, 0.1)) * 0.8;
  750. }
  751.  
  752. float Slots(vec3 p) {
  753. float dist = sdBox(p + vec3(-0.259,0.0,0.0), vec3(0.004, 0.1, 0.005));
  754. dist = min(dist, sdBox(p + vec3(-0.227,0.0,0.0), vec3(0.020, 0.1, 0.010)));
  755. return dist;
  756. }
  757.  
  758. float Slots2(vec3 p) {
  759. p.z = -abs(p.z);
  760. // float dist = 100000.0;
  761. float dist = sdBox(p + vec3(-0.179,0.055,0.01), vec3(0.010, 0.03, 0.005));
  762. p *= rotx(0.95);
  763. dist = min(dist, sdCappedCylinder(p+vec3(-0.175,0.040,0), 0.014, 0.02));
  764. // dist = min(dist, sdBox(p + vec3(-0.227,0.0,0.0), vec3(0.020, 0.1, 0.010)));
  765. return dist;
  766. }
  767.  
  768. float Pins(vec3 p) {
  769. p.z = -abs(p.z);
  770. p *= rotx(0.95);
  771. p += vec3(-0.175,0.015,0);
  772. p.x = abs(p.x);
  773. p.x -= 0.005;
  774. return sdVerticalCapsule(p.yxz, 0.018, 0.002);
  775. // return sdCappedCylinder(p, 0.002, 0.02);
  776. }
  777.  
  778.  
  779. float Cylinder(vec3 p) {
  780. float dist = sdCappedCylinder(p.yxz, 0.038, 0.270);
  781. dist = max(dist, -sdCappedCylinder(p.yxz + vec3(0,-0.470,0), 0.036, 0.270));
  782. dist = max(dist, -Cut(p));
  783. dist = max(dist, -Slots(p));
  784. dist = max(dist, -Slots2(p));
  785. return dist;
  786. }
  787.  
  788. float sdCircle(vec2 xy, float r) {
  789. return length(xy) - r;
  790. }
  791.  
  792. float sdRect(vec2 p, vec2 b) {
  793. vec2 q = abs(p) - b;
  794. return length(max(q,0.0)) + min(max(q.x,q.y),0.0);
  795. }
  796.  
  797. float earsFront(vec2 xy) {
  798. xy.x = -abs(xy.x);
  799. float dist = sdCircle(xy, 0.027);
  800. dist = max(dist, -sdCircle(xy, 0.0265));
  801. dist = max(dist, -sdRect(xy + vec2(0.0, -0.030), vec2(0.005, 0.010)));
  802. dist = min(dist, sdRect(xy + vec2(0.005, -0.056), vec2(0.0005, 0.030)));
  803. return dist;
  804. }
  805.  
  806. float earsSide(vec2 xy) {
  807. xy.x = -abs(xy.x);
  808. float dist = sdRect(xy + vec2(0, -0.050), vec2(0.012, 0.030));
  809. dist = min(dist, sdRect(xy, vec2(0.016, 0.030)));
  810. dist = min(dist, max(
  811. sdCircle(xy + vec2(0,-0.069), 0.016),
  812. -sdRect(xy + vec2(0.011*2.0, -0.060), vec2(0.011, 0.030))));
  813. return dist;
  814. }
  815.  
  816.  
  817. float bunnyEars(vec3 p) {
  818. p.x -= 0.227;
  819. return max(
  820. opExtrusion( p.zyx, earsFront(p.zy), 0.1 ),
  821. opExtrusion( p, earsSide(p.xy), 0.1 ));
  822. }
  823.  
  824. float ROUND(float x) {
  825. return floor(x + 0.5);
  826. }
  827.  
  828. float grips(vec3 p) {
  829. // 6-symmetry
  830. float angle = atan(p.z, p.y);
  831. float section = ROUND(angle * 3.0 / PI);
  832. float angle2 = section * PI / 3.0;
  833. vec3 p2 = p * rotx(angle2);
  834.  
  835. // T-track
  836. float dist = sdBox(p2.xyz + vec3(0.270-0.092,-0.038,0.0), vec3(0.092, 0.004, 0.013));
  837. dist = min(dist, sdBox(p2.xyz + vec3(0.270-0.092,-0.038,0.0), vec3(0.091, 0.012, 0.002)) - 0.001);
  838. return dist;
  839. }
  840.  
  841. #define SCALE 0.25
  842.  
  843. // FIXME
  844. vec3 dorot(vec3 p) {
  845. p.y -= 3.75;
  846. p *= SCALE;
  847. p = p.yxz;
  848. p.x = -p.x;
  849. p *= rotx(-PI/2.0);
  850.  
  851. // p = p * roty(-0.4) * rotx(PI/5.0);
  852. // p = p * roty(-0.4) * rotx(PI/7.0);
  853. // p = p * roty(-0.05);
  854. // p *= rotz(iTime*0.1) * rotx(-iTime*0.0123) * roty(iTime*0.09781623);
  855. return p;
  856. }
  857.  
  858. // bounding box
  859. float bb(vec3 p) {
  860. p.y-=0.01;
  861. return sdVerticalCapsule(p, 0.25, 0.08);
  862. }
  863.  
  864. // Return actual distance, step
  865. float handle2(vec3 p) {
  866. p = dorot(p);
  867.  
  868. float dist = bb(p);
  869. if (dist > 0.1) return dist;
  870.  
  871. dist = 100000.0;
  872. dist = min(dist, Cylinder(p));
  873. dist = min(dist, Clamp(p));
  874. dist = min(dist, Button(p));
  875. dist = min(dist, grips(p));
  876. dist = min(dist, bunnyEars(p));
  877. dist = min(dist, Pins(p));
  878.  
  879. return dist;
  880. }
  881.  
  882. float handle(vec3 p) {
  883. return handle2(p) / SCALE;
  884. }
  885.  
  886. Material getHiltMaterial(vec3 hp, vec3 ray_dir) {
  887. float dist = handle2(hp);
  888. vec3 hpr = dorot(hp);
  889.  
  890. if (grips(hpr) == dist) {
  891. return Material(vec3(0.01,0.01,0.01), 0.0, 0.4, vec3(0));
  892. }
  893.  
  894. if (Pins(hpr) == dist) {
  895. return Material(vec3(1.00, 0.71, 0.29), 1.0, 0.2, vec3(0));
  896. }
  897.  
  898. if (length(hpr.zy) < 0.036 && abs(hpr.x) < 0.201) {
  899. return Material(vec3(0.01,0.01,0.01), 0.2, 0.2, vec3(0));
  900. }
  901.  
  902. if (RedPill(hpr) == dist) {
  903. return Material(vec3(0.6,0.1,0.1), 0.5, 0.2, vec3(0));
  904. }
  905.  
  906. if (hpr.z < -0.055 && abs(hpr.y) < 0.011) {
  907. if (mod(hpr.x+0.2, 0.006) < 0.002) {
  908. return Material(vec3(0.3, 0.9, 0.3), 0.0, 0.4, vec3(0));
  909. } else {
  910. return Material(vec3(1.00, 0.71, 0.29), 1.0, 0.2, vec3(0));
  911. }
  912. }
  913.  
  914. return Material(vec3(1.0), 0.9, 0.3, vec3(0));
  915. // vec2 tpos = vec2(dot(hpr, vec3(13.1,1,0.5)), dot(hpr, vec3(7.033,0.1,2.2)));
  916. // vec3 c = texture(iChannel0, tpos).xyz;
  917. // return Material(vec3(0.3), 1.0-c.g, c.r*0.8 + 0.1);
  918. }
  919.  
  920.  
  921. </script>
  922.  
  923. <script id="hilt_cylinder" type="x-shader/x-fragment">
  924.  
  925. #define ITERATIONS 100
  926.  
  927. float handle(vec3 p) {
  928. vec2 h = vec2(.15, 0.7);
  929. p.y -= 3.65;
  930. vec2 d = abs(vec2(length(p.xz),p.y)) - h;
  931. return min(max(d.x,d.y),0.0) + length(max(d,0.0));
  932. }
  933.  
  934. Material getHiltMaterial(vec3 hp, vec3 ray_dir) {
  935. return Material(vec3(1.0), 0.9, 0.3, vec3(0));
  936. }
  937.  
  938. </script>
  939.  
  940. <script id="fragment_shader" type="x-shader/x-fragment">
  941. precision highp float;
  942. uniform float u_time;
  943. varying vec2 v_position;
  944. uniform float u_width;
  945. uniform float u_height;
  946. uniform sampler2D sampler;
  947. uniform mat4 u_move_matrix;
  948. uniform mat4 u_old_move_matrix;
  949.  
  950. const float PI = 3.1415926535;
  951.  
  952. struct Material {
  953. vec3 color;
  954. float metallic;
  955. float roughness;
  956. vec3 emission;
  957. };
  958.  
  959. $VARIABLES$
  960.  
  961. mat3 rotz(float f) {
  962. return mat3(cos(f), sin(f), 0,
  963. -sin(f), cos(f), 0,
  964. 0,0,1);
  965. }
  966.  
  967. mat3 roty(float f) {
  968. return mat3(cos(f), 0, sin(f),
  969. 0, 1, 0,
  970. -sin(f), 0, cos(f));
  971. }
  972.  
  973. mat3 rotx(float f) {
  974. return mat3(1,0,0,
  975. 0, cos(f), sin(f),
  976. 0, -sin(f), cos(f));
  977. }
  978.  
  979.  
  980.  
  981. float opExtrusion( in vec3 p, in float sdf, in float h) {
  982. vec2 w = vec2( sdf, abs(p.z) - h );
  983. return min(max(w.x,w.y),0.0) + length(max(w,0.0));
  984. }
  985.  
  986. float sdBox( vec3 p, vec3 b) {
  987. vec3 q = abs(p) - b;
  988. return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
  989. }
  990.  
  991. float sdSphere( vec3 p, float s) {
  992. return length(p)-s;
  993. }
  994.  
  995. float sdCylinder( vec3 p, vec3 c) {
  996. return length(p.xz-c.xy)-c.z;
  997. }
  998.  
  999. float sdCappedCylinder( vec3 p, float h, float r) {
  1000. vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(h,r);
  1001. return min(max(d.x,d.y),0.0) + length(max(d,0.0));
  1002. }
  1003.  
  1004. float sdRoundedCylinder( vec3 p, float ra, float rb, float h) {
  1005. vec2 d = vec2( length(p.xz)-2.0*ra+rb, abs(p.y) - h );
  1006. return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rb;
  1007. }
  1008.  
  1009. float sdVerticalCapsule( vec3 p, float h, float r )
  1010. {
  1011. p.x -= clamp( p.x, -h, h );
  1012. return length( p ) - r;
  1013. }
  1014.  
  1015. vec4 opElongate( in vec3 p, in vec3 h) {
  1016. return vec4( p-clamp(p,-h,h), 0.0 ); // faster, but produces zero in the interior elongated box
  1017.  
  1018. //vec3 q = abs(p)-h;
  1019. //return vec4( max(q,0.0), min(max(q.x,max(q.y,q.z)),0.0) );
  1020. }
  1021.  
  1022.  
  1023.  
  1024.  
  1025. //------------------------------------------------------------------------------
  1026. // BRDF
  1027. //------------------------------------------------------------------------------
  1028.  
  1029. float pow5(float x) {
  1030. float x2 = x * x;
  1031. return x2 * x2 * x;
  1032. }
  1033.  
  1034. float D_GGX(float linearRoughness, float NoH, const vec3 h) {
  1035. // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
  1036. float oneMinusNoHSquared = 1.0 - NoH * NoH;
  1037. float a = NoH * linearRoughness;
  1038. float k = linearRoughness / (oneMinusNoHSquared + a * a);
  1039. float d = k * k * (1.0 / PI);
  1040. return d;
  1041. }
  1042.  
  1043. float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) {
  1044. // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
  1045. float a2 = linearRoughness * linearRoughness;
  1046. float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
  1047. float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
  1048. return 0.5 / (GGXV + GGXL);
  1049. }
  1050.  
  1051. vec3 F_Schlick(const vec3 f0, float VoH) {
  1052. // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"
  1053. return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH);
  1054. }
  1055.  
  1056. float F_Schlick(float f0, float f90, float VoH) {
  1057. return f0 + (f90 - f0) * pow5(1.0 - VoH);
  1058. }
  1059.  
  1060. float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) {
  1061. // Burley 2012, "Physically-Based Shading at Disney"
  1062. float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH;
  1063. float lightScatter = F_Schlick(1.0, f90, NoL);
  1064. float viewScatter = F_Schlick(1.0, f90, NoV);
  1065. return lightScatter * viewScatter * (1.0 / PI);
  1066. }
  1067.  
  1068. float Fd_Lambert() {
  1069. return 1.0 / PI;
  1070. }
  1071.  
  1072. //------------------------------------------------------------------------------
  1073. // Indirect lighting
  1074. //------------------------------------------------------------------------------
  1075.  
  1076. vec3 Irradiance_SphericalHarmonics(const vec3 n) {
  1077. // Irradiance from "Ditch River" IBL (http://www.hdrlabs.com/sibl/archive.html)
  1078. return max(
  1079. vec3( 0.754554516862612, 0.748542953903366, 0.790921515418539)
  1080. + vec3(-0.083856548007422, 0.092533500963210, 0.322764661032516) * (n.y)
  1081. + vec3( 0.308152705331738, 0.366796330467391, 0.466698181299906) * (n.z)
  1082. + vec3(-0.188884931542396, -0.277402551592231, -0.377844212327557) * (n.x)
  1083. , 0.0);
  1084. }
  1085.  
  1086. vec2 PrefilteredDFG_Karis(float roughness, float NoV) {
  1087. // Karis 2014, "Physically Based Material on Mobile"
  1088. const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
  1089. const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040);
  1090.  
  1091. vec4 r = roughness * c0 + c1;
  1092. float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
  1093.  
  1094. return vec2(-1.04, 1.04) * a004 + r.zw;
  1095. }
  1096.  
  1097.  
  1098.  
  1099.  
  1100.  
  1101. vec3 A = vec3(0,3,0);
  1102. vec3 B = vec3(0,-4,0);
  1103.  
  1104. float get_point(vec3 p) {
  1105. vec3 pa = p - A, ba = B - A;
  1106. return clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
  1107. }
  1108.  
  1109. int get_led(vec3 p) {
  1110. return int(get_point(p) * 144.0);
  1111. }
  1112.  
  1113. float blade2(vec3 p, bool tangent) {
  1114. vec3 pa = p - A, ba = B - A;
  1115. float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
  1116. vec3 tmp = pa - ba * h;
  1117. float l2 = dot(tmp, tmp);
  1118. // float R = 0.08;
  1119. float R = 0.09;
  1120. if (tangent && l2 >= R*R) {
  1121. return sqrt(l2 - R*R);
  1122. } else {
  1123. return sqrt(l2) - R;
  1124. }
  1125. }
  1126.  
  1127. float blade(vec3 p, bool tangent) {
  1128. float ret = blade2(p, tangent);
  1129. // ret = min(ret, sdSphere(p, 0.8));
  1130. return ret;
  1131. }
  1132. $HILT$
  1133.  
  1134. float map(vec3 p, bool tangent) {
  1135. return min(blade(p, tangent), handle(p));
  1136. }
  1137.  
  1138. vec3 getBladeColor(vec3 pos) {
  1139. return texture2D(sampler, vec2(get_point(pos), 0.5)).rgb;
  1140. }
  1141.  
  1142. float march(vec3 start, vec3 dir, out vec3 haze_color) {
  1143. haze_color = vec3(0,0,0);
  1144. float dist = 0.0;
  1145. float last_dist = 0.0;
  1146. float last_d = 0.0;
  1147. bool hit = false;
  1148. float haze = 1.0;
  1149. for (int i = 0; i < ITERATIONS; i++) {
  1150. vec3 p =start + dir * dist;
  1151. float hilt_dist = handle(p);
  1152. float blade_dist = blade(p, !hit);
  1153. float d = min(hilt_dist, blade_dist);
  1154.  
  1155. if (dist > 20.0) return 10000.0;
  1156. if (d < 0.001) {
  1157. if (d < 0.0) {
  1158. hit = true;
  1159. // dist = last_dist + (dist - last_dist) * (last_d / (last_d - d));
  1160. dist = last_dist;
  1161. continue;
  1162. }
  1163. return dist;
  1164. }
  1165. if (get_led(p) > 0) {
  1166. // haze_color += getBladeColor(p) * clamp(0.002 / (blade_dist*blade_dist), 0.0, 1.0);
  1167.  
  1168. // haze_color += getBladeColor(p) * 0.0005 / (blade_dist*blade_dist*blade_dist) * d;
  1169.  
  1170. float haze_m = clamp(0.004 / (blade_dist*blade_dist), 0.0, 1.0);
  1171. haze_color += getBladeColor(p) * haze_m * haze;
  1172. haze *= (1.0 - haze_m);
  1173. }
  1174. d *= 0.99;
  1175. last_d = d;
  1176. last_dist = dist;
  1177. dist += d;
  1178. }
  1179. // if (hit) return dist;
  1180. // return -1.0;
  1181. return dist;
  1182. }
  1183.  
  1184. float march2(vec3 start, vec3 dir, out vec3 haze_color) {
  1185. haze_color = vec3(0,0,0);
  1186. float dist = 0.0;
  1187. float last_dist = 0.0;
  1188. float last_d = 0.0;
  1189. bool hit = false;
  1190. float haze = 1.0;
  1191. float start_dist = map(start, false);
  1192. for (int i = 0; i < ITERATIONS; i++) {
  1193. vec3 p =start + dir * dist;
  1194. float hilt_dist = handle(p);
  1195. float blade_dist = blade(p, !hit);
  1196. float d = min(hilt_dist, blade_dist);
  1197.  
  1198. if (dist > 20.0) return 10000.0;
  1199. if (d < start_dist / 2.0) {
  1200. if (d < 0.0) {
  1201. hit = true;
  1202. // dist = last_dist + (dist - last_dist) * (last_d / (last_d - d));
  1203. dist = last_dist;
  1204. continue;
  1205. }
  1206. return dist;
  1207. }
  1208. if (get_led(p) > 0) {
  1209. // haze_color += getBladeColor(p) * clamp(0.002 / (blade_dist*blade_dist), 0.0, 1.0);
  1210.  
  1211. // haze_color += getBladeColor(p) * 0.0005 / (blade_dist*blade_dist*blade_dist) * d;
  1212.  
  1213. float haze_m = clamp(0.004 / (blade_dist*blade_dist), 0.0, 1.0);
  1214. haze_color += getBladeColor(p) * haze_m * haze;
  1215. haze *= (1.0 - haze_m);
  1216. }
  1217. last_d = d;
  1218. last_dist = dist;
  1219. dist += d;
  1220. }
  1221. return dist;
  1222. }
  1223.  
  1224.  
  1225.  
  1226. vec3 getNormal(vec3 p) {
  1227. float E = 0.00001;
  1228. vec3 X = vec3(E,0.0,0.0);
  1229. vec3 Y = vec3(0.0,E,0.0);
  1230. vec3 Z = vec3(0.0,0.0,E);
  1231. // return normalize(vec3(map(p + X, false) - map(p - X, false),
  1232. // map(p + Y, false) - map(p - Y, false),
  1233. // map(p + Z, false) - map(p - Z, false)));
  1234.  
  1235. float D = map(p, false);
  1236. return normalize(vec3(map(p + X, false) - D,
  1237. map(p + Y, false) - D,
  1238. map(p + Z, false) - D));
  1239. }
  1240.  
  1241. float line_dist(vec3 pt1, vec3 dir1, vec3 pt2, vec3 dir2) {
  1242. vec3 n = normalize(cross(dir1, dir2));
  1243. return abs(dot(n, pt1 - pt2));
  1244. }
  1245.  
  1246.  
  1247.  
  1248. // Cast a ray starting at "from" and keep going until we hit something or
  1249. // run out of iterations.
  1250. float ray(vec3 from, vec3 direction) {
  1251. // How far we travelled (so far)
  1252. float travel_distance = 0.0;
  1253. float last_travel_distance = 0.0;
  1254. bool hit = false;
  1255. for (int i = 0; i < 60; i++) {
  1256. // calculate the current position along the ray
  1257. vec3 position = from + direction * travel_distance;
  1258. float tmp = map(position, false);
  1259. float distance_to_closest_object = tmp;
  1260. float step_size = hit ? tmp : tmp;
  1261.  
  1262. if (distance_to_closest_object < 0.0005) {
  1263. return travel_distance;
  1264. }
  1265. last_travel_distance = travel_distance;
  1266.  
  1267. // We can safely advance this far since we know that the closest
  1268. // object is this far away. (But possibly in a completely different
  1269. // direction.)
  1270. travel_distance += step_size;
  1271. }
  1272.  
  1273. return travel_distance;
  1274. }
  1275.  
  1276.  
  1277. float shadow(in vec3 origin, in vec3 direction) {
  1278. float hit = 1.0;
  1279. float t = 0.001;
  1280.  
  1281. for (int i = 0; i < 100; i++) {
  1282. float h = map(origin + direction * t, false);
  1283. if (h < 0.0002) return 0.0;
  1284. t += h;
  1285. hit = min(hit, 10.0 * h / t);
  1286. if (t >= 2.5) break;
  1287. }
  1288.  
  1289. return clamp(hit, 0.0, 1.0);
  1290. }
  1291.  
  1292.  
  1293.  
  1294. #define saturate(x) clamp(x, 0.0, 1.0)
  1295.  
  1296.  
  1297. Material getMaterial(vec3 hp, vec3 dir) {
  1298. if (blade(hp, false) <= handle(hp)) {
  1299. return Material(vec3(1.0), 0.0, 0.3, getBladeColor(hp));
  1300. } else {
  1301. return getHiltMaterial(hp, dir);
  1302. }
  1303. }
  1304.  
  1305. void main() {
  1306. vec3 light = vec3(-5, 8.0, -8.0);
  1307. vec3 eye = vec3(0.0, 0.0, -12.0);
  1308. float zoom = 1.5;
  1309. vec3 dir = normalize(
  1310. vec3((v_position.x) / u_width / zoom,
  1311. (v_position.y) / u_width / zoom, 2.0));
  1312. mat4 rot3 = mat4(0.0, -1.0, 0.0, 0.0,
  1313. 0.0, 0.0, -1.0, 0.0,
  1314. 1.0, 0.0, 0.0, 0.0,
  1315. 0.0, 0.0, 0.0, 1.0);
  1316.  
  1317. mat4 rot = rot3 * u_move_matrix;
  1318. vec3 new_eye = (rot * vec4(eye.xyz, 1.0)).xyz;
  1319. vec3 new_dir = (rot * vec4(dir.xyz, 1.0)).xyz;
  1320. light = (rot * vec4(light.xyz, 1.0)).xyz;
  1321.  
  1322. mat4 old_rot = rot3 * u_old_move_matrix;
  1323. vec3 old_eye = (old_rot * vec4(eye.xyz, 1.0)).xyz;
  1324. vec3 old_dir = (old_rot * vec4(dir.xyz, 1.0)).xyz;
  1325.  
  1326. float minpoint = 1.0;
  1327.  
  1328. if (true) {
  1329. // gauss
  1330. for (int i = 0; i < 5; i++) {
  1331. vec3 e1 = mix(old_eye, new_eye, minpoint);
  1332. vec3 d1 = normalize(mix(old_dir, new_dir, minpoint));
  1333. float delta = 0.001;
  1334. vec3 e2 = mix(old_eye, new_eye, minpoint - delta);
  1335. vec3 d2 = normalize(mix(old_dir, new_dir, minpoint - delta));
  1336. float dist1 = line_dist(e1, d1, A, B-A);
  1337. float dist2 = line_dist(e2, d2, A, B-A);
  1338. minpoint = minpoint - dist1 / ((dist1-dist2)/delta);
  1339. }
  1340. minpoint = clamp(minpoint, 0.0, 1.0);
  1341. }
  1342.  
  1343. eye = mix(old_eye, new_eye, minpoint);
  1344. dir = normalize(mix(old_dir, new_dir, minpoint));
  1345.  
  1346. vec3 haze_color;
  1347. float x = march(eye, dir, haze_color);
  1348.  
  1349. vec3 hp = eye + dir * x;
  1350. vec3 color = vec3(0);
  1351.  
  1352. if (x < 20.0) {
  1353. Material mat = getMaterial(hp, dir);
  1354.  
  1355. // vec3 normal = getNormal(hp);
  1356. // vec3 light_dir = light - hp;
  1357. // float light_dist2 = dot(light_dir, light_dir) / 10.0;
  1358. // light_dir = normalize(light_dir);
  1359. // float l = dot(light_dir, normal) * 5.0;
  1360.  
  1361. // l = max(l, 0.0);
  1362. // l /= sqrt(light_dist2);
  1363. // l += 0.1; // ambient
  1364. // color *= l;
  1365. // color += color2 * 2.0;
  1366.  
  1367. // vec3 reflection = reflect(dir, normal);
  1368. // float l2 = max(dot(reflection, light_dir), 0.0);
  1369. // color += vec3(200.0) * pow(l2, 60.0);
  1370. // vec3 haze = vec3(0.01, 0.01, 0.01);
  1371. // float haze_mix = x < 0.0 ? 0.0 : pow(0.95, x);
  1372. // color = haze_mix * color + (1.0 - haze_mix) * haze;
  1373. // color += haze_color;
  1374.  
  1375.  
  1376. // color = sqrt(color);
  1377. // gl_FragColor = vec4(color, 1.0);
  1378.  
  1379. vec3 v = normalize(-dir);
  1380. vec3 n = getNormal(hp);
  1381. vec3 l = light - hp;
  1382. vec3 ld = normalize(l);
  1383. vec3 h = normalize(ld + v);
  1384. vec3 r = normalize(reflect(dir, n));
  1385.  
  1386. float NoV = abs(dot(n, v)) + 1e-5;
  1387. float NoL = saturate(dot(n, ld));
  1388. float NoH = saturate(dot(n, h));
  1389. float LoH = saturate(dot(ld, h));
  1390.  
  1391. vec3 baseColor = mat.color * 0.5;
  1392.  
  1393. float intensity = 3.8;
  1394. float indirectIntensity = 0.1;
  1395.  
  1396. float linearRoughness = mat.roughness * mat.roughness;
  1397. vec3 diffuseColor = (1.0 - mat.metallic) * baseColor.rgb;
  1398. vec3 f0 = 0.04 * (1.0 - mat.metallic) + baseColor.rgb * mat.metallic;
  1399.  
  1400. float attenuation = shadow(hp, l);
  1401.  
  1402. // specular BRDF
  1403. float D = D_GGX(linearRoughness, NoH, h);
  1404. float V = V_SmithGGXCorrelated(linearRoughness, NoV, NoL);
  1405. vec3 F = F_Schlick(f0, LoH);
  1406. vec3 Fr = (D * V) * F;
  1407.  
  1408. // diffuse BRDF
  1409. vec3 Fd = diffuseColor * Fd_Burley(linearRoughness, NoV, NoL, LoH);
  1410.  
  1411. color = Fd + Fr;
  1412. color *= (intensity * attenuation * NoL) * vec3(0.98, 0.92, 0.89);
  1413.  
  1414. // diffuse indirect
  1415. vec3 indirectDiffuse = Irradiance_SphericalHarmonics(n) * Fd_Lambert();
  1416.  
  1417. vec3 indirect_haze_color;
  1418. float indirectHit = march2(hp + r * 0.1, r, indirect_haze_color);
  1419. // float indirectHit = ray(hp + r * 0.1, r);
  1420. vec3 indirectSpecular = vec3(0.5, 0.5, 0.5) + (dot(vec3(0,0,-1), r) + 1.0) * 0.2;
  1421. vec3 indirectHitPosition = hp + r * indirectHit;
  1422. if (indirectHit < 20.0) {
  1423. Material indirectMaterial = getMaterial(indirectHitPosition, r);
  1424. indirectSpecular = indirectMaterial.color + indirectMaterial.emission;
  1425. }
  1426. indirectSpecular += indirect_haze_color;
  1427.  
  1428. // indirect contribution
  1429. vec2 dfg = PrefilteredDFG_Karis(mat.roughness, NoV);
  1430. vec3 specularColor = f0 * dfg.x + dfg.y;
  1431. vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;
  1432.  
  1433. color += ibl * indirectIntensity;
  1434.  
  1435. color += mat.emission;
  1436. }
  1437.  
  1438. // vec3 haze = vec3(0.01, 0.01, 0.01);
  1439. // float haze_mix = x > 20.0 ? 0.0 : pow(0.95, x);
  1440. // color = haze_mix * color + (1.0 - haze_mix) * haze;
  1441. color += haze_color;
  1442.  
  1443. // Clip to white
  1444. color += vec3(dot(max(color - vec3(1), vec3(0)), vec3(0.33)));
  1445. // color += vec3(dot(max(color - vec3(1), 0.0), vec3(0.299, 0.587, 0.114)));
  1446. // color += vec3(max(dot(color, vec3(0.299, 0.587, 0.114)) - 1.0, 0.0));
  1447.  
  1448. // gl_FragColor = pow(vec4(color, 1.0), vec4(1.0/2.2));
  1449. gl_FragColor = pow(vec4(color, 1.0), vec4(1.0/2.2));
  1450. }
  1451. </script>
  1452.  
  1453. <script>
  1454.  
  1455. function SafeguardInputs(e) {
  1456. if (e.target.value == "") {
  1457. console.log("SafeguardInputs() run to avoid null value");
  1458. var changeEvent = new Event('change');
  1459. e.target.value = 0;
  1460. if (e.target.id === "VARIANT_VALUE") {
  1461. FIND("VARIANT_SLIDER").value = 0;
  1462. }
  1463. e.target.dispatchEvent(changeEvent);
  1464. }
  1465. }
  1466.  
  1467. var gl = null;
  1468. var shaderProgram = null;
  1469. var t = 0.0;
  1470.  
  1471. var width;
  1472. var height;
  1473.  
  1474. // Create n textures of about 1MB each.
  1475. function initGL() {
  1476. // Clear existing tab links and tab bodies before populating
  1477. // var tabLinksElement = FIND("TABLINKS");
  1478. // var tabBodiesElement = FIND("TABBODIES");
  1479. // tabLinksElement.innerHTML = "";
  1480. // tabBodiesElement.innerHTML = "";
  1481.  
  1482. AddTab("color", "Styles",effect_links.sort().join(""))
  1483. AddTab("rgb", "Colors", ""); updateRgbTabContent();
  1484. AddTab("layer", "Layers", layer_links.sort().join(""));
  1485. AddTab("function", "Functions", function_links.sort().join(""));
  1486. AddTab("transition", "Transitions", transition_links.sort().join(""));
  1487. AddTab("effect", "Effects");
  1488. AddTab("lockup_type", "Lockup Types");
  1489. AddTab("arguments", "Arguments");
  1490. AddTab("example", "Examples", template_links.join(""));
  1491. AddTab("history", "History");
  1492. AddTab("arg_string", "ArgString");
  1493. EFFECT_ENUM_BUILDER.addToTab("effect", "EFFECT_");
  1494. LOCKUP_ENUM_BUILDER.addToTab("lockup_type", "LOCKUP_");
  1495. ArgumentName_ENUM_BUILDER.addToTab("arguments", "");
  1496.  
  1497. // Add arg string.
  1498. var A = "";
  1499. A += "Arg string: <input id=ARGSTR name=arg type=text size=80 value='builtin 0 1' onchange='ArgStringChanged()' /><br><table>";
  1500. var v = Object.keys(ArgumentName_ENUM_BUILDER.value_to_name);
  1501. for (var i = 0; i < v.length; i++) {
  1502. var V = parseInt(v[i]);
  1503. var N = ArgumentName_ENUM_BUILDER.value_to_name[V];
  1504. A += "<tr><td>" + N + "</td><td>";
  1505. if (N.search("COLOR") >= 0) {
  1506. A += "<input type=color id=ARGSTR_"+N+" onclick='ClickArgColor("+N+")' onchange='ClickArgColor("+N+")' >";
  1507. } else {
  1508. A += "<input type=button value='<' onclick='IncreaseArg("+N+",-1)' >";
  1509. A += "<input id=ARGSTR_"+N+" type='number' size=6 value=0 onchange='ArgChanged("+N+")' onfocusout='SafeguardInputs(event)' >";
  1510. A += "<input type=button value='>' onclick='IncreaseArg("+N+",1)' >";
  1511. }
  1512. A += "</td></tr>\n";
  1513. }
  1514. A += "</table\n";
  1515. AddTabContent("arg_string", A);
  1516.  
  1517.  
  1518. var canvas = FIND("canvas_id");
  1519.  
  1520. width = window.innerWidth;
  1521. height = window.innerHeight;
  1522.  
  1523. if(window.devicePixelRatio !== undefined) {
  1524. dpr = window.devicePixelRatio;
  1525. } else {
  1526. dpr = 1;
  1527. }
  1528.  
  1529. width = width * 2 / 3;
  1530. height /= 3;
  1531. canvas.width = width * dpr;
  1532. canvas.height = height * dpr;
  1533. canvas.style.width = width + 'px';
  1534. canvas.style.height = height + 'px';
  1535.  
  1536. var enlargeCanvas = false;
  1537. FIND('ENLARGE').onclick = function() {
  1538. enlargeCanvas = !enlargeCanvas;
  1539. this.innerText = enlargeCanvas ? 'Reduce' : 'Enlarge';
  1540. if (enlargeCanvas) {
  1541. height = window.innerHeight / 2;
  1542. } else {
  1543. height = window.innerHeight / 3;
  1544. }
  1545.  
  1546. // Update the canvas dimensions
  1547. canvas.width = width * dpr;
  1548. canvas.height = height * dpr;
  1549. canvas.style.width = width + 'px';
  1550. canvas.style.height = height + 'px';
  1551. }
  1552.  
  1553. gl = canvas.getContext("experimental-webgl", {colorSpace: "srgb", antialias:false});
  1554.  
  1555. if (!gl) {
  1556. throw "Unable to fetch WebGL rendering context for Canvas";
  1557. }
  1558.  
  1559. var str = new URL(window.location.href).searchParams.get("S");
  1560. if (!str) {
  1561. str = "Layers<Red,ResponsiveLockupL<White,TrInstant,TrFade<100>,Int<26000>>,ResponsiveLightningBlockL<White>,ResponsiveMeltL<Mix<TwistAngle<>,Red,Yellow>>,ResponsiveDragL<White>,ResponsiveClashL<White,TrInstant,TrFade<200>,Int<26000>>,ResponsiveBlastL<White>,ResponsiveBlastWaveL<White>,ResponsiveBlastFadeL<White>,ResponsiveStabL<White>,InOutTrL<TrWipe<300>,TrWipeIn<500>>>";
  1562. }
  1563. FIND("style").value = str;
  1564.  
  1565. Run();
  1566. DoLayerize();
  1567.  
  1568. // Bind a vertex buffer with a single triangle
  1569. var buffer = gl.createBuffer();
  1570. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  1571. var bufferData = new Float32Array([
  1572. -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]);
  1573. gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
  1574. gl.enableVertexAttribArray(shaderProgram.a_position);
  1575. gl.vertexAttribPointer(shaderProgram.a_position, 2, gl.FLOAT, false, 0, 0);
  1576.  
  1577. var texture = gl.createTexture();
  1578. gl.bindTexture(gl.TEXTURE_2D, texture);
  1579.  
  1580. // Start the event loop.
  1581. tick();
  1582. }
  1583.  
  1584. class Matrix {
  1585. constructor(w, h) {
  1586. this.w = w;
  1587. this.h = h;
  1588. this.values = new Float32Array(w * h);
  1589. if (w == h) {
  1590. for (var z = 0; z < w; z++) {
  1591. this.set(z, z, 1.0);
  1592. }
  1593. }
  1594. }
  1595. get(x, y) { return this.values[y * this.w + x]; }
  1596. set(x, y, v) { this.values[y * this.w + x] = v; }
  1597. mult(o) {
  1598. var ret = new Matrix(o.w, this.h);
  1599. for (var x = 0; x < o.w; x++) {
  1600. for (var y = 0; y < this.h; y++) {
  1601. var sum = 0.0;
  1602. for (var z = 0; z < this.w; z++) {
  1603. sum += this.get(z, y) * o.get(x, z);
  1604. }
  1605. ret.set(x, y, sum);
  1606. }
  1607. }
  1608. return ret;
  1609. }
  1610. static mkzrot(a) {
  1611. var ret = new Matrix(4, 4);
  1612. var s = Math.sin(a);
  1613. var c = Math.cos(a);
  1614. ret.set(0, 0, c);
  1615. ret.set(1, 1, c);
  1616. ret.set(0, 1, s);
  1617. ret.set(1, 0, -s);
  1618. return ret;
  1619. }
  1620. static mkxrot(a) {
  1621. var ret = new Matrix(4, 4);
  1622. var s = Math.sin(a);
  1623. var c = Math.cos(a);
  1624. ret.set(1, 1, c);
  1625. ret.set(2, 2, c);
  1626. ret.set(1, 2, s);
  1627. ret.set(2, 1, -s);
  1628. return ret;
  1629. }
  1630. static mkyrot(a) {
  1631. var ret = new Matrix(4, 4);
  1632. var s = Math.sin(a);
  1633. var c = Math.cos(a);
  1634. ret.set(0, 0, c);
  1635. ret.set(2, 2, c);
  1636. ret.set(0, 2, s);
  1637. ret.set(2, 0, -s);
  1638. return ret;
  1639. }
  1640. static mktranslate(x, y, z) {
  1641. var ret = new Matrix(4, 4);
  1642. ret.set(0,3,x);
  1643. ret.set(1,3,y);
  1644. ret.set(2,3,z);
  1645. return ret;
  1646. }
  1647.  
  1648. tostr() {
  1649. var ret = "{";
  1650. for (var x = 0; x < this.w; x++) {
  1651. for (var y = 0; y < this.h; y++) {
  1652. ret += this.get(x, y);
  1653. ret += ", ";
  1654. }
  1655. ret += ";";
  1656. }
  1657. ret += "}";
  1658. return ret;
  1659. }
  1660. };
  1661.  
  1662. function default_move_matrix() {
  1663. return Matrix.mktranslate(-0.023, 0.0, -0.12);
  1664. }
  1665.  
  1666. var MOVE_MATRIX = default_move_matrix();
  1667. var OLD_MOVE_MATRIX = default_move_matrix();
  1668. var MOUSE_POSITIONS = [];
  1669. var IN_FRAME = false;
  1670. var BLADE_ANGLE = 0.0;
  1671.  
  1672. function mouse_speed(t1, t2) {
  1673. var dx = MOUSE_POSITIONS[t1+0]-MOUSE_POSITIONS[t2+0];
  1674. var dy = MOUSE_POSITIONS[t1+1]-MOUSE_POSITIONS[t2+1];
  1675. var dt = MOUSE_POSITIONS[t1+2]-MOUSE_POSITIONS[t2+2];
  1676. if (dt == 0) return 0.0;
  1677. return Math.sqrt(dx * dx + dy * dy) / Math.abs(dt);
  1678. }
  1679.  
  1680. function mouse_move(e) {
  1681. if (mouseswingsState.get()) return;
  1682. IN_FRAME = true;
  1683. var canvas = FIND("canvas_id");
  1684. var rect = canvas.getBoundingClientRect();
  1685. var w = rect.right - rect.left;
  1686. var h = rect.bottom - rect.top;
  1687. var d = min(h, w);
  1688. var x = (e.clientX - (rect.left + rect.right)/2.0) / d;
  1689. var y = (e.clientY - (rect.top + rect.bottom)/2.0) / d;
  1690. var now = actual_millis();
  1691. MOUSE_POSITIONS = MOUSE_POSITIONS.concat([x* 10000, y * 10000, now])
  1692. while (MOUSE_POSITIONS.length > 0 && now - MOUSE_POSITIONS[2] > 100) {
  1693. MOUSE_POSITIONS = MOUSE_POSITIONS.slice(3);
  1694. }
  1695.  
  1696. // console.log("x = "+x+" y = "+y);
  1697. if (e.shiftKey) {
  1698. MOVE_MATRIX = default_move_matrix();
  1699. } else {
  1700. BLADE_ANGLE=-y;
  1701. MOVE_MATRIX = Matrix.mkzrot(Math.PI/2.0).mult(Matrix.mkxrot(-y)).mult(Matrix.mkzrot(y));
  1702.  
  1703. MOVE_MATRIX = Matrix.mkyrot(Math.PI/2.0)
  1704. MOVE_MATRIX = MOVE_MATRIX.mult(Matrix.mktranslate(1.0, 0.04, 0.0));
  1705. MOVE_MATRIX = MOVE_MATRIX.mult(Matrix.mkyrot(-x/3));
  1706. MOVE_MATRIX = MOVE_MATRIX.mult(Matrix.mktranslate(-1.0, 0.0, 0.0));
  1707. MOVE_MATRIX = MOVE_MATRIX.mult(Matrix.mkzrot(-y));
  1708. MOVE_MATRIX = MOVE_MATRIX.mult(Matrix.mktranslate(-0.17, 0.0, 0.0));
  1709. }
  1710. // console.log(MOVE_MATRIX.values);
  1711. }
  1712.  
  1713. function get_swing_speed() {
  1714. var now = actual_millis();
  1715. while (MOUSE_POSITIONS.length > 0 && now - MOUSE_POSITIONS[2] > 100) {
  1716. MOUSE_POSITIONS = MOUSE_POSITIONS.slice(3);
  1717. }
  1718. var len = MOUSE_POSITIONS.length;
  1719. if (len >= 6) {
  1720. return mouse_speed(0, len - 6);
  1721. }
  1722. if (IN_FRAME || !autoswingState.get()) return 0.0;
  1723. return Math.sin(millis() * Math.PI / 1000.0) * 250 + 250
  1724. }
  1725.  
  1726. function get_swing_accel() {
  1727. var now = actual_millis();
  1728. while (MOUSE_POSITIONS.length > 0 && now - MOUSE_POSITIONS[2] > 100) {
  1729. MOUSE_POSITIONS = MOUSE_POSITIONS.slice(3);
  1730. }
  1731. var len = MOUSE_POSITIONS.length;
  1732. if (len >= 6) {
  1733. var speed = mouse_speed(0, len - 6);
  1734. if (MOUSE_POSITIONS.length >= 9) {
  1735. return (speed - mouse_speed(0, Math.floor(len/6)*3)) * 2.0;
  1736. }
  1737. }
  1738. if (IN_FRAME) return 0.0;
  1739. return Math.cos(millis() * Math.PI / 500.0) * 100 + 100
  1740. }
  1741.  
  1742. function mouse_leave(e) {
  1743. console.log("Mouse leave!");
  1744. MOVE_MATRIX = default_move_matrix();
  1745. MOUSE_POSITIONS = [];
  1746. IN_FRAME = false;
  1747. }
  1748.  
  1749.  
  1750. function compile() {
  1751. // Create a shader that samples a 2D image.
  1752. var vertexShader = gl.createShader(gl.VERTEX_SHADER);
  1753. gl.shaderSource(vertexShader,
  1754. FIND("vertex_shader").textContent);
  1755. gl.compileShader(vertexShader);
  1756.  
  1757. var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  1758. var shader_code = FIND("fragment_shader").textContent;
  1759.  
  1760. variables = [];
  1761. // shader_code = shader_code.replace("$FUNCTION$", current_style.gencode());
  1762. shader_code = shader_code.replace("$VARIABLES$", variables.join("\n"));
  1763. if (graflexState.get()) {
  1764. shader_code = shader_code.replace("$HILT$", FIND("hilt_graflex").textContent);
  1765. } else {
  1766. shader_code = shader_code.replace("$HILT$", FIND("hilt_cylinder").textContent);
  1767. }
  1768. // console.log(shader_code);
  1769.  
  1770. gl.shaderSource(fragmentShader, shader_code);
  1771. gl.compileShader(fragmentShader);
  1772. if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  1773.  
  1774. var v = shader_code.split("\n");
  1775. for (var i = 0; i < v.length; i++) {
  1776. console.log( (i+1) + ": " + v[i]);
  1777. }
  1778. throw "Could not compile shader:\n\n" + gl.getShaderInfoLog(fragmentShader);
  1779. }
  1780.  
  1781. shaderProgram = gl.createProgram();
  1782. gl.attachShader(shaderProgram, vertexShader);
  1783. gl.attachShader(shaderProgram, fragmentShader);
  1784. gl.linkProgram(shaderProgram);
  1785. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  1786. throw "Could not link the shader program!\n\n" + gl.getProgramInfoLog(shaderProgram);
  1787. }
  1788. gl.useProgram(shaderProgram);
  1789.  
  1790. }
  1791.  
  1792. var varnum = 0;
  1793. var variables = [];
  1794. var vartypes = {};
  1795.  
  1796. function genvar(t) {
  1797. varnum++;
  1798. var variable = "u_" + varnum;
  1799. variables.push( "uniform " + t + " " + variable + ";");
  1800. vartypes[variable] = t;
  1801. return variable;
  1802. }
  1803.  
  1804. function setvar(variable, val) {
  1805. // console.log(variable + " = " + val);
  1806. if (vartypes[variable] == "float") {
  1807. gl.uniform1f(gl.getUniformLocation(shaderProgram, variable), val);
  1808. return;
  1809. }
  1810. if (vartypes[variable] == "int") {
  1811. gl.uniform1i(gl.getUniformLocation(shaderProgram, variable), val);
  1812. return;
  1813. }
  1814. console.log("SETVAR ERROR " + variable);
  1815. }
  1816.  
  1817. class MyError {
  1818. constructor(desc) {
  1819. this.desc = desc;
  1820. this.begin_pos = -1;
  1821. this.end_pos = -1;
  1822. }
  1823. setBegin(pos) { this.begin_pos = pos; return this; }
  1824. setEnd(pos) { this.end_pos = pos; return this; }
  1825. setArg(arg) {
  1826. if (arg && arg.__end_pos) {
  1827. this.begin_pos = arg.__begin_pos;
  1828. this.end_pos = arg.__end_pos;
  1829. }
  1830. return this;
  1831. }
  1832. setThis(arg) {
  1833. if (arg && arg.__end_pos && this.begin_pos == -1) {
  1834. this.begin_pos = arg.__begin_pos;
  1835. this.end_pos = arg.__end_pos;
  1836. }
  1837. return this;
  1838. }
  1839. valueOf() { return this.desc; }
  1840. };
  1841.  
  1842. function Arg(expected_type, arg, default_arg) {
  1843. //console.log("ARGUMENT: " + expected_type);
  1844. //console.log(arg);
  1845. //if (typeof(arg) == "object") console.log(arg.ID);
  1846. //console.log(default_arg);
  1847. if (arg == undefined) {
  1848. if (typeof(default_arg) == "number") {
  1849. // console.log("DEFAULT ARG" + default_arg);
  1850. return new INTEGER(default_arg);
  1851. }
  1852. if (default_arg != undefined) {
  1853. // This must copy the argument!
  1854. return default_arg;
  1855. }
  1856. throw "Too few arguments";
  1857. }
  1858. if (typeof(arg) != "number" && !arg.getType) {
  1859. throw "What is this?? " + arg;
  1860. }
  1861. if (expected_type == "TIME_FUNCTION" && arg.getType() == "FUNCTION") {
  1862. return arg;
  1863. }
  1864. if (typeof(arg) != "number" && arg.getType() != expected_type) {
  1865. throw "Expected " + expected_type + " but got " + arg;
  1866. }
  1867. if (expected_type == "INT" && typeof(arg) == "number") {
  1868. return new INTEGER(arg);
  1869. }
  1870. if (expected_type == "INT" || expected_type == "EFFECT" || expected_type == "LOCKUP_TYPE" || expected_type == "ArgumentName") {
  1871. return arg;
  1872. }
  1873. if (expected_type == "COLOR" ||
  1874. expected_type == "FireConfig" ||
  1875. expected_type == "TRANSITION" ||
  1876. expected_type == "FUNCTION" ||
  1877. expected_type == "TIME_FUNCTION") {
  1878. if (typeof(arg) != "object") {
  1879. throw "Expected a " + expected_type;
  1880. }
  1881. return arg;
  1882. }
  1883.  
  1884. throw "Not INT, COLOR, EFFECT, LOCKUP_TYPE, FUNCTION or TRANSITION";
  1885. }
  1886.  
  1887. function IntArg(arg, def_arg) { return Arg("INT", arg, def_arg); }
  1888. function ColorArg(arg, def_arg) { return Arg("COLOR", arg, def_arg); }
  1889.  
  1890. var pp_is_url = 0;
  1891. var pp_is_verbose = 0;
  1892.  
  1893. var next_id = 1000;
  1894. var style_ids = {};
  1895.  
  1896. var identifiers = {};
  1897.  
  1898. function AddIdentifier(name, value) {
  1899. identifiers[name] = value;
  1900. }
  1901.  
  1902. class ARG {
  1903. constructor(name, type, comment, default_value) {
  1904. this.name = name;
  1905. this.type = type;
  1906. this.comment = comment;
  1907. this.default_value = default_value;
  1908. }
  1909. };
  1910.  
  1911. class STYLE {
  1912. constructor(comment, args) {
  1913. this.comment = comment;
  1914. // if (args) console.log(args);
  1915. this.args = args;
  1916. this.argnum = 0;
  1917. this.argdefs = [];
  1918. this.super_short_desc = false;
  1919. this.ID = next_id;
  1920. next_id ++;
  1921. }
  1922.  
  1923. add_arg(name, expected_type, comment, default_value) {
  1924. if (focus_trace[0] == this.args[this.argnum]) {
  1925. focus_trace = [this, name, expected_type, focus_trace];
  1926. }
  1927. // console.log("add_arg");
  1928. // console.log(name);
  1929. // console.log(this.args);
  1930. // console.log(default_value);
  1931. try {
  1932. this[name] = Arg(expected_type, this.args[this.argnum], default_value);
  1933. // console.log(this[name]);
  1934. } catch(e) {
  1935. if (typeof(e) == "string") {
  1936. e = new MyError(e + " for argument " + (this.argnum + 1) + " (" + name + ")");
  1937. e.setArg(this.args[this.argnum]);
  1938. }
  1939. throw e;
  1940. }
  1941. this.argnum++;
  1942. this.argdefs.push(new ARG(name, expected_type, comment, default_value));
  1943. }
  1944.  
  1945. get_id() {
  1946. style_ids[this.ID] = this;
  1947. return this.ID;
  1948. }
  1949.  
  1950. DOCOPY() {
  1951. pp_is_url++;
  1952. var url = this.pp();
  1953. pp_is_url--;
  1954. var parser = new Parser(url, classes, identifiers);
  1955. ret = parser.parse();
  1956. ret.COMMENT = this.COMMENT;
  1957. return ret;
  1958. }
  1959.  
  1960. call_pp_no_comment(arg) {
  1961. var C = arg.COMMENT;
  1962. arg.COMMENT = null;
  1963. var ret;
  1964. try {
  1965. ret = arg.pp();
  1966. }
  1967. finally {
  1968. arg.COMMENT = C;
  1969. }
  1970. return ret;
  1971. }
  1972.  
  1973. DescribeValue(arg) {
  1974. if (typeof(arg) == "undefined") return "undefined";
  1975. if (typeof(arg) == "number") {
  1976. return "" + arg;
  1977. } else {
  1978. return arg.pp();
  1979. // return this.call_pp_no_comment(arg);
  1980. }
  1981. }
  1982.  
  1983. Indent(text) {
  1984. return text;
  1985. }
  1986.  
  1987. gencomment() {
  1988. if (!this.COMMENT) return "";
  1989. var ret = this.COMMENT;
  1990. if (ret[ret.length-1] != " ") ret += " ";
  1991. ret = "/*"+ret+"*/";
  1992. if (this.COMMENT.split("\n").length > 1) {
  1993. ret += "\n";
  1994. } else {
  1995. ret += " ";
  1996. }
  1997. return ret;
  1998. }
  1999.  
  2000. addcomment(COMMENT) {
  2001. if (!COMMENT) return;
  2002. if (!this.COMMENT) {
  2003. this.COMMENT = COMMENT;
  2004. } else {
  2005. this.COMMENT += "\n" + COMMENT;
  2006. }
  2007. }
  2008.  
  2009. prependcomment(COMMENT) {
  2010. if (!COMMENT) return;
  2011. if (!this.COMMENT) {
  2012. this.COMMENT = COMMENT;
  2013. } else {
  2014. this.COMMENT = COMMENT + "\n" + this.COMMENT;
  2015. }
  2016. }
  2017.  
  2018. PPURL(name, note) {
  2019. if (this.super_short_desc) return "$";
  2020. pp_is_url++;
  2021. var ret = name;
  2022. ret = this.gencomment() + ret;
  2023. var comma = false;
  2024. if (arguments.length > 2 || this.argdefs.length > 0) {
  2025. ret += "<";
  2026. for (var i = 2; i < arguments.length; i += 2) {
  2027. if (comma) ret += ",";
  2028. comma = true;
  2029. var V = this.DescribeValue(arguments[i]);
  2030. var arg = this.Indent(V);
  2031. ret += arg;
  2032. }
  2033. ret += ">";
  2034. }
  2035. pp_is_url--;
  2036.  
  2037. return ret;
  2038. }
  2039.  
  2040. extraButtons(arg) {
  2041. return "";
  2042. }
  2043.  
  2044. PP(name, note) {
  2045. if (pp_is_url) {
  2046. return this.PPURL.apply(this, arguments);
  2047. }
  2048. var id = this.get_id();
  2049. var ret = "";
  2050. if (this.COMMENT) {
  2051. ret += "/* "+this.COMMENT.split("\n").join("<br>")+" */<br>";
  2052. console.log("RET = " + ret);
  2053. }
  2054. ret += "<div id=X" + id + " class='pp-container' onclick='FocusOn(" + id + ",event)'>\n";
  2055. ret += "<span title='" + note + "'>" + name + "</span>&lt;\n";
  2056. ret += "<div class='pp-content'>\n";
  2057. var comma = false;
  2058. for (var i = 2; i < arguments.length; i += 2) {
  2059. if (comma) ret += ",<br>";
  2060. comma = true;
  2061. var arg = arguments[i];
  2062. var note = arguments[i+1];
  2063. var comment = null;
  2064. if (typeof(arg) == "number") {
  2065. arg = "" + arg;
  2066. } else {
  2067. comment = arg.COMMENT;
  2068. arg = this.call_pp_no_comment(arg);
  2069. }
  2070. if (arg.indexOf("<br>") == -1 && arg.indexOf("<div") == -1 && !comment) {
  2071. ret += arg+" /* "+note+" */\n";
  2072. } else {
  2073. ret += "/* "+note+" */"+ this.extraButtons(i/2) +"<br>\n";
  2074. if (comment) {
  2075. ret += "/* "+comment+" */<br>\n";
  2076. }
  2077. ret += arg;
  2078. }
  2079. }
  2080. ret += "</div>&gt;</div>\n";
  2081.  
  2082. return ret;
  2083. }
  2084.  
  2085. PPshort(name, note) {
  2086. if (pp_is_url) {
  2087. return this.PPURL.apply(this, arguments);
  2088. }
  2089. var id = this.get_id();
  2090. var ret = this.gencomment();
  2091. ret += "<div id=X" + id + " class='pp-container' onclick='FocusOn(" + id + ",event)'>\n";
  2092. ret += "<span title='" + note + "'>" + name + "</span>\n";
  2093.  
  2094. if (arguments.length > 2) {
  2095. ret += "&lt;";
  2096. var comma = false;
  2097. for (var i = 2; i < arguments.length; i += 2) {
  2098. if (comma) ret += ",";
  2099. comma = true;
  2100. var arg = arguments[i];
  2101. var note = arguments[i+1];
  2102. if (typeof(arg) == "number") {
  2103. ret += "<span title='"+note+"'>"+arg+"</span>";
  2104. } else {
  2105. ret += "<span>/* "+note+" */</span><br>\n";
  2106. ret += arg.pp();
  2107. }
  2108. }
  2109. ret += "&gt;";
  2110. }
  2111. ret += "</div>\n";
  2112.  
  2113. return ret;
  2114. }
  2115.  
  2116. SameValue(a, b) {
  2117. // console.log("SAMEVALUE");
  2118. // console.log(a);
  2119. // console.log(b);
  2120. // console.log(this.DescribeValue(a));
  2121. // console.log(this.DescribeValue(b));
  2122. return a == b || this.DescribeValue(a) == this.DescribeValue(b);
  2123. }
  2124.  
  2125. pp() {
  2126. var tmp = [this.constructor.name.replace("Class", ""), this.comment];
  2127. var l = this.argdefs.length;
  2128. if (pp_is_url && !pp_is_verbose) {
  2129. // Drop default arguments
  2130. while (l > 0 && this.argdefs[l-1].default_value != undefined &&
  2131. this.SameValue(this[this.argdefs[l-1].name], this.argdefs[l-1].default_value)) l--;
  2132. }
  2133. for (var i = 0; i < l; i++) {
  2134. tmp.push(this[this.argdefs[i].name]);
  2135. tmp.push(this.argdefs[i].comment);
  2136. }
  2137. return this.PP.apply(this, tmp);
  2138. }
  2139. getType() { return "COLOR"; }
  2140.  
  2141. run(blade) {
  2142. for (var i = 0; i < this.argdefs.length; i++) {
  2143. var arg = this[this.argdefs[i].name];
  2144. if (typeof(arg) == "object") arg.run(blade);
  2145. }
  2146. }
  2147.  
  2148. isEffect() {
  2149. for (var i = 0; i < this.argdefs.length; i++) {
  2150. if (this.argdefs[i].type == "EFFECT") return true;
  2151. if (this.argdefs[i].type == "LOCKUP_TYPE") return true;
  2152. }
  2153. return false;
  2154. }
  2155.  
  2156. // Doesn't work??
  2157. toString() { return this.constructor.name + "[id = " + this.ID + "]"; }
  2158.  
  2159. set_right_side(right) {
  2160. if (this.argdefs.length != right.argdefs.length) {
  2161. console.log("SET RIGHT SIDE NON-MATCH");
  2162. return;
  2163. }
  2164. this.right_side = right;
  2165. for (var i = 0; i < this.argdefs.length; i++) {
  2166. if (this.argdefs[i].name != right.argdefs[i].name) {
  2167. console.log("SET RIGHT SIDE NON-MATCH");
  2168. return;
  2169. }
  2170.  
  2171. var l_arg = this[this.argdefs[i].name];
  2172. var r_arg = right[this.argdefs[i].name];
  2173. if (typeof(l_arg) == "object" && typeof(r_arg) == "object") {
  2174. l_arg.set_right_side(r_arg);
  2175. }
  2176. }
  2177. }
  2178.  
  2179. get_container() {
  2180. var id = this.ID;
  2181. if (this.right_side) id = this.right_side.ID;
  2182. return FIND("X" + id);
  2183. }
  2184.  
  2185. update_displays() {
  2186. for (var i = 0; i < this.argdefs.length; i++) {
  2187. var arg = this[this.argdefs[i].name];
  2188. if (typeof(arg) == "object") arg.update_displays();
  2189. }
  2190.  
  2191. if (this.IS_RUNNING) {
  2192. var container = this.get_container();
  2193. if (container) {
  2194. if (this.IS_RUNNING()) {
  2195. container.classList.add('running');
  2196. } else {
  2197. container.classList.remove('running');
  2198. }
  2199. }
  2200. }
  2201. }
  2202.  
  2203. argify(state) {
  2204. for (var i = 0; i < this.argdefs.length; i++) {
  2205. var arg = this[this.argdefs[i].name];
  2206. if (typeof(arg) == "object") {
  2207. this[this.argdefs[i].name] = arg.argify(state);
  2208. }
  2209. }
  2210. return this;
  2211. }
  2212. }
  2213.  
  2214.  
  2215. class MACRO extends STYLE {
  2216. SetExpansion(expansion) {
  2217. this.expansion = expansion;
  2218. }
  2219. run(blade) { this.expansion.run(blade); }
  2220. getInteger(led) { return this.expansion.getInteger(led); }
  2221. getColor(A,B,C) { return this.expansion.getColor(A,B,C); }
  2222. getType() { return this.expansion.getType(); }
  2223. isMacro() { return true; }
  2224. isEffect() { return this.expansion.isEffect(); }
  2225. begin() { this.expansion.begin(); }
  2226. done() { return this.expansion.done(); }
  2227. IS_RUNNING() {
  2228. if (this.expansion.IS_RUNNING)
  2229. return this.expansion.IS_RUNNING();
  2230. return false;
  2231. }
  2232. bend(t, len, scale) {
  2233. if (this.expansion.bend)
  2234. return this.expansion.bend(t, len, scale);
  2235. return scale * t / len;
  2236. }
  2237. };
  2238.  
  2239. class INTEGER extends STYLE {
  2240. constructor(v) {
  2241. super();
  2242. this.value = v;
  2243. }
  2244. run(blade) {}
  2245. getInteger(led) { return this.value; }
  2246. valueOf() { return this.value; }
  2247. pp() {
  2248. if (pp_is_url) {
  2249. if (this.super_short_desc) return "$";
  2250. return this.gencomment() + this.value;
  2251. }
  2252. return this.PPshort(this.value, "VALUE");
  2253. }
  2254. getType() { return "INT"; }
  2255. };
  2256.  
  2257. function INT(x) {
  2258. return new INTEGER(x);
  2259. }
  2260.  
  2261. class BINARY extends STYLE {
  2262. constructor(v) {
  2263. super();
  2264. this.value = v;
  2265. }
  2266. run(blade) {}
  2267. getInteger(led) { return this.value; }
  2268. valueOf() { return this.value; }
  2269. pp() {
  2270. if (pp_is_url) {
  2271. if (this.super_short_desc) return "$";
  2272. return this.gencomment() + "0b" +this.value.toString(2);
  2273. }
  2274. return this.PPshort("0b" +this.value.toString(2), "VALUE");
  2275. }
  2276. getType() { return "INT"; }
  2277. };
  2278.  
  2279.  
  2280. function AddEnum(enum_type, name, value) {
  2281. if (value == undefined) {
  2282. value = enum_type.last_value + 1;
  2283. }
  2284. enum_type.last_value = value;
  2285. enum_type.value_to_name[value] = name;
  2286. window[name] = value;
  2287. AddIdentifier(name, function() { return new enum_type(value); });
  2288. console.log(" ENUM " + name + " = " + value);
  2289. }
  2290.  
  2291. class EnumBuilder {
  2292. constructor(name, prefix) {
  2293. this.name = name;
  2294. this.prefix = prefix ? prefix : "";
  2295. this.last_value = -1
  2296. this.value_to_name = {};
  2297. }
  2298. addValue(name, value) {
  2299. if (value == undefined) {
  2300. value = this.last_value + 1;
  2301. }
  2302. this.last_value = value;
  2303. this.value_to_name[value] = name;
  2304. window[name] = value;
  2305. console.log(" ENUM " + name + " = " + value);
  2306. }
  2307. addToTab(tab, common_prefix) {
  2308. if (!common_prefix) {
  2309. common_prefix = "florb";
  2310. }
  2311. var v = Object.keys(this.value_to_name);
  2312. for (var i = 0; i < v.length; i++) {
  2313. var V = parseInt(v[i]);
  2314. var N = this.value_to_name[V];
  2315. var label = N.replace(common_prefix, "");
  2316. AddTabContent(tab, mkbutton2(label, this.prefix+N));
  2317. }
  2318. }
  2319. build() {
  2320. class ENUMClass extends INTEGER {
  2321. pp() {
  2322. if (pp_is_url) {
  2323. if (this.super_short_desc) return "$";
  2324. } else if (0) {
  2325. var ret = "<select>";
  2326. var v = Object.keys(this.constructor.value_to_name);
  2327. for (var i = 0; i < v.length; i++) {
  2328. var V = parseInt(v[i]);
  2329. var N = this.constructor.value_to_name[V];
  2330. ret += "<option value="+V;
  2331. if (this.value == V) ret+=" selected";
  2332. ret += ">"+N+"</option>";
  2333. }
  2334. ret += "</select>";
  2335. return ret;
  2336. }
  2337.  
  2338.  
  2339. var ret = this.gencomment() + this.value;
  2340. if (this.constructor.value_to_name[this.value]) {
  2341. ret = this.constructor.prefix + this.constructor.value_to_name[this.value];
  2342. }
  2343. return this.PPshort(ret, this.getType());
  2344. }
  2345. getType() { return this.constructor.NAME; }
  2346. };
  2347. ENUMClass.value_to_name = this.value_to_name;
  2348. ENUMClass.NAME = this.name;
  2349. ENUMClass.prefix = this.prefix
  2350.  
  2351. function ENUM(value) { return new ENUMClass(value); }
  2352. window[this.name] = ENUM;
  2353.  
  2354. var v = Object.keys(this.value_to_name);
  2355. for (var i = 0; i < v.length; i++) {
  2356. var V = parseInt(v[i]);
  2357. var N = this.value_to_name[V];
  2358. AddIdentifier(this.prefix + N, ENUM.bind(null, V));
  2359. }
  2360. }
  2361. }
  2362.  
  2363. EFFECT_ENUM_BUILDER = new EnumBuilder("EFFECT");
  2364. EFFECT_ENUM_BUILDER.addValue("EFFECT_NONE", 0);
  2365. EFFECT_ENUM_BUILDER.addValue("EFFECT_CLASH");
  2366. EFFECT_ENUM_BUILDER.addValue("EFFECT_BLAST");
  2367. EFFECT_ENUM_BUILDER.addValue("EFFECT_FORCE");
  2368. EFFECT_ENUM_BUILDER.addValue("EFFECT_STAB");
  2369. EFFECT_ENUM_BUILDER.addValue("EFFECT_BOOT");
  2370. EFFECT_ENUM_BUILDER.addValue("EFFECT_LOCKUP_BEGIN");
  2371. EFFECT_ENUM_BUILDER.addValue("EFFECT_LOCKUP_END");
  2372. EFFECT_ENUM_BUILDER.addValue("EFFECT_DRAG_BEGIN");
  2373. EFFECT_ENUM_BUILDER.addValue("EFFECT_DRAG_END");
  2374. EFFECT_ENUM_BUILDER.addValue("EFFECT_PREON");
  2375. EFFECT_ENUM_BUILDER.addValue("EFFECT_POSTOFF");
  2376. EFFECT_ENUM_BUILDER.addValue("EFFECT_IGNITION");
  2377. EFFECT_ENUM_BUILDER.addValue("EFFECT_RETRACTION");
  2378. EFFECT_ENUM_BUILDER.addValue("EFFECT_CHANGE");
  2379. EFFECT_ENUM_BUILDER.addValue("EFFECT_NEWFONT");
  2380. EFFECT_ENUM_BUILDER.addValue("EFFECT_LOW_BATTERY");
  2381. EFFECT_ENUM_BUILDER.addValue("EFFECT_POWERSAVE");
  2382. EFFECT_ENUM_BUILDER.addValue("EFFECT_BATTERY_LEVEL");
  2383. EFFECT_ENUM_BUILDER.addValue("EFFECT_VOLUME_LEVEL");
  2384. EFFECT_ENUM_BUILDER.addValue("EFFECT_ON");
  2385. EFFECT_ENUM_BUILDER.addValue("EFFECT_FAST_ON");
  2386. EFFECT_ENUM_BUILDER.addValue("EFFECT_QUOTE");
  2387. EFFECT_ENUM_BUILDER.addValue("EFFECT_SECONDARY_IGNITION");
  2388. EFFECT_ENUM_BUILDER.addValue("EFFECT_SECONDARY_RETRACTION");
  2389. EFFECT_ENUM_BUILDER.addValue("EFFECT_OFF");
  2390. EFFECT_ENUM_BUILDER.addValue("EFFECT_FAST_OFF");
  2391. EFFECT_ENUM_BUILDER.addValue("EFFECT_OFF_CLASH");
  2392. EFFECT_ENUM_BUILDER.addValue("EFFECT_NEXT_QUOTE");
  2393. EFFECT_ENUM_BUILDER.addValue("EFFECT_INTERACTIVE_PREON");
  2394. EFFECT_ENUM_BUILDER.addValue("EFFECT_INTERACTIVE_BLAST");
  2395. EFFECT_ENUM_BUILDER.addValue("EFFECT_TRACK");
  2396. EFFECT_ENUM_BUILDER.addValue("EFFECT_BEGIN_BATTLE_MODE");
  2397. EFFECT_ENUM_BUILDER.addValue("EFFECT_END_BATTLE_MODE");
  2398. EFFECT_ENUM_BUILDER.addValue("EFFECT_BEGIN_AUTO_BLAST");
  2399. EFFECT_ENUM_BUILDER.addValue("EFFECT_END_AUTO_BLAST");
  2400. EFFECT_ENUM_BUILDER.addValue("EFFECT_ALT_SOUND");
  2401. EFFECT_ENUM_BUILDER.addValue("EFFECT_TRANSITION_SOUND");
  2402. EFFECT_ENUM_BUILDER.addValue("EFFECT_SOUND_LOOP");
  2403. EFFECT_ENUM_BUILDER.addValue("EFFECT_STUN");
  2404. EFFECT_ENUM_BUILDER.addValue("EFFECT_FIRE");
  2405. EFFECT_ENUM_BUILDER.addValue("EFFECT_CLIP_IN");
  2406. EFFECT_ENUM_BUILDER.addValue("EFFECT_CLIP_OUT");
  2407. EFFECT_ENUM_BUILDER.addValue("EFFECT_RELOAD");
  2408. EFFECT_ENUM_BUILDER.addValue("EFFECT_MODE");
  2409. EFFECT_ENUM_BUILDER.addValue("EFFECT_RANGE");
  2410. EFFECT_ENUM_BUILDER.addValue("EFFECT_EMPTY");
  2411. EFFECT_ENUM_BUILDER.addValue("EFFECT_FULL");
  2412. EFFECT_ENUM_BUILDER.addValue("EFFECT_JAM");
  2413. EFFECT_ENUM_BUILDER.addValue("EFFECT_UNJAM");
  2414. EFFECT_ENUM_BUILDER.addValue("EFFECT_PLI_ON");
  2415. EFFECT_ENUM_BUILDER.addValue("EFFECT_PLI_OFF");
  2416. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_START");
  2417. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_ACTION1");
  2418. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_ACTION2");
  2419. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_CHOICE");
  2420. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_RESPONSE1");
  2421. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_RESPONSE2");
  2422. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_RESULT1");
  2423. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_RESULT2");
  2424. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_WIN");
  2425. EFFECT_ENUM_BUILDER.addValue("EFFECT_GAME_LOSE");
  2426. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER1");
  2427. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER2");
  2428. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER3");
  2429. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER4");
  2430. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER5");
  2431. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER6");
  2432. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER7");
  2433. EFFECT_ENUM_BUILDER.addValue("EFFECT_USER8");
  2434. EFFECT_ENUM_BUILDER.addValue("EFFECT_SD_CARD_NOT_FOUND");
  2435. EFFECT_ENUM_BUILDER.addValue("EFFECT_ERROR_IN_FONT_DIRECTORY");
  2436. EFFECT_ENUM_BUILDER.addValue("EFFECT_ERROR_IN_BLADE_ARRAY");
  2437. EFFECT_ENUM_BUILDER.addValue("EFFECT_FONT_DIRECTORY_NOT_FOUND");
  2438. EFFECT_ENUM_BUILDER.build();
  2439.  
  2440. LOCKUP_ENUM_BUILDER = new EnumBuilder("LOCKUP_TYPE", "SaberBase::");
  2441. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_NONE", 0);
  2442. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_NORMAL");
  2443. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_DRAG");
  2444. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_ARMED");
  2445. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_AUTOFIRE");
  2446. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_MELT");
  2447. LOCKUP_ENUM_BUILDER.addValue("LOCKUP_LIGHTNING_BLOCK");
  2448. LOCKUP_ENUM_BUILDER.build();
  2449.  
  2450. ArgumentName_ENUM_BUILDER = new EnumBuilder("ArgumentName");
  2451. ArgumentName_ENUM_BUILDER.addValue("BASE_COLOR_ARG", 1);
  2452. ArgumentName_ENUM_BUILDER.addValue("ALT_COLOR_ARG", 2);
  2453. ArgumentName_ENUM_BUILDER.addValue("STYLE_OPTION_ARG", 3);
  2454. ArgumentName_ENUM_BUILDER.addValue("IGNITION_OPTION_ARG", 4);
  2455. ArgumentName_ENUM_BUILDER.addValue("IGNITION_TIME_ARG", 5);
  2456. ArgumentName_ENUM_BUILDER.addValue("IGNITION_DELAY_ARG", 6);
  2457. ArgumentName_ENUM_BUILDER.addValue("IGNITION_COLOR_ARG", 7);
  2458. ArgumentName_ENUM_BUILDER.addValue("IGNITION_POWER_UP_ARG", 8);
  2459. ArgumentName_ENUM_BUILDER.addValue("BLAST_COLOR_ARG", 9);
  2460. ArgumentName_ENUM_BUILDER.addValue("CLASH_COLOR_ARG", 10);
  2461. ArgumentName_ENUM_BUILDER.addValue("LOCKUP_COLOR_ARG", 11);
  2462. ArgumentName_ENUM_BUILDER.addValue("LOCKUP_POSITION_ARG", 12);
  2463. ArgumentName_ENUM_BUILDER.addValue("DRAG_COLOR_ARG", 13);
  2464. ArgumentName_ENUM_BUILDER.addValue("DRAG_SIZE_ARG", 14);
  2465. ArgumentName_ENUM_BUILDER.addValue("LB_COLOR_ARG", 15);
  2466. ArgumentName_ENUM_BUILDER.addValue("STAB_COLOR_ARG", 16);
  2467. ArgumentName_ENUM_BUILDER.addValue("MELT_SIZE_ARG", 17);
  2468. ArgumentName_ENUM_BUILDER.addValue("SWING_COLOR_ARG", 18);
  2469. ArgumentName_ENUM_BUILDER.addValue("SWING_OPTION_ARG", 19);
  2470. ArgumentName_ENUM_BUILDER.addValue("EMITTER_COLOR_ARG", 20);
  2471. ArgumentName_ENUM_BUILDER.addValue("EMITTER_SIZE_ARG", 21);
  2472. ArgumentName_ENUM_BUILDER.addValue("PREON_COLOR_ARG", 22);
  2473. ArgumentName_ENUM_BUILDER.addValue("PREON_OPTION_ARG", 23);
  2474. ArgumentName_ENUM_BUILDER.addValue("PREON_SIZE_ARG", 24);
  2475. ArgumentName_ENUM_BUILDER.addValue("RETRACTION_OPTION_ARG", 25);
  2476. ArgumentName_ENUM_BUILDER.addValue("RETRACTION_TIME_ARG", 26);
  2477. ArgumentName_ENUM_BUILDER.addValue("RETRACTION_DELAY_ARG", 27);
  2478. ArgumentName_ENUM_BUILDER.addValue("RETRACTION_COLOR_ARG", 28);
  2479. ArgumentName_ENUM_BUILDER.addValue("RETRACTION_COOL_DOWN_ARG", 29);
  2480. ArgumentName_ENUM_BUILDER.addValue("POSTOFF_COLOR_ARG", 30);
  2481. ArgumentName_ENUM_BUILDER.addValue("OFF_COLOR_ARG", 31);
  2482. ArgumentName_ENUM_BUILDER.addValue("OFF_OPTION_ARG", 32);
  2483. ArgumentName_ENUM_BUILDER.addValue("ALT_COLOR2_ARG", 33);
  2484. ArgumentName_ENUM_BUILDER.addValue("ALT_COLOR3_ARG", 34);
  2485. ArgumentName_ENUM_BUILDER.addValue("STYLE_OPTION2_ARG", 35);
  2486. ArgumentName_ENUM_BUILDER.addValue("STYLE_OPTION3_ARG", 36);
  2487. ArgumentName_ENUM_BUILDER.addValue("IGNITION_OPTION2_ARG", 37);
  2488. ArgumentName_ENUM_BUILDER.addValue("RETRACTION_OPTION2_ARG", 38);
  2489. ArgumentName_ENUM_BUILDER.build();
  2490.  
  2491. function effect_to_argument(effect) {
  2492. switch (effect + 0) {
  2493. case EFFECT_CLASH: return CLASH_COLOR_ARG;
  2494. case EFFECT_BLAST: return BLAST_COLOR_ARG;
  2495. case EFFECT_STAB: return STAB_COLOR_ARG;
  2496. case EFFECT_PREON: return PREON_COLOR_ARG;
  2497. case EFFECT_POSTOFF: return POSTOFF_COLOR_ARG;
  2498. }
  2499. return undefined;
  2500. }
  2501.  
  2502. function lockup_to_argument(effect) {
  2503. switch (effect + 0) {
  2504. case LOCKUP_NORMAL: return LOCKUP_COLOR_ARG;
  2505. case LOCKUP_DRAG: return DRAG_COLOR_ARG;
  2506. case LOCKUP_LIGHTNING_BLOCK: return LB_COLOR_ARG;
  2507. }
  2508. return undefined;
  2509. }
  2510.  
  2511. class FUNCTION extends STYLE {
  2512. getType() { return "FUNCTION"; }
  2513. };
  2514.  
  2515. class TIME_FUNCTION extends FUNCTION {
  2516. getType() { return "TIME_FUNCTION"; }
  2517. };
  2518.  
  2519. class TRANSITION extends STYLE {
  2520. getType() { return "TRANSITION"; }
  2521.  
  2522. IS_RUNNING() { return !this.done(); }
  2523. };
  2524.  
  2525. class CONFIG extends STYLE {
  2526. PP(name, note) {
  2527. if (pp_is_url) {
  2528. return this.PPURL.apply(this, arguments);
  2529. }
  2530. var id = this.get_id();
  2531. var ret = "";
  2532. ret += "<span title='"+ note +"'>" + name + "</span>&lt;\n";
  2533. ret += "<div>\n";
  2534. var comma = false;
  2535. for (var i = 2; i < arguments.length; i += 2) {
  2536. if (comma) ret += ",<br>";
  2537. comma = true;
  2538. var arg = arguments[i];
  2539. var note = arguments[i+1];
  2540. if (typeof(arg) == "number") {
  2541. arg = "" + arg;
  2542. } else {
  2543. arg = arg.pp();
  2544. }
  2545. var comment = arg.COMMENT;
  2546. if (arg.indexOf("<br>") == -1 && arg.indexOf("<div") == -1 && !comment) {
  2547. ret += arg+" /* "+note+" */\n";
  2548. } else {
  2549. ret += "/* "+note+" */<br>\n";
  2550. if (comment) {
  2551. ret += "/* "+comment+" */<br>\n";
  2552. }
  2553. ret += arg;
  2554. }
  2555. }
  2556. ret += "</div>&gt;\n";
  2557.  
  2558. return ret;
  2559. }
  2560. getType() { return "CONFIG"; }
  2561. };
  2562.  
  2563. function FixColor(c) {
  2564. return min(65535, Math.floor(Math.pow(parseInt(c, 16) / 255.0, 2.2) * 65536));
  2565. }
  2566.  
  2567. function hex2(N) {
  2568. var ret = N.toString(16);
  2569. if (ret.length < 2) ret = "0" + ret;
  2570. return ret;
  2571. }
  2572.  
  2573. function UnFixColor(c) {
  2574. return hex2(min(255, Math.floor(Math.pow(parseInt(c) / 65535.0, 1.0/2.2) * 255)));
  2575. }
  2576.  
  2577. function ClickColor() {
  2578. var color_button = FIND("COLOR");
  2579. color_button.addEventListener("input", ClickColor, false);
  2580. var R = FixColor(color_button.value.substr(1,2));
  2581. var G = FixColor(color_button.value.substr(3,2));
  2582. var B = FixColor(color_button.value.substr(5,2));
  2583. SetTo("Rgb16<"+R+","+G+","+B+">");
  2584. }
  2585.  
  2586. var effect_links = [];
  2587. var layer_links = [];
  2588. var effect_type_links = []
  2589. var template_links = [];
  2590. var function_links = []
  2591. var transition_links = [];
  2592.  
  2593. var all_colors = {};
  2594. var colorNames = {};
  2595. var colorData = [];
  2596.  
  2597. class RgbClass extends STYLE {
  2598. constructor(r,g,b,a) {
  2599. super();
  2600. this.r = IntArg(r)/255.0;
  2601. this.g = IntArg(g)/255.0;
  2602. this.b = IntArg(b)/255.0;
  2603. if (this.r < 0) throw "Red is negative";
  2604. if (this.g < 0) throw "Blue is negative";
  2605. if (this.b < 0) throw "Green is negative";
  2606. if (this.r > 1.0) throw "Red too big.";
  2607. if (this.g > 1.0) throw "Green too big.";
  2608. if (this.b > 1.0) throw "Blue too big.";
  2609. if (a == undefined) {
  2610. this.a = 1.0;
  2611. this.name = colorNames[r+","+g+","+b]
  2612. } else {
  2613. this.a = a;
  2614. }
  2615. }
  2616. run(blade) {}
  2617. getColor(led) {
  2618. return this;
  2619. }
  2620. pp() {
  2621. if (this.name) return this.PPshort(this.name,"Color");
  2622. return this.PPshort("Rgb", "RGB Color",
  2623. Math.round(this.r*255), "Red component",
  2624. Math.round(this.g*255), "Green component",
  2625. Math.round(this.b*255), "Blue component");
  2626. }
  2627. mix(other, blend) {
  2628. var ret = new RgbClass(0,0,0);
  2629. ret.r = other.r * blend + this.r * (1.0 - blend);
  2630. ret.g = other.g * blend + this.g * (1.0 - blend);
  2631. ret.b = other.b * blend + this.b * (1.0 - blend);
  2632. ret.a = other.a * blend + this.a * (1.0 - blend);
  2633. return ret;
  2634. }
  2635. multiply(v) {
  2636. var ret = new RgbClass(0,0,0);
  2637. ret.r = this.r * v;
  2638. ret.g = this.g * v;
  2639. ret.b = this.b * v;
  2640. ret.a = this.a * v;
  2641. return ret;
  2642. }
  2643. paintOver(other) {
  2644. var ret = new RgbClass(0,0,0);
  2645. ret.r = this.r * (1.0 - other.a) + other.r;
  2646. ret.g = this.g * (1.0 - other.a) + other.g;
  2647. ret.b = this.b * (1.0 - other.a) + other.b;
  2648. ret.a = this.a * (1.0 - other.a) + other.a;
  2649. return ret;
  2650. }
  2651.  
  2652. // angle = 0 - 98304 (32768 * 3) (non-inclusive)
  2653. rotate(angle) {
  2654. var H;
  2655. if (angle == 0) return this;
  2656. var MAX = max(this.r, this.g, this.b);
  2657. var MIN = min(this.r, this.g, this.b);
  2658. var C = MAX - MIN;
  2659. if (C == 0) return this; // Can't rotate something without color.
  2660. // Note 16384 = 60 degrees.
  2661. if (this.r == MAX) {
  2662. // r is biggest
  2663. H = (this.g - this.b) / C;
  2664. } else if (this.g == MAX) {
  2665. // g is biggest
  2666. H = (this.b - this.r) / C + 2;
  2667. } else {
  2668. // b is biggest
  2669. H = (this.r - this.g) / C + 4;
  2670. }
  2671. H += angle / 16384.0;
  2672. return new RgbClass(f(5+H, C, MAX), f(3+H, C, MAX), f(1+H, C, MAX));
  2673. }
  2674.  
  2675. argify(state) {
  2676. if (state.color_argument) {
  2677. ret = RgbArg_(ArgumentName(state.color_argument), this);
  2678. state.color_argument = false;
  2679. return ret;
  2680. } else {
  2681. return this;
  2682. }
  2683. }
  2684. };
  2685.  
  2686. function f(n, C, MAX) {
  2687. var k = n % 6;
  2688. var x = MAX - C * clamp(min(k, 4 - k), 0, 1);
  2689. return x*255.0;
  2690. }
  2691.  
  2692. function Rgb(r,g,b) {
  2693. return new RgbClass(r,g,b);
  2694. }
  2695.  
  2696. function Transparent(r,g,b) {
  2697. var ret = Rgb(0,0,0)
  2698. ret.a = 0.0;
  2699. return ret;
  2700. }
  2701.  
  2702. class Rgb16Class extends RgbClass {
  2703. constructor(r,g,b) {
  2704. super(r * 255.0/65535.0,g * 255.0/65535.0,b * 255.0/65535.0);
  2705. // this.name = colorNames[r+","+g+","+b]
  2706. // this.name
  2707. }
  2708. run(blade) {}
  2709. getColor(led) {
  2710. return this;
  2711. }
  2712. pp() {
  2713. if (this.name) return this.PPshort(this.name,"Color");
  2714. return this.PPshort("Rgb16", "RGB Color",
  2715. Math.round(this.r*65535), "Red component",
  2716. Math.round(this.g*65535), "Green component",
  2717. Math.round(this.b*65535), "Blue component");
  2718. }
  2719. };
  2720.  
  2721. function RgbF(r,g,b) {
  2722. return new Rgb16Class(r * 65535,g * 65535,b * 65535);
  2723. }
  2724.  
  2725.  
  2726. function Rgb16(r,g,b) {
  2727. return new Rgb16Class(r,g,b);
  2728. }
  2729.  
  2730. class AlphaLClass extends STYLE {
  2731. isEffect() { return this.ALPHA.isEffect(); }
  2732. constructor(COLOR, ALPHA) {
  2733. super("Makes transparent color", Array.from(arguments));
  2734. this.add_arg("COLOR", "COLOR", "COLOR");
  2735. this.add_arg("ALPHA", "FUNCTION", "Alpha function");
  2736. }
  2737. getColor(led) {
  2738. var ret = this.COLOR.getColor(led);
  2739. if (ret == 0) return Transparent(0,0,0);
  2740. return ret.multiply(this.ALPHA.getInteger(led)/32768.0)
  2741. }
  2742. IS_RUNNING() {
  2743. if (this.ALPHA.IS_RUNNING)
  2744. return this.ALPHA.IS_RUNNING();
  2745. if (this.COLOR.IS_RUNNING)
  2746. return this.COLOR.IS_RUNNING();
  2747. return false;
  2748. }
  2749. };
  2750.  
  2751. function AlphaL(COLOR, ALPHA) {
  2752. return new AlphaLClass(COLOR, ALPHA);
  2753. }
  2754.  
  2755. class AlphaMixLClass extends MACRO {
  2756. constructor(ARGS) {
  2757. super("Mix and alpha", ARGS);
  2758. this.COLORS = Array.from(ARGS).slice(1);
  2759. this.add_arg("F", "FUNCTION", "0=first color, 32768=last color");
  2760. for (var i = 1; i < this.COLORS.length + 1; i++)
  2761. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  2762. this.SetExpansion(AlphaL(new MixClass(ARGS), this.F.DOCOPY()));
  2763. }
  2764. }
  2765.  
  2766. function AlphaMixL(F, C1, C2) {
  2767. return new AlphaMixLClass(Array.from(arguments));
  2768. };
  2769.  
  2770. function ReplaceNode(old_node, new_node) {
  2771. FocusOnLow(old_node.get_id());
  2772. pp_is_url++;
  2773. FIND("style").value = new_node.pp();
  2774. pp_is_url--;
  2775. Run();
  2776. }
  2777.  
  2778. function DuplicateLayer(id, arg, event) {
  2779. event.stopPropagation();
  2780. console.log("DuplicateLayer: "+id +", "+arg);
  2781. arg-=2;
  2782. var layer = style_ids[id];
  2783. var new_layer = new LayersClass( [layer.BASE].concat(layer.LAYERS.slice(0, arg), [layer.LAYERS[arg].DOCOPY()], layer.LAYERS.slice(arg)) );
  2784. ReplaceNode(layer, new_layer);
  2785. }
  2786.  
  2787. function RemoveLayer(id, arg, event) {
  2788. event.stopPropagation();
  2789. console.log("RemoveLayer: "+id +", "+arg);
  2790. arg-=2;
  2791. var layer = style_ids[id];
  2792. var new_layer = new LayersClass( [layer.BASE].concat(layer.LAYERS.slice(0, arg), layer.LAYERS.slice(arg+1)) );
  2793. ReplaceNode(layer, new_layer);
  2794. }
  2795.  
  2796. function DownLayer(id, arg, event) {
  2797. event.stopPropagation();
  2798. console.log("DownLayer: "+id +", "+arg);
  2799. arg-=2;
  2800. var layer = style_ids[id];
  2801. var new_layer = new LayersClass( [layer.BASE].concat(layer.LAYERS.slice(0, arg),
  2802. [layer.LAYERS[arg+1], layer.LAYERS[arg]],
  2803. layer.LAYERS.slice(arg+2)) );
  2804. ReplaceNode(layer, new_layer);
  2805. }
  2806.  
  2807. function UpLayer(id, arg, event) {
  2808. console.log("UpLayer: "+id +", "+arg);
  2809. DownLayer(id, arg-1, event);
  2810. }
  2811.  
  2812. class LayersClass extends STYLE {
  2813. Indent(text) {
  2814. if (text.substr(0, 2) == '/*') {
  2815. var tmp = text.split('*/');
  2816. if (tmp[1][0] != '\n') tmp[1] = '\n' + tmp[1].trimStart();
  2817. text = tmp.join('*/');
  2818. }
  2819. return "\n " + text.split("\n").join("\n ");
  2820. }
  2821. extraButtons(arg) {
  2822. if (arg == 1) return "";
  2823. var id = this.get_id();
  2824. var ret = "<button class='extra-buttons' title='Duplicate Layer' onclick='DuplicateLayer("+id+","+arg+",event)'>+</button>";
  2825. ret += "<button class='extra-buttons' title='Remove Layer' onclick='RemoveLayer("+id+","+arg+",event)'>X</button>";
  2826. if (arg > 2) ret += "<button class='extra-buttons' title='Move Layer Up' onclick='UpLayer("+id+","+arg+",event)'>&#5169;</button>";
  2827. if (arg <= this.LAYERS.length) ret += "<button class='extra-buttons' title='Move Layer Down'onclick='DownLayer("+id+","+arg+",event)'>&#5167;</button>";
  2828. return ret;
  2829. }
  2830. constructor(ARGS) {
  2831. super("Mix alpha-blended layers", ARGS);
  2832. this.LAYERS = Array.from(ARGS).slice(1);
  2833. this.add_arg("BASE", "COLOR", "Base layer");
  2834. for (var i = 1; i < this.LAYERS.length + 1; i++)
  2835. this.add_arg("LAYER" + i, "COLOR", "Layer " + i);
  2836. }
  2837. getColor(led) {
  2838. var ret = this.BASE.getColor(led);
  2839. for (var i = 0; i < this.LAYERS.length; i++) {
  2840. ret = ret.paintOver(this.LAYERS[i].getColor(led));
  2841. }
  2842. return ret;
  2843. }
  2844. argify(state) {
  2845. this.BASE = this.BASE.argify(state);
  2846. state.color_argument = false;
  2847. var ret = super.argify(state);
  2848. state.color_argument = false;
  2849. return ret;
  2850. }
  2851. }
  2852.  
  2853. function Layers(BASE, Layer1, Layer2) {
  2854. return new LayersClass(Array.from(arguments));
  2855. }
  2856.  
  2857.  
  2858. function enc(s) {
  2859. return s.replace(/</g, "&lt;").replace(/>/g, "&gt;");
  2860. }
  2861. function encstr(s) {
  2862. return s.replace("\n", "\\n");
  2863. }
  2864.  
  2865. function mkbutton2(name, val) {
  2866. return "<input type=button class='btn' onclick='SetToAndFormat(\""+val+"\", event)' value='"+enc(name)+"'>\n";
  2867. }
  2868.  
  2869. function mkbutton(name) {
  2870. return mkbutton2(name, name);
  2871. }
  2872.  
  2873.  
  2874. function AddTemplate(name) {
  2875. var val = name;
  2876. if (name.length > 40) {
  2877. name = name.slice(0,40) + '...';
  2878. }
  2879. template_links.push( mkbutton2(name, val) );
  2880. }
  2881. function AddEffect(val) {
  2882. var name = val.split("<")[0];
  2883. effect_links.push( mkbutton2(name, val) );
  2884. }
  2885. function AddLayer(val) {
  2886. var name = val.split("<")[0];
  2887. layer_links.push( mkbutton2(name, val) );
  2888. }
  2889. function AddFunction(val) {
  2890. var name = val.split("<")[0];
  2891. function_links.push( mkbutton2(name, val) );
  2892. }
  2893. function AddTransition(val) {
  2894. var name = val.split("<")[0];
  2895. transition_links.push( mkbutton2(name, val) );
  2896. }
  2897. function AddEffectWL(val) {
  2898. AddEffect(val);
  2899. val=val.slice(0, val.length-1);
  2900. var tmp1 = val.split("<");
  2901. var tmp2 = val.split(",");
  2902. AddLayer(tmp1[0] + "L<" + tmp2.slice(1).join(",") + ">")
  2903. }
  2904. function AddEffectWLF(val) {
  2905. AddEffect(val);
  2906. val=val.slice(0, val.length-1);
  2907. var tmp1 = val.split("<");
  2908. var tmp2 = val.split(",");
  2909. AddLayer(tmp1[0] + "L<" + tmp2.slice(1).join(",") + ">")
  2910. AddFunction(tmp1[0] + "F<" + tmp2.slice(2).join(",") + ">")
  2911. }
  2912.  
  2913. var history_html = "";
  2914. function AddHistory(name, type) {
  2915. var label = name;
  2916. if (label.length > 80) label = label.slice(0,78) + "...";
  2917. name = name.split("\n").join(" ").split(" ").join(" ").split(" ").join(" ").split("< ").join("<");
  2918. var btn = "<input type=button class='btn' onclick='SetToAndFormat(\""+name+"\", event)' value='"+enc(label)+"'>\n";
  2919. var tag = "<span class=MAGIC_CLASS_"+type+">" + btn + "</span>\n";
  2920. history_html = tag + history_html.replace(tag, "");
  2921. FIND("history_tabcontent").innerHTML = history_html;
  2922. }
  2923.  
  2924. function mapcolor(x) {
  2925. x /= 255.0;
  2926. x = Math.pow(x, 1.0/2.2);
  2927. return Math.round(x * 255);
  2928. }
  2929.  
  2930.  
  2931. //sort color by hue
  2932. function rgbToHsl(r, g, b) {
  2933. r /= 255, g /= 255, b /= 255;
  2934.  
  2935. var max = Math.max(r, g, b);
  2936. var min = Math.min(r, g, b);
  2937. var h, s, l = (max + min) / 2;
  2938.  
  2939. if (max == min) {
  2940. h = s = 0; // achromatic
  2941. } else {
  2942. var d = max - min;
  2943. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  2944.  
  2945. switch (max) {
  2946. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  2947. case g: h = (b - r) / d + 2; break;
  2948. case b: h = (r - g) / d + 4; break;
  2949. }
  2950.  
  2951. h /= 6;
  2952. }
  2953.  
  2954. return [ h, s, l ];
  2955. }
  2956.  
  2957. function mkcolorbutton(name, r, g, b) {
  2958. r = mapcolor(r);
  2959. g = mapcolor(g);
  2960. b = mapcolor(b);
  2961. var hsl = rgbToHsl(r, g, b);
  2962. console.log("mkcolorbutton:name="+name+" rgb="+r+","+g+","+b+" hsl="+hsl[0]+","+hsl[1]+","+hsl[2]+" ");
  2963. var sortString;
  2964. if (hsl[1] == 0.0) {
  2965. sortString = "C:"+hsl[2];
  2966. } else if (hsl[1] < 0.3 || hsl[2] > 0.8 || hsl[2] < 0.2) {
  2967. sortString = "B:"+hsl[0];
  2968. } else {
  2969. sortString = "A:"+hsl[0];
  2970. }
  2971. var bgColor = "rgb("+r+","+g+","+b+")";
  2972. if (r == 0 && g == 0 && b == 0) bgColor = "black"
  2973. var textColor = (name === "Black" || name === "Blue") ? "white" : "black";
  2974. return "<input srt='"+sortString+"' type=button style='border: 1px solid black;padding:8px;color:"+textColor+";background:"+bgColor+"' class=btn onclick='SetTo(\""+name+"\")' value='"+enc(name)+"'>\n";
  2975. }
  2976.  
  2977. function AddColor(name, r, g, b) {
  2978. colorNames[r+","+g+","+b] = name;
  2979. colorData.push(mkcolorbutton(name, r, g, b));
  2980. all_colors[name] = new RgbClass(r, g, b);
  2981. }
  2982.  
  2983.  
  2984. AddTemplate("InOutHelper<SimpleClash<Lockup<Blast<Blue,White>,AudioFlicker<Blue,White>>,White>, 300, 800>");
  2985. // AddTemplate("StyleFirePtr<Red, Yellow>");
  2986. AddTemplate("InOutHelper<EasyBlade<OnSpark<Green>, White>, 300, 800>>");
  2987. AddTemplate("InOutHelper<EasyBlade<Sparkle<Blue>, White>, 300, 800>>");
  2988. AddTemplate("IgnitionDelay<500, InOutHelper<EasyBlade<OnSpark<Green>, White>, 300, 800>>>");
  2989. AddTemplate("RetractionDelay<500, InOutHelper<EasyBlade<OnSpark<Green>, White>, 300, 800>>>");
  2990. AddTemplate("StyleNormalPtr<AudioFlicker<Yellow, White>, Blue, 300, 800>");
  2991. AddTemplate("InOutSparkTip<EasyBlade<Magenta, White>, 300, 800>>");
  2992. AddTemplate("StyleNormalPtr<Gradient<Red, Blue>, Gradient<Cyan, Yellow>, 300, 800>");
  2993. AddTemplate("StyleNormalPtr<Pulsing<Red, Rgb<50,0,0>, 5000>, White, 300, 800, Red>");
  2994. AddTemplate("StyleRainbowPtr<300, 800>");
  2995. AddTemplate("StyleStrobePtr<White, Rainbow, 15, 300, 800>");
  2996. AddTemplate("StyleFirePtr<Red, Yellow>");
  2997. AddTemplate("Layers<Red, ResponsiveLockupL<White, TrInstant, TrFade<100>, Int<26000>, Int<6000>>,ResponsiveLightningBlockL<White, TrInstant, TrInstant>,ResponsiveMeltL<Mix<TwistAngle<>,Red,Yellow>, TrWipeIn<600>, TrWipe<600>, Int<4000>, Int<10000>>,ResponsiveDragL<White, TrInstant, TrInstant, Int<2000>, Int<10000>>,ResponsiveClashL<White, TrInstant, TrFade<200>, Int<26000>, Int<6000>>,ResponsiveBlastL<White, Int<400>, Int<100>, Int<400>, Int<28000>, Int<8000>>,ResponsiveBlastWaveL<White, Int<400>, Int<100>, Int<400>, Int<28000>, Int<8000>>,ResponsiveBlastFadeL<White, Int<8000>, Int<400>, Int<28000>, Int<8000>>,ResponsiveStabL<White, TrWipeIn<600>, TrWipe<600>, Int<14000>, Int<8000>>,InOutTrL<TrWipe<300>, TrWipeIn<500>>>");
  2998.  
  2999.  
  3000. AddLayer("AlphaL<Red, Int<16000>>");
  3001. AddLayer("AlphaMixL<Bump<Int<16384>,Int<16384>>,Red,Green,Blue>");
  3002. AddEffectWL("AudioFlicker<White, Blue>");
  3003. AddEffectWLF("Blast<Blue, White>");
  3004. AddEffectWL("BlastFadeout<Blue, White>");
  3005. AddEffect("Blinking<Red, Blue, 1000, 500>");
  3006. AddLayer("BlinkingL<Blue, Int<1000>, Int<500>>");
  3007. AddEffect("BrownNoiseFlicker<Green, Magenta, 50>");
  3008. AddLayer("BrownNoiseFlickerL<Magenta, Int<50>>");
  3009. AddEffect("ColorChange<TrInstant, Red, Green, Blue>");
  3010. AddEffect("ColorSelect<Variation, TrInstant, Red, Green, Blue>");
  3011. AddFunction("IntSelect<Variation, 0, 8192,32768>");
  3012. AddEffect("ColorCycle<Blue, 0, 1, Cyan, 100, 3000, 5000>");
  3013. AddEffect("ColorSequence<500, Red, Green, Blue>");
  3014. AddEffect("EffectSequence<EFFECT_CLASH, Red, Green, Blue>");
  3015. AddEffect("Cylon<Red, 5, 20>");
  3016. AddEffect("Gradient<Blue, Green, Yellow, Red>");
  3017. AddEffect("Gradient<Red, Blue, Green>");
  3018. AddEffect("Gradient<Red, Blue>");
  3019. AddEffect("Hue<16384>");
  3020. AddEffectWL("HumpFlicker<Green, Magenta, 50>");
  3021. AddEffect("InOutHelper<White, 300, 800, Black>");
  3022. AddEffect("InOutSparkTip<Red, 1000, 800, White>");
  3023. AddEffect("InOutTr<Green, TrColorCycle<3000>, TrFade<500>>");
  3024. AddEffect("Layers<Green, AlphaL<Red, Int<16000>>>");
  3025. AddEffectWL("LocalizedClash<Red, White>");
  3026. AddEffectWL("Lockup<Green, Red>");
  3027. AddEffectWL("LockupTr<Red, White, TrFade<100>, TrFade<100>, SaberBase::LOCKUP_MELT>");
  3028. AddEffect("Mix<Int<16384>, Red, Blue>");
  3029. AddEffect("OnSpark<Green, White, 200>");
  3030. AddLayer("OnSparkL<White, Int<200>>");
  3031. AddEffectWL("OriginalBlast<Blue, White>");
  3032. AddEffect("Pulsing<Blue, Red, 800>");
  3033. AddLayer("PulsingL<Red, Int<800>>");
  3034. AddEffect("Rainbow");
  3035. AddEffect("Remap<SmoothStep<Sin<Int<10>>, Sin<Int<7>>>, Rainbow>");
  3036. AddEffect("RandomBlink<3000>");
  3037. AddLayer("RandomBlinkL<Int<3000>, Green>");
  3038. AddEffect("RandomFlicker<Yellow, Blue>");
  3039. AddLayer("RandomL<Blue>");
  3040. AddEffectWL("RandomPerLEDFlicker<Green, Magenta>");
  3041. AddEffect("Rgb16<0,0,65536>");
  3042. AddEffect("Rgb<100,100,100>");
  3043. AddEffect("RgbCycle");
  3044. AddEffect("RotateColorsX<Variation,Red>");
  3045. AddEffect("Sequence<Red, Black, 100, 37, 0b0001010100011100, 0b0111000111000101, 0b0100000000000000>");
  3046. AddLayer("SequenceL<Red, 100, 37, 0b0001010100011100, 0b0111000111000101, 0b0100000000000000>");
  3047. AddEffectWL("SimpleClash<Red, White, 40>");
  3048. AddEffect("Sparkle<Blue>");
  3049. AddLayer("SparkleL");
  3050. AddEffect("Stripes<1000, 1000, Cyan, Magenta, Yellow, Blue>");
  3051. AddEffect("Strobe<Black, White, 15, 1>");
  3052. AddLayer("StrobeL<White, Int<15>, Int<1>>");
  3053. AddEffect("StyleFire<Blue, Cyan>");
  3054. AddEffect("MultiTransitionEffect<Blue, White, TrWipe<50>, TrWipe<50>, EFFECT_BLAST>");
  3055. AddEffect("TransitionEffect<Blue,Green,TrFade<500>,TrBoing<500,3>,EFFECT_BLAST>");
  3056. AddEffectWL("TransitionLoop<Blue, TrConcat<TrFade<200>, Red, TrFade<200>>>");
  3057. AddFunction("BendTimePow<1000, 16384>");
  3058. AddFunction("BendTimePowInv<1000, 16384>");
  3059. AddFunction("ReverseTime<1000, 16384>");
  3060.  
  3061.  
  3062. AddEffect("IgnitionDelay<500, InOutHelper<EasyBlade<OnSpark<Green>, White>, 300, 800>>>");
  3063. AddEffect("RetractionDelay<500, InOutHelper<EasyBlade<OnSpark<Green>, White>, 300, 800>>>");
  3064.  
  3065.  
  3066. AddLayer("TransitionEffectL<TrConcat<TrWipe<50>, White, TrWipe<50>>, EFFECT_BLAST>");
  3067. AddLayer("MultiTransitionEffectL<TrConcat<TrWipe<50>, White, TrWipe<50>>, EFFECT_BLAST>");
  3068. AddLayer("TransitionPulseL<TrConcat<TrFade<200>, Red, TrFade<200>>, ThresholdPulseF<Saw<Int<60>>, Int<16384>>>")
  3069.  
  3070. AddTransition("TrBoing<300, 2>");
  3071. AddTransition("TrBlink<1000, 3>");
  3072. AddTransition("TrColorCycle<3000>");
  3073. AddTransition("TrConcat<TrFade<100>, White, TrFade<100>>");
  3074. AddTransition("TrDelay<500>");
  3075. AddTransition("TrFade<300>");
  3076. AddTransition("TrInstant");
  3077. AddTransition("TrJoin<TrFade<500>, TrWipe<500>>");
  3078. AddTransition("TrJoinR<TrFade<500>, TrWipe<500>>");
  3079. AddTransition("TrRandom<TrFade<500>, TrWipe<500>, TrBoing<500, 2>>");
  3080. AddTransition("TrSelect<Variation,TrFade<500>, TrWipe<500>, TrBoing<500, 2>>");
  3081. AddTransition("TrSmoothFade<300>");
  3082. AddTransition("TrWipe<500>");
  3083. AddTransition("TrWipeIn<500>");
  3084. AddTransition("TrCenterWipe<500>");
  3085. AddTransition("TrCenterWipeSpark<WHITE, 500>");
  3086. AddTransition("TrCenterWipeIn<500>");
  3087. AddTransition("TrCenterWipeInSpark<WHITE, 500>");
  3088. AddTransition("TrWaveX<White>");
  3089. AddTransition("TrSparkX<White>");
  3090. AddTransition("TrWipeSparkTip<White, 300>");
  3091. AddTransition("TrWipeInSparkTip<White, 300>");
  3092. AddTransition("TrWipeSparkTipX<White, Int<300>>");
  3093. AddTransition("TrWipeInSparkTipX<White, Int<300>>");
  3094. AddTransition("TrExtend<1000, TrFade<500>>");
  3095. AddTransition("TrLoop<TrFade<500>>");
  3096. AddTransition("TrLoopN<5, TrFade<500>>");
  3097. AddTransition("TrLoopUntil<EffectPulseF<EFFECT_CLASH>, TrConcat<TrFade<500>, Green, TrFade<500>>, TrFade<100>>");
  3098. AddTransition("TrDoEffect<TrFade<100>, EFFECT_BLAST>");
  3099. AddTransition("TrDoEffectAlways<TrFade<100>, EFFECT_BLAST>");
  3100.  
  3101. AddFunction("BatteryLevel");
  3102. AddFunction("VolumeLevel");
  3103. AddFunction("BlinkingF<Int<1000>, Int<500>>");
  3104. AddFunction("BrownNoiseF<Int<50>>");
  3105. AddFunction("HumpFlickerF<50>");
  3106. AddFunction("NoisySoundLevel");
  3107. AddFunction("NoisySoundLevelCompat");
  3108. AddFunction("SmoothSoundLevel");
  3109. AddFunction("SwingSpeed<250>");
  3110. AddFunction("SwingAcceleration<130>");
  3111. AddFunction("ClashImpactF<>");
  3112. AddFunction("Bump<Int<16384>>");
  3113. AddFunction("Ifon<Int<0>, Int<32768>>");
  3114. AddFunction("IgnitionTime<>");
  3115. AddFunction("RetractionTime<>");
  3116. AddFunction("InOutFunc<300, 800>");
  3117. AddFunction("InOutHelperF<InOutFunc<300, 800>>");
  3118. AddFunction("Int<32768>");
  3119. AddFunction("Scale<Sin<Int<10>>,Int<0>,Int<4000>>");
  3120. AddFunction("InvertF<Ifon<Int<0>, Int<32768>>>");
  3121. AddFunction("Sin<Int<10>>");
  3122. AddFunction("Saw<Int<10>>");
  3123. AddFunction("SmoothStep<Sin<Int<10>>, Sin<Int<7>>>");
  3124. AddFunction("Trigger<EFFECT_FORCE, Int<500>, Int<1000>, Int<500>>");
  3125. AddFunction("ChangeSlowly<NoisySoundLevel, Int<50000>>");
  3126. AddFunction("SlowNoise<Int<1000>>");
  3127. AddFunction("IsLessThan<SwingSpeed<250>, Int<100>>");
  3128. AddFunction("IsGreaterThan<SwingSpeed<250>, Int<100>>");
  3129. AddFunction("IsBetween<SwingSpeed<250>, Int<100>, Int<120>>");
  3130. AddFunction("ClampF<RandomPerLEDF, 8000, 12000>");
  3131. AddFunction("LayerFunctions<Bump<Int<0>>, Bump<Int<32768>>>");
  3132. AddFunction("OnSparkF<Int<200>>");
  3133. AddFunction("PulsingF<Int<800>>");
  3134. AddFunction("RandomBlinkF<Int<3000>>");
  3135. AddFunction("RandomF");
  3136. AddFunction("RandomPerLEDF");
  3137. AddFunction("SequenceF<100, 37, 0b0001010100011100, 0b0111000111000101, 0b0100000000000000>");
  3138. AddFunction("SparkleF");
  3139. AddFunction("StrobeF<Int<15>, Int<1>>");
  3140. AddFunction("BlastFadeoutF");
  3141. AddFunction("OriginalBlastF");
  3142. AddFunction("Variation");
  3143. AddFunction("AltF");
  3144. AddFunction("SyncAltToVarianceF");
  3145. AddFunction("TwistAngle<>");
  3146. AddFunction("TwistAcceleration<>");
  3147. AddFunction("BladeAngle<>");
  3148. AddFunction("Sum<RandomPerLEDF, Bump<Int<16384>>>");
  3149. AddFunction("Subtract<RandomPerLEDF, Bump<Int<16384>>>");
  3150. AddFunction("Mult<RandomPerLEDF, Bump<Int<16384>>>");
  3151. AddFunction("Percentage<RandomPerLEDF, 20>");
  3152. AddFunction("Divide<RandomPerLEDF, Int<10>>");
  3153. AddFunction("ModF<Sin<Int<10>>, Int<8192>>");
  3154. AddFunction("HoldPeakF<RandomF, Int<300>, Int<32768>>");
  3155. AddFunction("CenterDistF<>");
  3156. AddFunction("EffectPosition<>");
  3157. AddFunction("TimeSinceEffect<>");
  3158. AddFunction("WavNum<>");
  3159. AddFunction("WavLen<>");
  3160. AddFunction("CircularSectionF<Sin<Int<3>>, Sin<Int<2>>>");
  3161. AddFunction("LinearSectionF<Sin<Int<3>>, Sin<Int<2>>>");
  3162. AddFunction("EffectRandomF<EFFECT_CLASH>");
  3163. AddFunction("EffectPulseF<EFFECT_CLASH>");
  3164. AddFunction("IncrementWithReset<EffectPulseF<EFFECT_CLASH>>");
  3165. AddFunction("IncrementModuloF<EffectPulseF<EFFECT_CLASH>>");
  3166. AddFunction("ThresholdPulseF<Saw<Int<60>>, Int<16384>>");
  3167. AddFunction("IncrementF<Saw<Int<60>>, Int<16384>, Int<32768>, Int<1024>>");
  3168. AddFunction("EffectIncrementF<EFFECT_CLASH, Int<32768>, Int<8192>>");
  3169. AddFunction("MarbleF<Int<-2000>, Int<40000>, Ifon<Int<827680>, Int<0>>, Int<1276800>>");
  3170. AddFunction("LockupPulseF<SaberBase::LOCKUP_NORMAL>");
  3171.  
  3172. AddColor("AliceBlue", 223, 239, 255);
  3173. AddColor("Aqua", 0, 255, 255);
  3174. AddColor("Aquamarine", 55, 255, 169);
  3175. AddColor("Azure", 223, 255, 255);
  3176. AddColor("Bisque", 255, 199, 142);
  3177. AddColor("Black", 0, 0, 0);
  3178. AddColor("BlanchedAlmond", 255, 213, 157);
  3179. AddColor("Blue", 0, 0, 255);
  3180. AddColor("Chartreuse", 55, 255, 0);
  3181. AddColor("Coral", 255, 55, 19);
  3182. AddColor("Cornsilk", 255, 239, 184);
  3183. AddColor("Cyan", 0, 255, 255);
  3184. AddColor("DarkOrange", 255, 68, 0);
  3185. AddColor("DeepPink", 255, 0, 75);
  3186. AddColor("DeepSkyBlue", 0, 135, 255);
  3187. AddColor("DodgerBlue", 2, 72, 255);
  3188. AddColor("FloralWhite", 255, 244, 223);
  3189. AddColor("GhostWhite", 239, 239, 255);
  3190. AddColor("Green", 0, 255, 0);
  3191. AddColor("GreenYellow", 108, 255, 6);
  3192. AddColor("HoneyDew", 223, 255, 223);
  3193. AddColor("HotPink", 255, 36, 118);
  3194. AddColor("Ivory", 255, 255, 223);
  3195. AddColor("LavenderBlush", 255, 223, 233);
  3196. AddColor("LemonChiffon", 255, 244, 157);
  3197. AddColor("LightCyan", 191, 255, 255);
  3198. AddColor("LightPink", 255, 121, 138);
  3199. AddColor("LightSalmon", 255, 91, 50);
  3200. AddColor("LightYellow", 255, 255, 191);
  3201. AddColor("Magenta", 255, 0, 255);
  3202. AddColor("MintCream", 233, 255, 244);
  3203. AddColor("MistyRose", 255, 199, 193);
  3204. AddColor("Moccasin", 255, 199, 119);
  3205. AddColor("NavajoWhite", 255, 187, 108);
  3206. AddColor("Orange", 255, 97, 0);
  3207. AddColor("OrangeRed", 255, 14, 0);
  3208. AddColor("PapayaWhip", 255, 221, 171);
  3209. AddColor("PeachPuff", 255, 180, 125);
  3210. AddColor("Pink", 255, 136, 154);
  3211. AddColor("Red", 255, 0, 0);
  3212. AddColor("SeaShell", 255, 233, 219);
  3213. AddColor("Snow", 255, 244, 244);
  3214. AddColor("SpringGreen", 0, 255, 55);
  3215. AddColor("SteelBlue", 14, 57, 118);
  3216. AddColor("Tomato", 255, 31, 15);
  3217. AddColor("White", 255, 255, 255);
  3218. AddColor("Yellow", 255, 255, 0);
  3219. // New in ProffieOS 8.x:
  3220. AddColor("ElectricPurple", 127, 0, 255);
  3221. AddColor("ElectricViolet", 71, 0, 255);
  3222. AddColor("ElectricLime", 156, 255, 0);
  3223. AddColor("Amber", 255, 135, 0);
  3224. AddColor("CyberYellow", 255, 168, 0);
  3225. AddColor("CanaryYellow", 255, 221, 0);
  3226. AddColor("PaleGreen", 28, 255, 28);
  3227. AddColor("Flamingo", 255, 80, 254);
  3228. AddColor("VividViolet", 90, 0, 255);
  3229. AddColor("PsychedelicPurple", 186, 0, 255);
  3230. AddColor("HotMagenta", 255, 0, 156);
  3231. AddColor("BrutalPink", 255, 0, 128);
  3232. AddColor("NeonRose", 255, 0, 55);
  3233. AddColor("VividRaspberry", 255, 0, 38);
  3234. AddColor("HaltRed", 255, 0, 19);
  3235. AddColor("MoltenCore", 255, 24, 0);
  3236. AddColor("SafetyOrange", 255, 33, 0);
  3237. AddColor("OrangeJuice", 255, 55, 0);
  3238. AddColor("Orange", 255, 97, 0);
  3239. AddColor("ImperialYellow", 255, 115, 0);
  3240. AddColor("SchoolBus", 255, 176, 0);
  3241. AddColor("SuperSaiyan", 255, 186, 0);
  3242. AddColor("Star", 255, 201, 0);
  3243. AddColor("Lemon", 255, 237, 0);
  3244. AddColor("ElectricBanana", 246, 255, 0);
  3245. AddColor("BusyBee", 231, 255, 0);
  3246. AddColor("ZeusBolt", 219, 255, 0);
  3247. AddColor("LimeZest", 186, 255, 0);
  3248. AddColor("Limoncello", 135, 255, 0);
  3249. AddColor("CathodeGreen", 0, 255, 22);
  3250. AddColor("MintyParadise", 0, 255, 128);
  3251. AddColor("PlungePool", 0, 255, 156);
  3252. AddColor("VibrantMint", 0, 255, 201);
  3253. AddColor("MasterSwordBlue", 0, 255, 219);
  3254. AddColor("BrainFreeze", 0, 219, 255);
  3255. AddColor("BlueRibbon", 0, 33, 255);
  3256. AddColor("RareBlue", 0, 13, 255);
  3257. AddColor("OverdueBlue", 13, 0, 255);
  3258. AddColor("ViolentViolet", 55, 0, 255);
  3259.  
  3260. AddLayer("InOutHelperL<InOutFuncX<Int<300>,Int<800>>>");
  3261. AddLayer("InOutTrL<TrWipe<300>,TrWipeIn<500>>");
  3262.  
  3263. AddLayer("ResponsiveLockupL<White, TrInstant, TrInstant, Int<26000>, Int<6000>>");
  3264. AddLayer("ResponsiveLightningBlockL<White, TrInstant, TrInstant>");
  3265. AddLayer("ResponsiveMeltL<Mix<TwistAngle<>,Red,Yellow>, TrInstant, TrInstant, Int<4000>, Int<10000>>");
  3266. AddLayer("ResponsiveDragL<White, TrInstant, TrInstant, Int<2000>, Int<10000>>");
  3267. AddLayer("ResponsiveClashL<White, TrInstant, TrFade<200>, Int<26000>, Int<6000>>");
  3268. AddLayer("ResponsiveBlastL<White, Int<400>, Int<100>, Int<400>, Int<28000>, Int<8000>>");
  3269. AddLayer("ResponsiveBlastWaveL<White, Int<400>, Int<100>, Int<400>, Int<28000>, Int<8000>>");
  3270. AddLayer("ResponsiveBlastFadeL<White, Int<8000>, Int<400>, Int<28000>, Int<8000>>");
  3271. AddLayer("ResponsiveStabL<White, TrWipeIn<600>, TrWipe<600>, Int<14000>, Int<8000>>");
  3272. AddLayer("SyncAltToVarianceL");
  3273.  
  3274. var WHITE = Rgb(255,255,255);
  3275. var RED = Rgb(255,0,0);
  3276. var GREEN = Rgb(0,255,0);
  3277. var BLUE = Rgb(0,0,255);
  3278. var YELLOW = Rgb(255,255,0);
  3279. var CYAN = Rgb(0,255,255);
  3280. var MAGENTA = Rgb(255,0,255);
  3281. var WHITE = Rgb(255,255,255);
  3282. var BLACK = Rgb(0,0,0);
  3283. var OrangeRed = Rgb(255,14,0);
  3284.  
  3285.  
  3286. //--
  3287. class RainbowClass extends STYLE {
  3288. constructor() {
  3289. super("Scrolling color rainbow", arguments);
  3290. }
  3291. run(blade) {
  3292. this.m = millis();
  3293. }
  3294. getColor(led) {
  3295. return RgbF(max(0.0, sin( (this.m * 3.0 + led * 50.0) % 1024.0 * Math.PI * 2.0 / 1000.0)),
  3296. max(0.0, sin( (this.m * 3.0 + led * 50.0 + 1024.0/3.0) % 1024.0 * Math.PI * 2.0 / 1000.0)),
  3297. max(0.0, sin( (this.m * 3.0 + led * 50.0 + 1024.0 * 2.0/3.0) % 1024.0 * Math.PI * 2.0 / 1000.0)));
  3298. }
  3299.  
  3300. pp() { return this.PPshort("Rainbow", "Scrolling color rainbow"); }
  3301. };
  3302.  
  3303. function Rainbow() {
  3304. return new RainbowClass();
  3305. }
  3306.  
  3307. var STATE_ON = 0;
  3308. // 1 = lockup
  3309. // 2 = drag
  3310. // 3 = lb
  3311. // 4 = melt
  3312. var STATE_LOCKUP = 0;
  3313. var STATE_ROTATE = 0;
  3314. var STATE_NUM_LEDS = 144;
  3315.  
  3316. var handled_lockups = {};
  3317.  
  3318. function IsHandledLockup(lockup_type) {
  3319. return current_style.__handled_lockups[lockup_type];
  3320. }
  3321.  
  3322. function HandleLockup(lockup_type) {
  3323. if (lockup_type.getInteger) {
  3324. lockup_type = lockup_type.getInteger(0);
  3325. }
  3326. handled_lockups[lockup_type] = 1;
  3327. }
  3328.  
  3329.  
  3330. class BladeEffect {
  3331. constructor(type, start_micros, location) {
  3332. this.type = type;
  3333. this.start_micros = start_micros;
  3334. this.location = location;
  3335. this.wavnum = random(10);
  3336. }
  3337. };
  3338.  
  3339.  
  3340. class Range {
  3341. constructor(start, end) {
  3342. this.start = start;
  3343. this.end = end;
  3344. }
  3345. Size() { return max(0, this.end - this.start); }
  3346. Intersect(other) {
  3347. return new Range(max(this.start, other.start), min(this.end, other.end));
  3348. }
  3349. };
  3350.  
  3351. // TODO
  3352. // Gray out buttons not applicable to the current type. - BC WIP.
  3353. // Save -> save to local storage (keep 10?) maybe with images?
  3354. // save as -> save to local storage with name - BC basic version done.
  3355. // Mix
  3356.  
  3357. class ColorCycleClass extends STYLE {
  3358. constructor(COLOR, percentage, rpm,
  3359. ON_COLOR, on_percentage, on_rpm,
  3360. fade_time_millis) {
  3361. super();
  3362. this.COLOR = ColorArg(COLOR);
  3363. this.percentage = IntArg(percentage);
  3364. this.rpm = IntArg(rpm);
  3365. this.ON_COLOR = ColorArg(ON_COLOR, COLOR.DOCOPY());
  3366. this.on_percentage = IntArg(on_percentage, percentage);
  3367. this.on_rpm = IntArg(on_rpm, rpm);
  3368. this.fade_time_millis = IntArg(fade_time_millis, 1);
  3369. this.last_micros_ = 0;
  3370. this.fade_ = 0.0;
  3371. this.pos_ = 0.0;
  3372. }
  3373. run(blade) {
  3374. this.COLOR.run(blade);
  3375. this.ON_COLOR.run(blade);
  3376. var now = millis();
  3377. var delta = now - this.last_micros_;
  3378. this.last_micros_ = now;
  3379. if (delta > 1000) delta = 1;
  3380. var fade_delta = delta / this.fade_time_millis;
  3381. if (!blade.is_on()) fade_delta = - fade_delta;
  3382. this.fade_ = Math.max(0.0, Math.min(1.0, this.fade_ + fade_delta));
  3383. var rpm = this.rpm * (1.0 - this.fade_) + this.on_rpm * this.fade_;
  3384. var percentage = this.percentage * (1.0 - this.fade_) + this.on_percentage * this.fade_;
  3385. this.fraction_ = percentage / 100.0;
  3386. this.pos_ = ((this.pos_ + delta / 60000.0 * rpm) % 1.0);
  3387. }
  3388. getColor(led) {
  3389. var led_range = new Range(led / 144.0, (led + 1) / 144.0);
  3390. var black_mix = 0.0;
  3391. if (this.pos_ + this.fraction_ < 1.0) {
  3392. black_mix = new Range(this.pos_, this.pos_ + this.fraction_).Intersect(led_range).Size();
  3393. } else {
  3394. black_mix = new Range(this.pos_, 1.0).Intersect(led_range).Size() +
  3395. new Range(0.0, (this.pos_ + this.fraction_) % 1.0).Intersect(led_range).Size();
  3396. }
  3397. black_mix *= 144.0;
  3398. var c = this.COLOR.getColor(led);
  3399. var on_c = this.ON_COLOR.getColor(led);
  3400. c = c.mix(on_c, this.fade_);
  3401. c = BLACK.mix(c, black_mix);
  3402. return c;
  3403. }
  3404. pp() {
  3405. return this.PP("ColorCycle", "Rotating beam",
  3406. this.COLOR, "beam color",
  3407. this.percentage, "percentage of blade lit",
  3408. this.rpm, "rotation speed",
  3409. this.ON_COLOR, "beam color when on",
  3410. this.on_percentage, "percentage of blade lit when on",
  3411. this.on_rpm, "rotation speed when on",
  3412. this.fade_time_millis, "time to transition to/from on state");
  3413. }
  3414. };
  3415.  
  3416. function ColorCycle(COLOR, percentage, rpm,
  3417. ON_COLOR, on_percentage, on_rpm,
  3418. fade_time_millis) {
  3419. return new ColorCycleClass(COLOR, percentage, rpm,
  3420. ON_COLOR, on_percentage, on_rpm,
  3421. fade_time_millis);
  3422. }
  3423.  
  3424.  
  3425. class CylonClass extends STYLE {
  3426. constructor(COLOR, percentage, rpm,
  3427. ON_COLOR, on_percentage, on_rpm,
  3428. fade_time_millis) {
  3429. super();
  3430. this.COLOR = ColorArg(COLOR);
  3431. this.percentage = IntArg(percentage);
  3432. this.rpm = IntArg(rpm);
  3433. this.ON_COLOR = ColorArg(ON_COLOR, COLOR.DOCOPY());
  3434. this.on_percentage = IntArg(on_percentage, percentage);
  3435. this.on_rpm = IntArg(on_rpm, rpm);
  3436. this.fade_time_millis = IntArg(fade_time_millis, 1);
  3437. this.last_micros_ = 0;
  3438. this.fade_ = 0.0;
  3439. this.pos_ = 0.0;
  3440. }
  3441. run(blade) {
  3442. this.COLOR.run(blade);
  3443. this.ON_COLOR.run(blade);
  3444. var now = millis();
  3445. var delta = now - this.last_micros_;
  3446. this.last_micros_ = now;
  3447. if (delta > 1000) delta = 1;
  3448. var fade_delta = delta / this.fade_time_millis;
  3449. if (!blade.is_on()) fade_delta = - fade_delta;
  3450. this.fade_ = Math.max(0.0, Math.min(1.0, this.fade_ + fade_delta));
  3451. // setvar(this.MIX, this.fade_);
  3452. var rpm = this.rpm * (1.0 - this.fade_) + this.on_rpm * this.fade_;
  3453. var percentage = this.percentage * (1.0 - this.fade_) + this.on_percentage * this.fade_;
  3454. this.fraction_ = percentage / 100.0;
  3455. // TODO: FIXME THIS SHOULD BE SIN()
  3456. this.pos_ = (this.pos_ + delta / 60000.0 * rpm) % 1.0;
  3457. this.POS = (Math.sin(this.pos_ * Math.PI * 2.0) + 1.0) * (0.5 - percentage/200.0);
  3458. }
  3459. getColor(led) {
  3460. var led_range = new Range(led / 144.0, (led + 1) / 144.0);
  3461. var black_mix = new Range(this.POS, this.POS + this.fraction_).Intersect(led_range).Size();
  3462. black_mix *= 144.0;
  3463. var c = this.COLOR.getColor(led);
  3464. var on_c = this.ON_COLOR.getColor(led);
  3465. c = c.mix(on_c, this.fade_);
  3466. c = BLACK.mix(c, black_mix);
  3467. return c;
  3468. }
  3469. pp() {
  3470. return this.PP("Cylon", "Rotating beam",
  3471. this.COLOR, "beam color",
  3472. this.percentage, "percentage of blade lit",
  3473. this.rpm, "rotation speed",
  3474. this.ON_COLOR, "beam color when on",
  3475. this.on_percentage, "percentage of blade lit when on",
  3476. this.on_rpm, "rotation speed when on",
  3477. this.fade_time_millis, "time to transition to/from on state");
  3478. }
  3479. };
  3480.  
  3481. function Cylon(COLOR, percentage, rpm,
  3482. ON_COLOR, on_percentage, on_rpm,
  3483. fade_time_millis) {
  3484. return new CylonClass(COLOR, percentage, rpm,
  3485. ON_COLOR, on_percentage, on_rpm,
  3486. fade_time_millis);
  3487. }
  3488.  
  3489. class OnSparkFClass extends FUNCTION {
  3490. constructor(T, SPARK_COLOR, MILLIS) {
  3491. super("Returns 32768 on startup and then fades out for 'MILLIS' milliseconds on startup.", arguments);
  3492. this.add_arg("MILLIS", "FUNCTION", "Millis", 200);
  3493. this.on_ = false;
  3494. this.on_millis_ = 0;
  3495. }
  3496. run(blade) {
  3497. super.run(blade);
  3498. var ms = this.MILLIS.getInteger(0);
  3499.  
  3500. var m = millis();
  3501. if (blade.is_on() != this.on_) {
  3502. this.on_ = blade.is_on();
  3503. if (this.on_) this.on_millis_ = m;
  3504. }
  3505. var t = m - this.on_millis_;
  3506. if (t < ms) {
  3507. this.mix_ = 1.0 - t / ms;
  3508. } else {
  3509. this.mix_ = 0.0;
  3510. }
  3511. }
  3512. getInteger(led) {
  3513. return this.mix_ * 32768;
  3514. }
  3515. };
  3516.  
  3517. function OnSparkF(MILLIS) {
  3518. return new OnSparkFClass(MILLIS);
  3519. }
  3520.  
  3521. class OnSparkLClass extends MACRO {
  3522. constructor(SPARK_COLOR, MILLIS) {
  3523. super("Shows the spark color for 'MILLIS' milliseconds on startup.", arguments);
  3524. this.add_arg("SPARK_COLOR", "COLOR", "Spark color", WHITE.DOCOPY());
  3525. this.add_arg("MILLIS", "FUNCTION", "Millis", Int(200));
  3526. this.SetExpansion(AlphaL(this.SPARK_COLOR, OnSparkF(this.MILLIS)));
  3527. }
  3528. };
  3529.  
  3530. function OnSparkL(SPARK_COLOR, MILLIS) {
  3531. return new OnSparkLClass(SPARK_COLOR, MILLIS);
  3532. }
  3533.  
  3534. class OnSparkXClass extends MACRO {
  3535. constructor(T, SPARK_COLOR, MILLIS) {
  3536. super("Shows the spark color for 'MILLIS' milliseconds on startup.", arguments);
  3537. this.add_arg("T", "COLOR", "Base color");
  3538. this.add_arg("SPARK_COLOR", "COLOR", "Spark color", WHITE.DOCOPY());
  3539. this.add_arg("MILLIS", "FUNCTION", "Millis", Int(200));
  3540. this.SetExpansion(Layers(T, OnSparkL(this.SPARK_COLOR, this.MILLIS)));
  3541. }
  3542. };
  3543.  
  3544. function OnSparkX(T, SPARK_COLOR, MILLIS) {
  3545. return new OnSparkXClass(T, SPARK_COLOR, MILLIS);
  3546. }
  3547.  
  3548. class OnSparkClass extends MACRO {
  3549. constructor(T, SPARK_COLOR, MILLIS) {
  3550. super("Shows the spark color for 'MILLIS' milliseconds on startup.", arguments);
  3551. this.add_arg("T", "COLOR", "Base color");
  3552. this.add_arg("SPARK_COLOR", "COLOR", "Spark color", WHITE.DOCOPY());
  3553. this.add_arg("MILLIS", "INT", "Millis", 200);
  3554. this.SetExpansion(OnSparkX(T, this.SPARK_COLOR, Int(this.MILLIS)));
  3555. }
  3556. };
  3557.  
  3558. function OnSpark(T, SPARK_COLOR, MILLIS) {
  3559. return new OnSparkClass(T, SPARK_COLOR, MILLIS);
  3560. }
  3561.  
  3562. class PulsingFClass extends FUNCTION {
  3563. constructor(PULSE_MILLIS) {
  3564. super("Pulses between 0 and 32768 every M milliseconds", Array.from(arguments));
  3565. this.add_arg("PULSE_MILLIS", "FUNCTION", "M");
  3566. }
  3567. run(blade) {
  3568. super.run(blade)
  3569. this.var_ = 0.5 + 0.5 * Math.sin(millis() * 3.1415 * 2.0/ this.PULSE_MILLIS.getInteger(0));
  3570. }
  3571. getInteger(led) {
  3572. return this.var_ * 32768;
  3573. }
  3574. }
  3575.  
  3576. function PulsingF(PULSE_MILLIS) {
  3577. return new PulsingFClass(PULSE_MILLIS);
  3578. }
  3579.  
  3580. class PulsingLClass extends MACRO {
  3581. constructor(COLOR2, PULSE_MILLIS) {
  3582. super("Pulses between transparent and B every M milliseconds", Array.from(arguments));
  3583. this.add_arg("COLOR2", "COLOR", "B");
  3584. this.add_arg("PULSE_MILLIS", "FUNCTION", "M");
  3585. this.SetExpansion(AlphaL(COLOR2, PulsingF(PULSE_MILLIS)));
  3586. }
  3587. }
  3588.  
  3589. function PulsingL(COLOR2, PULSE_MILLIS) {
  3590. return new PulsingLClass(COLOR2, PULSE_MILLIS);
  3591. }
  3592.  
  3593. class PulsingXClass extends MACRO {
  3594. constructor(COLOR1, COLOR2, PULSE_MILLIS) {
  3595. super("Pulses between A and B every M milliseconds", Array.from(arguments));
  3596. this.add_arg("COLOR1", "COLOR", "A");
  3597. this.add_arg("COLOR2", "COLOR", "B");
  3598. this.add_arg("PULSE_MILLIS", "FUNCTION", "M");
  3599. this.SetExpansion(Layers(COLOR1, PulsingL(COLOR2, PULSE_MILLIS)));
  3600. }
  3601. }
  3602.  
  3603. function PulsingX(COLOR1, COLOR2, PULSE_MILLIS) {
  3604. return new PulsingXClass(COLOR1, COLOR2, PULSE_MILLIS);
  3605. }
  3606.  
  3607. class PulsingClass extends MACRO {
  3608. constructor(COLOR1, COLOR2, PULSE_MILLIS) {
  3609. super("Pulses between A and B every M milliseconds", Array.from(arguments));
  3610. this.add_arg("COLOR1", "COLOR", "A");
  3611. this.add_arg("COLOR2", "COLOR", "B");
  3612. this.add_arg("PULSE_MILLIS", "INT", "M");
  3613. this.SetExpansion(PulsingX(COLOR1, COLOR2, Int(PULSE_MILLIS)));
  3614. }
  3615. }
  3616.  
  3617. function Pulsing(COLOR1, COLOR2, PULSE_MILLIS) {
  3618. return new PulsingClass(COLOR1, COLOR2, PULSE_MILLIS);
  3619. }
  3620.  
  3621. class SparkleFClass extends FUNCTION {
  3622. constructor(SPARK_CHANCE_PROMILLE, SPARK_INTENSITY) {
  3623. super("Sparkles!!", Array.from(arguments));
  3624. this.add_arg("SPARK_CHANCE_PROMILLE", "INT", "Chance of new sparks.", 300);
  3625. this.add_arg("SPARK_INTENSITY", "INT", "Initial spark intensity", 1024);
  3626. this.sparks = new Uint16Array(144 + 4);
  3627. this.last_update = 0;
  3628. }
  3629. run(blade) {
  3630. super.run(blade);
  3631. var m = millis();
  3632. if (m - this.last_update >= 10) {
  3633. this.last_update = m;
  3634. var fifo = 0
  3635. var N = blade.num_leds();
  3636. for (var i = 2; i <= N + 2; i++) {
  3637. var x = ((this.sparks[i-1] + this.sparks[i+1]) * 200 + this.sparks[i] * 570) / 1024;
  3638. this.sparks[i-1] = fifo;
  3639. fifo = x;
  3640. }
  3641. this.sparks[N] = fifo;
  3642. if (random(1000) < this.SPARK_CHANCE_PROMILLE) {
  3643. this.sparks[random(blade.num_leds()) + 2] += this.SPARK_INTENSITY;
  3644. }
  3645. }
  3646. }
  3647. getInteger(led) {
  3648. return clamp(this.sparks[led + 2], 0, 255) << 7;
  3649. }
  3650. }
  3651.  
  3652. function SparkleF(SPARK_CHANCE_PROMILLE, SPARK_INTENSITY) {
  3653. return new SparkleFClass(SPARK_CHANCE_PROMILLE, SPARK_INTENSITY);
  3654. }
  3655.  
  3656. class SparkleLClass extends MACRO {
  3657. constructor(SPARKLE_COLOR, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY) {
  3658. super("Sparkles!!", Array.from(arguments));
  3659. this.add_arg("SPARKLE_COLOR", "COLOR", "Spark color", Rgb(255,255,255));
  3660. this.add_arg("SPARK_CHANCE_PROMILLE", "INT", "Chance of new sparks.", 300);
  3661. this.add_arg("SPARK_INTENSITY", "INT", "Initial spark intensity", 1024);
  3662. this.SetExpansion(AlphaL(this.SPARKLE_COLOR, SparkleF(this.SPARK_CHANCE_PROMILLE, this.SPARK_INTENSITY)));
  3663. }
  3664. }
  3665.  
  3666. function SparkleL(SPARKLE_COLOR, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY) {
  3667. return new SparkleLClass(SPARKLE_COLOR, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY);
  3668. }
  3669.  
  3670. class SparkleClass extends MACRO {
  3671. constructor(BASE, SPARKLE_COLOR, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY) {
  3672. super("Sparkles!!", Array.from(arguments));
  3673. this.add_arg("BASE", "COLOR", "Normal blade color");
  3674. this.add_arg("SPARKLE_COLOR", "COLOR", "Spark color", Rgb(255,255,255));
  3675. this.add_arg("SPARK_CHANCE_PROMILLE", "INT", "Chance of new sparks.", 300);
  3676. this.add_arg("SPARK_INTENSITY", "INT", "Initial spark intensity", 1024);
  3677. this.SetExpansion(Layers(BASE, SparkleL(this.SPARKLE_COLOR, this.SPARK_CHANCE_PROMILLE, this.SPARK_INTENSITY)));
  3678. }
  3679. }
  3680.  
  3681. function Sparkle(BASE, SPARKLE_COLOR, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY) {
  3682. return new SparkleClass(BASE, SPARKLE_COLOR, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY);
  3683. }
  3684.  
  3685. class StrobeFClass extends FUNCTION {
  3686. constructor(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3687. super("Stroboscope effect", arguments);
  3688. this.add_arg("STROBE_FREQUENCY", "FUNCTION", "Strobe frequency.");
  3689. this.add_arg("STROBE_MILLIS", "FUNCTION", "Pulse length in milliseconds.");
  3690. this.strobe_ = false;
  3691. this.strobe_start_ = 0;
  3692. }
  3693. run(blade) {
  3694. super.run(blade);
  3695. var m = millis();
  3696. var strobe_millis = this.STROBE_MILLIS.getInteger(0);
  3697. var strobe_frequency = this.STROBE_FREQUENCY.getInteger(0);
  3698. var timeout = this.strobe_ ? strobe_millis : (1000 / strobe_frequency);
  3699. if (m - this.strobe_start_ > timeout) {
  3700. this.strobe_start_ += timeout;
  3701. if (m - this.strobe_start_ > strobe_millis + (1000 / strobe_frequency))
  3702. this.strobe_start_ = m;
  3703. this.strobe_ = !this.strobe_;
  3704. }
  3705. }
  3706. getInteger(led) {
  3707. return this.strobe_ ? 32768 : 0;
  3708. }
  3709. };
  3710.  
  3711. function StrobeF(STROBE_FREQUENCY, STROBE_MILLIS) {
  3712. return new StrobeFClass(STROBE_FREQUENCY, STROBE_MILLIS);
  3713. }
  3714.  
  3715. class StrobeLClass extends MACRO {
  3716. constructor(STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3717. super("Stroboscope effect", arguments);
  3718. this.add_arg("STROBE_COLOR", "COLOR", "Strobe color");
  3719. this.add_arg("STROBE_FREQUENCY", "FUNCTION", "Strobe frequency.");
  3720. this.add_arg("STROBE_MILLIS", "FUNCTION", "Pulse length in milliseconds.");
  3721. this.SetExpansion(AlphaL(STROBE_COLOR, StrobeF(STROBE_FREQUENCY, STROBE_MILLIS)));
  3722. }
  3723. };
  3724.  
  3725. function StrobeL(STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3726. return new StrobeLClass(STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS);
  3727. }
  3728.  
  3729. class StrobeXClass extends MACRO {
  3730. constructor(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3731. super("Stroboscope effect", arguments);
  3732. this.add_arg("T", "COLOR", "Base color");
  3733. this.add_arg("STROBE_COLOR", "COLOR", "Strobe color");
  3734. this.add_arg("STROBE_FREQUENCY", "FUNCTION", "Strobe frequency.");
  3735. this.add_arg("STROBE_MILLIS", "FUNCTION", "Pulse length in milliseconds.");
  3736. this.SetExpansion(Layers(T, StrobeL(STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS)));
  3737. }
  3738. };
  3739.  
  3740. function StrobeX(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3741. return new StrobeXClass(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS);
  3742. }
  3743.  
  3744. class StrobeClass extends MACRO {
  3745. constructor(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3746. super("Stroboscope effect", arguments);
  3747. this.add_arg("T", "COLOR", "Base color");
  3748. this.add_arg("STROBE_COLOR", "COLOR", "Strobe color");
  3749. this.add_arg("STROBE_FREQUENCY", "INT", "Strobe frequency.");
  3750. this.add_arg("STROBE_MILLIS", "INT", "Pulse length in milliseconds.");
  3751. this.SetExpansion(StrobeX(T, STROBE_COLOR, Int(STROBE_FREQUENCY), Int(STROBE_MILLIS)));
  3752. }
  3753. };
  3754.  
  3755. function Strobe(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS) {
  3756. return new StrobeClass(T, STROBE_COLOR, STROBE_FREQUENCY, STROBE_MILLIS);
  3757. }
  3758.  
  3759. class GradientClass extends STYLE {
  3760. constructor(COLORS) {
  3761. super("COLOR2 at base, COLOR2 at tip, smooth gradient in between.", COLORS);
  3762. this.COLORS = COLORS;
  3763. for (var i = 0; i < this.COLORS.length; i++)
  3764. this.add_arg("COLOR" + (i + 1), "COLOR", "COLOR " + (i + 1));
  3765. }
  3766. run(blade) {
  3767. for (var i = 0; i < this.COLORS.length; i++)
  3768. this.COLORS[i].run(blade);
  3769. this.num_leds_ = 1.0 * blade.num_leds();
  3770. }
  3771. getColor(led) {
  3772. var pos = led / this.num_leds_ * (this.COLORS.length - 1);
  3773. var N = min(this.COLORS.length -2, Math.floor(pos));
  3774. return this.COLORS[N].getColor(led).mix(this.COLORS[N+1].getColor(led), pos - N) ;
  3775. }
  3776. };
  3777.  
  3778. function Gradient(A, B, C, D) {
  3779. return new GradientClass(Array.from(arguments));
  3780. }
  3781.  
  3782.  
  3783. class MixClass extends STYLE {
  3784. constructor(ARGS) {
  3785. super("Mix between colors", ARGS);
  3786. this.COLORS = Array.from(ARGS).slice(1);
  3787. this.add_arg("F", "FUNCTION", "0=first color, 32768=last color");
  3788. for (var i = 1; i < this.COLORS.length + 1; i++)
  3789. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  3790. }
  3791. run(blade) {
  3792. this.F.run(blade);
  3793. for (var i = 0; i < this.COLORS.length; i++)
  3794. this.COLORS[i].run(blade);
  3795. }
  3796. getColor(led) {
  3797. var v = this.F.getInteger(led);
  3798. var pos = max(0, min(32768, v)) * (this.COLORS.length - 1) / 32768;
  3799. var N = min(this.COLORS.length -2, Math.floor(pos));
  3800. return this.COLORS[N].getColor(led).mix(this.COLORS[N+1].getColor(led), pos - N) ;
  3801. }
  3802. };
  3803.  
  3804. function Mix(F, C1, C2) {
  3805. return new MixClass(Array.from(arguments));
  3806. };
  3807.  
  3808. class IgnitionDelayXClass extends STYLE {
  3809. constructor(DELAY_MILLIS, BASE) {
  3810. super("Delays ignition by DELAY_MILLIS", Array.from(arguments));
  3811. this.add_arg("DELAY_MILLIS", "FUNCTION", "Ignition delay, in milliseconds");
  3812. this.add_arg("BASE", "COLOR", "Blade style");
  3813. }
  3814. is_on() {
  3815. return this.is_on_;
  3816. }
  3817. num_leds() {
  3818. return this.blade.num_leds()
  3819. }
  3820. GetEffects() { return this.blade.GetEffects(); }
  3821. run(blade) {
  3822. this.DELAY_MILLIS.run(blade);
  3823. var delay_millis = this.DELAY_MILLIS.getInteger(0);
  3824. this.blade = blade;
  3825. if (blade.is_on()) {
  3826. if (!this.waiting) {
  3827. this.waiting = true;
  3828. this.wait_start_time = millis();
  3829. }
  3830. var waited = millis() - this.wait_start_time;
  3831. if (waited > delay_millis) {
  3832. this.is_on_ = true;
  3833. this.wait_start_time = millis() - delay_millis - 1;
  3834. }
  3835. } else {
  3836. this.waiting = false;
  3837. this.is_on_ = false;
  3838. }
  3839. this.BASE.run(this)
  3840. }
  3841. getColor(led) {
  3842. return this.BASE.getColor(led);
  3843. }
  3844. }
  3845.  
  3846. function IgnitionDelayX(millis, base) {
  3847. return new IgnitionDelayXClass(millis, base);
  3848. }
  3849.  
  3850. class IgnitionDelayClass extends MACRO {
  3851. constructor(DELAY_MILLIS, BASE) {
  3852. super("Delays ignition by DELAY_MILLIS", Array.from(arguments));
  3853. this.add_arg("DELAY_MILLIS", "INT", "Ignition delay, in milliseconds");
  3854. this.add_arg("BASE", "COLOR", "Blade style");
  3855. this.SetExpansion(IgnitionDelayX(Int(DELAY_MILLIS), BASE));
  3856. }
  3857. }
  3858.  
  3859. function IgnitionDelay(millis, base) {
  3860. return new IgnitionDelayClass(millis, base);
  3861. }
  3862.  
  3863. class RetractionDelayXClass extends STYLE {
  3864. constructor(DELAY_MILLIS, BASE) {
  3865. super("Delays retraction by DELAY_MILLIS", Array.from(arguments));
  3866. this.add_arg("DELAY_MILLIS", "FUNCTION", "Ignition delay, in milliseconds");
  3867. this.add_arg("BASE", "COLOR", "Blade style");
  3868. }
  3869. is_on() {
  3870. return this.is_on_;
  3871. }
  3872. num_leds() {
  3873. return this.blade.num_leds()
  3874. }
  3875. GetEffects() { return this.blade.GetEffects(); }
  3876. run(blade) {
  3877. this.DELAY_MILLIS.run(blade);
  3878. var delay_millis = this.DELAY_MILLIS.getInteger(0);
  3879. this.blade = blade;
  3880. if (!blade.is_on()) {
  3881. if (!this.waiting) {
  3882. this.waiting = true;
  3883. this.wait_start_time = millis();
  3884. }
  3885. var waited = millis() - this.wait_start_time;
  3886. if (waited > delay_millis) {
  3887. this.is_on_ = false;
  3888. this.wait_start_time = millis() - delay_millis - 1;
  3889. }
  3890. } else {
  3891. this.waiting = false;
  3892. this.is_on_ = true;
  3893. }
  3894. this.BASE.run(this)
  3895. }
  3896. getColor(led) {
  3897. return this.BASE.getColor(led);
  3898. }
  3899. }
  3900.  
  3901. function RetractionDelayX(millis, base) {
  3902. return new RetractionDelayXClass(millis, base);
  3903. }
  3904.  
  3905. class RetractionDelayClass extends MACRO {
  3906. constructor(DELAY_MILLIS, BASE) {
  3907. super("Delays retraction by DELAY_MILLIS", Array.from(arguments));
  3908. this.add_arg("DELAY_MILLIS", "INT", "Ignition delay, in milliseconds");
  3909. this.add_arg("BASE", "COLOR", "Blade style");
  3910. this.SetExpansion(RetractionDelayX(Int(DELAY_MILLIS), BASE));
  3911. }
  3912. }
  3913. function RetractionDelay(millis, base) {
  3914. return new RetractionDelayClass(millis, base);
  3915. }
  3916.  
  3917. class RandomBlinkFClass extends FUNCTION {
  3918. constructor(MILLIHZ) {
  3919. super("Blink each LED randomly MILLIHZ times per second.", arguments);
  3920. this.add_arg("MILLIHZ", "FUNCTION", "how often to blink");
  3921. this.last_update = 0;
  3922. this.state = [];
  3923. }
  3924. run(blade) {
  3925. super.run(blade);
  3926. var now = micros();
  3927. if (now - this.last_update > 1000000000 / this.MILLIHZ.getInteger(0)) {
  3928. this.last_update = now;
  3929. for (var i = 0; i < blade.num_leds(); i++) {
  3930. this.state[i] = random(2);
  3931. }
  3932. }
  3933. }
  3934.  
  3935. getInteger(led) {
  3936. return this.state[led] ? 32768 : 0;
  3937. }
  3938. };
  3939.  
  3940. function RandomBlinkF(millihz) {
  3941. return new RandomBlinkFClass(millihz);
  3942. }
  3943.  
  3944. class RandomBlinkLClass extends MACRO {
  3945. constructor(MILLIHZ, COLOR1, COLOR2) {
  3946. super("Blink each LED randomly MILLIHZ times per second.", arguments);
  3947. this.add_arg("MILLIHZ", "FUNCTION", "how often to blink");
  3948. this.add_arg("COLOR2", "COLOR", "second color", BLACK.DOCOPY());
  3949. this.SetExpansion(AlphaL(this.COLOR2, RandomBlinkF(MILLIHZ)));
  3950. }
  3951. };
  3952.  
  3953. function RandomBlinkL(millihz, c1) {
  3954. return new RandomBlinkLClass(millihz, c1);
  3955. }
  3956.  
  3957. class RandomBlinkXClass extends MACRO {
  3958. constructor(MILLIHZ, COLOR1, COLOR2) {
  3959. super("Blink each LED randomly MILLIHZ times per second.", arguments);
  3960. this.add_arg("MILLIHZ", "FUNCTION", "how often to blink");
  3961. this.add_arg("COLOR1", "COLOR", "first color", WHITE.DOCOPY());
  3962. this.add_arg("COLOR2", "COLOR", "second color", BLACK.DOCOPY());
  3963. this.SetExpansion(Layers(this.COLOR1, RandomBlinkL(this.MILLIHZ, this.COLOR2)));
  3964. }
  3965. };
  3966.  
  3967. function RandomBlinkX(millihz, c1, c2) {
  3968. return new RandomBlinkXClass(millihz, c1, c2);
  3969. }
  3970.  
  3971. class RandomBlinkClass extends MACRO {
  3972. constructor(MILLIHZ, COLOR1, COLOR2) {
  3973. super("Blink each LED randomly MILLIHZ times per second.", arguments);
  3974. this.add_arg("MILLIHZ", "INT", "how often to blink");
  3975. this.add_arg("COLOR1", "COLOR", "first color", WHITE.DOCOPY());
  3976. this.add_arg("COLOR2", "COLOR", "second color", BLACK.DOCOPY());
  3977. this.SetExpansion(RandomBlinkX(Int(this.MILLIHZ), this.COLOR1, this.COLOR2));
  3978. }
  3979. };
  3980.  
  3981. function RandomBlink(MILLIHZ, COLOR1, COLOR2) {
  3982. return new RandomBlinkClass(MILLIHZ, COLOR1, COLOR2);
  3983. }
  3984.  
  3985. class SequenceFClass extends FUNCTION {
  3986. constructor(ARGS) {
  3987. super("Pre-defined sequence of 0 and 32768", ARGS);
  3988. this.add_arg("MILLIS_PER_BIT", "INT", "Milliseconds per bit.");
  3989. this.add_arg("BITS", "INT", "total bits");
  3990. for (var i = 0; i < this.BITS; i+= 16) {
  3991. this.add_arg("BITS"+i, "INT", "Bit sequence " + ((i/16)+1));
  3992. }
  3993. this.SEQUENCE = Array.from(ARGS).slice(2);
  3994. }
  3995. run(blade) {
  3996. super.run(blade);
  3997. var now = millis();
  3998. var bit = (now / this.MILLIS_PER_BIT) % min(this.BITS, this.SEQUENCE.length * 16);
  3999. this.on = !!(this.SEQUENCE[bit >> 4] >> ((~bit) & 0xf) & 1)
  4000. }
  4001. getInteger(led) {
  4002. return this.on ? 32768 : 0;
  4003. }
  4004. };
  4005.  
  4006. function SequenceF(MILLIHZ_PER_BIT, BITS, SEQUENCE) {
  4007. return new SequenceFClass(Array.from(arguments));
  4008. }
  4009.  
  4010. class SequenceLClass extends MACRO {
  4011. constructor(ARGS) {
  4012. super("Pre-defined sequence", ARGS);
  4013. this.add_arg("COLOR", "COLOR", "Color if bit is 2");
  4014. this.add_arg("MILLIS_PER_BIT", "INT", "Milliseconds per bit.");
  4015. this.add_arg("BITS", "INT", "total bits");
  4016. for (var i = 0; i < this.BITS; i+= 16) {
  4017. this.add_arg("BITS"+i, "INT", "Bit sequence " + ((i/16)+1));
  4018. }
  4019. this.SetExpansion(AlphaL(this.COLOR, new SequenceFClass(ARGS.slice(1))));
  4020. }
  4021. };
  4022.  
  4023. function SequenceL(COLOR2, MILLIHZ_PER_BIT, BITS, SEQUENCE) {
  4024. return new SequenceLClass(Array.from(arguments));
  4025. }
  4026.  
  4027. class SequenceClass extends MACRO {
  4028. constructor(ARGS) {
  4029. super("Pre-defined sequence", ARGS);
  4030. this.add_arg("COLOR1", "COLOR", "Color if bit is 1");
  4031. this.add_arg("COLOR2", "COLOR", "Color if bit is 0");
  4032. this.add_arg("MILLIS_PER_BIT", "INT", "Milliseconds per bit.");
  4033. this.add_arg("BITS", "INT", "total bits");
  4034. for (var i = 0; i < this.BITS; i+= 16) {
  4035. this.add_arg("BITS"+i, "INT", "Bit sequence " + ((i/16)+1));
  4036. }
  4037. this.SetExpansion(Layers(this.COLOR2, new SequenceLClass([this.COLOR1].concat(ARGS.slice(2)))));
  4038. }
  4039. };
  4040.  
  4041. function Sequence(COLOR1, COLOR2, MILLIHZ_PER_BIT, BITS, SEQUENCE) {
  4042. return new SequenceClass(Array.from(arguments));
  4043. }
  4044.  
  4045. class ColorSequenceClass extends STYLE {
  4046. constructor(ARGS) {
  4047. super("Pre-defined sequence", ARGS);
  4048. this.add_arg("MILLIS_PER_COLOR", "INT", "Milliseconds before moving to next color.");
  4049. this.COLORS = Array.from(ARGS).slice(1);
  4050. for (var i = 1; i < this.COLORS.length + 1; i++)
  4051. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  4052. this.last_micros = 0;
  4053. this.n = 0;
  4054. }
  4055. run(blade) {
  4056. super.run(blade);
  4057. var now = micros();
  4058. var millis_per_color = this.MILLIS_PER_COLOR.getInteger(0);
  4059. if (now - this.last_micros > millis_per_color * 1000) {
  4060. if (now - this.last_micros > millis_per_color * 10000) {
  4061. this.n = 0;
  4062. this.last_micros = now;
  4063. } else {
  4064. this.n = (this.n + 1) % this.COLORS.length;
  4065. this.last_micros += millis_per_color * 1000;
  4066. }
  4067. }
  4068. }
  4069. getColor(led) { return this.COLORS[this.n].getColor(led); }
  4070. };
  4071.  
  4072. function ColorSequence(MPC, C) {
  4073. return new ColorSequenceClass(Array.from(arguments));
  4074. };
  4075.  
  4076. class EffectSequenceClass extends STYLE {
  4077. constructor(ARGS) {
  4078. super("Sequence that changes on events.", ARGS);
  4079. this.add_arg("EFFECT", "EFFECT", "effect that goes to next color");
  4080. this.COLORS = Array.from(ARGS).slice(1);
  4081. for (var i = 1; i < this.COLORS.length + 1; i++)
  4082. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  4083. this.last_micros = 0;
  4084. this.n = this.COLORS.length - 1;
  4085. this.effect_ = new OneshotEffectDetector(this.EFFECT);
  4086. }
  4087. run(blade) {
  4088. super.run(blade);
  4089. var now = micros();
  4090.  
  4091. if (this.effect_.Detect(blade)) {
  4092. this.n = (this.n + 1) % this.COLORS.length;
  4093. }
  4094. }
  4095. getColor(led) { return this.COLORS[this.n].getColor(led); }
  4096. };
  4097.  
  4098. function EffectSequence(MPC, C) {
  4099. return new EffectSequenceClass(Array.from(arguments));
  4100. };
  4101.  
  4102. class StripesXClass extends STYLE {
  4103. constructor(ARGS) {
  4104. super("Configurable rainbow", ARGS);
  4105. this.add_arg("WIDTH", "FUNCTION", "Stripe width");
  4106. this.add_arg("SPEED", "FUNCTION", "Scroll speed");
  4107. this.COLORS = ARGS.slice(2);
  4108. for (var i = 1; i < this.COLORS.length + 1; i++)
  4109. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  4110. this.last_micros = 0;
  4111. this.m = 0;
  4112. }
  4113. run(blade) {
  4114. super.run(blade);
  4115. var now_micros = micros();
  4116. var delta_micros = now_micros - this.last_micros;
  4117. this.last_micros = now_micros;
  4118. this.m = MOD( (this.m + delta_micros * this.SPEED.getInteger(0) / 333), (this.COLORS.length * 341 * 1024))
  4119. this.mult = (50000 * 1024 / this.WIDTH.getInteger(0));
  4120. }
  4121. GET_COLOR(N, led, p, ret) {
  4122. if (N >= this.COLORS.length || p < 0) return;
  4123. if (p > 0 && p < 512) {
  4124. var tmp = this.COLORS[N].getColor(led);
  4125. var mul = sin(p * Math.PI / 512.0);
  4126. ret.r += tmp.r * mul;
  4127. ret.g += tmp.g * mul;
  4128. ret.b += tmp.b * mul;
  4129. }
  4130. this.GET_COLOR(N+1, led, p - 341, ret);
  4131. }
  4132. getColor(led) {
  4133. var p = ((this.m + led * this.mult) >> 10) % (this.COLORS.length * 341);
  4134. var ret = Rgb(0,0,0);
  4135. this.GET_COLOR(0, led, p, ret);
  4136. this.GET_COLOR(0, led, p + 341 * this.COLORS.length, ret);
  4137. return ret;
  4138. }
  4139. }
  4140.  
  4141. function StripesX(W,S,C) {
  4142. return new StripesXClass(Array.from(arguments));
  4143. }
  4144.  
  4145. class StripesClass extends MACRO {
  4146. constructor(ARGS) {
  4147. super("Configurable rainbow", ARGS);
  4148. this.add_arg("WIDTH", "INT", "Stripe width");
  4149. this.add_arg("SPEED", "INT", "Scroll speed");
  4150. this.COLORS = ARGS.slice(2);
  4151. for (var i = 1; i < this.COLORS.length + 1; i++)
  4152. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  4153.  
  4154. this.SetExpansion(new StripesXClass([Int(this.WIDTH), Int(this.SPEED)].concat(this.COLORS)));
  4155. }
  4156. }
  4157.  
  4158. function Stripes(W,S,C) {
  4159. return new StripesClass(Array.from(arguments));
  4160. }
  4161.  
  4162. class AudioFlickerLClass extends MACRO {
  4163. constructor(COLOR) {
  4164. super("Audio flicker layer, higher volumes means less transparent.", arguments);
  4165. this.add_arg("COLOR", "COLOR", "COLOR");
  4166. this.SetExpansion(AlphaL(this.COLOR, NoisySoundLevelCompat()));
  4167. }
  4168. }
  4169.  
  4170. function AudioFlickerL(B) {
  4171. return new AudioFlickerLClass(B);
  4172. }
  4173.  
  4174. class AudioFlickerClass extends MACRO {
  4175. constructor(A, B) {
  4176. super("Select between A and B based on audio. Higher volumes means more B.", arguments);
  4177. this.add_arg("A","COLOR","A");
  4178. this.add_arg("B","COLOR","B");
  4179. this.SetExpansion(Layers(this.A, AudioFlickerL(this.B)));
  4180. }
  4181. };
  4182.  
  4183. function AudioFlicker(A, B) {
  4184. return new AudioFlickerClass(A, B);
  4185. }
  4186.  
  4187. class RandomFClass extends FUNCTION {
  4188. constructor(A, B) {
  4189. super("Random number 0 - 32768.", arguments);
  4190. }
  4191. run(blade) {
  4192. this.var_ = Math.random() * 32768;;
  4193. }
  4194. getInteger(led) {
  4195. return this.var_;
  4196. }
  4197. };
  4198.  
  4199. function RandomF() {
  4200. return new RandomFClass();
  4201. }
  4202.  
  4203. class RandomLClass extends MACRO {
  4204. constructor(A) {
  4205. super("Selects between A and transparent randomly.", arguments);
  4206. this.add_arg("A", "COLOR", "A");
  4207. this.SetExpansion(AlphaL(A, RandomF()));
  4208. }
  4209. };
  4210.  
  4211. function RandomL(A) {
  4212. return new RandomLClass(A);
  4213. }
  4214.  
  4215. class RandomFlickerClass extends MACRO {
  4216. constructor(A, B) {
  4217. super("Selects between A and B randomly.", arguments);
  4218. this.add_arg("A", "COLOR", "A");
  4219. this.add_arg("B", "COLOR", "B");
  4220. this.SetExpansion(Layers(A, RandomL(B)));
  4221. }
  4222. };
  4223.  
  4224. function RandomFlicker(A, B) {
  4225. return new RandomFlickerClass(A, B);
  4226. }
  4227.  
  4228. class RandomPerLEDFClass extends FUNCTION {
  4229. constructor() {
  4230. super("Returns random 0-32768.", arguments);
  4231. }
  4232. getInteger(led) {
  4233. return random(32768);
  4234. }
  4235. };
  4236.  
  4237. function RandomPerLEDF() {
  4238. return new RandomPerLEDFClass();
  4239. }
  4240.  
  4241. class RandomPerLEDFlickerLClass extends MACRO {
  4242. constructor(A) {
  4243. super("Selects between A and transparent randomly.", arguments);
  4244. this.add_arg("A", "COLOR", "A");
  4245. this.SetExpansion(AlphaL(A, RandomPerLEDF()));
  4246. }
  4247. };
  4248.  
  4249. function RandomPerLEDFlickerL(A) {
  4250. return new RandomPerLEDFlickerLClass(A);
  4251. }
  4252.  
  4253. class RandomPerLEDFlickerClass extends MACRO {
  4254. constructor(A, B) {
  4255. super("Selects between A and B randomly.", arguments);
  4256. this.add_arg("A", "COLOR", "A");
  4257. this.add_arg("B", "COLOR", "B");
  4258. this.SetExpansion(Layers(A, RandomPerLEDFlickerL(B)));
  4259. }
  4260. };
  4261.  
  4262. function RandomPerLEDFlicker(A, B) {
  4263. return new RandomPerLEDFlickerClass(A, B);
  4264. }
  4265.  
  4266. class RemapClass extends STYLE {
  4267. constructor(F, COLOR) {
  4268. super("Remaps the pixels of COLOR based on F", arguments);
  4269. this.add_arg("F", "FUNCTION", "remap function");
  4270. this.add_arg("COLOR", "COLOR", "COLOR");
  4271. }
  4272. run(blade) {
  4273. super.run(blade);
  4274. this.num_leds = blade.num_leds();
  4275. }
  4276. getColor(led) {
  4277. var pos = this.F.getInteger(led);
  4278. var led = clamp(pos * this.num_leds, 0, this.num_leds * 32768 - 1);
  4279. var fraction = led & 0x7fff;
  4280. led = clamp(led >> 15, 0, this.num_leds);
  4281. return this.COLOR.getColor(led).mix(
  4282. this.COLOR.getColor(min(led + 1, this.num_leds - 1)),
  4283. fraction / 32768);
  4284. }
  4285. }
  4286.  
  4287. function Remap(F, COLOR) {
  4288. return new RemapClass(F, COLOR);
  4289. }
  4290.  
  4291. class BrownNoiseFClass extends FUNCTION {
  4292. constructor(grade) {
  4293. super("Randomly return values between 0 and 32768, but keeps nearby values similar", Array.from(arguments));
  4294. this.add_arg("GRADE", "FUNCTION", "grade");
  4295. }
  4296. run(blade) {
  4297. super.run(blade);
  4298. this.mix = Math.floor(Math.random()*32768);
  4299. }
  4300. getInteger(led) {
  4301. var grade = this.GRADE.getInteger(led);
  4302. this.mix += Math.floor(Math.random() * (grade * 2 + 1)) - grade;
  4303. this.mix = clamp(this.mix, 0, 32768);
  4304. return this.mix;
  4305. }
  4306. };
  4307.  
  4308. function BrownNoiseF(grade) {
  4309. return new BrownNoiseFClass(grade);
  4310. }
  4311.  
  4312. class BrownNoiseFlickerLClass extends MACRO {
  4313. constructor(B, GRADE) {
  4314. super("Randomly selects between A and B but keeps nearby pixels similar", Array.from(arguments));
  4315. this.add_arg("B", "COLOR", "B");
  4316. this.add_arg("GRADE", "FUNCTION", "grade");
  4317. this.SetExpansion(AlphaL(B, BrownNoiseF(GRADE)))
  4318. }
  4319. };
  4320.  
  4321. function BrownNoiseFlickerL(B, grade) {
  4322. return new BrownNoiseFlickerLClass(B, grade);
  4323. }
  4324.  
  4325. class BrownNoiseFlickerClass extends MACRO {
  4326. constructor(A, B, GRADE) {
  4327. super("Randomly selects between A and B but keeps nearby pixels similar", Array.from(arguments));
  4328. this.add_arg("A", "COLOR", "A");
  4329. this.add_arg("B", "COLOR", "B");
  4330. this.add_arg("GRADE", "INT", "grade");
  4331. this.SetExpansion(Layers(A, BrownNoiseFlickerL(B, Int(this.GRADE * 128))))
  4332. }
  4333. };
  4334.  
  4335. function BrownNoiseFlicker(A, B, grade) {
  4336. return new BrownNoiseFlickerClass(A, B, grade);
  4337. }
  4338.  
  4339. class HumpFlickerFXClass extends FUNCTION {
  4340. constructor(hump_width) {
  4341. super("Picks a random spot for a bump each frame.", Array.from(arguments));
  4342. this.add_arg("hump_width", "FUNCTION", "Hump width");
  4343. }
  4344. run(blade) {
  4345. super.run(blade);
  4346. this.pos = Math.floor(Math.random() * blade.num_leds());
  4347. }
  4348. getInteger(led) {
  4349. return clamp(Math.abs(led - this.pos) * 32768 / this.hump_width.getInteger(led), 0, 32768);
  4350. }
  4351. };
  4352.  
  4353. function HumpFlickerFX(hump_width) {
  4354. return new HumpFlickerFXClass(hump_width);
  4355. }
  4356.  
  4357. class HumpFlickerFClass extends MACRO {
  4358. constructor(hump_width) {
  4359. super("Picks a random spot for a bump each frame.", Array.from(arguments));
  4360. this.add_arg("hump_width", "INT", "Hump width");
  4361. this.SetExpansion(HumpFlickerFX(Int(hump_width)));
  4362. }
  4363. };
  4364.  
  4365. function HumpFlickerF(hump_width) {
  4366. return new HumpFlickerFClass(hump_width);
  4367. }
  4368.  
  4369. class HumpFlickerLClass extends MACRO {
  4370. constructor(B, hump_width) {
  4371. super("Picks a random spot for a bump each frame.", Array.from(arguments));
  4372. this.add_arg("B", "COLOR", "B");
  4373. this.add_arg("hump_width", "INT", "Hump width");
  4374. this.SetExpansion(AlphaL(B, HumpFlickerF(hump_width)));
  4375. }
  4376. };
  4377.  
  4378. function HumpFlickerL(B, hump_width) {
  4379. return new HumpFlickerLClass(B, hump_width);
  4380. }
  4381.  
  4382. class HumpFlickerClass extends MACRO {
  4383. constructor(A, B, hump_width) {
  4384. super("Picks a random spot for a bump each frame.", Array.from(arguments));
  4385. this.add_arg("A", "COLOR", "A");
  4386. this.add_arg("B", "COLOR", "B");
  4387. this.add_arg("hump_width", "INT", "Hump width");
  4388. this.SetExpansion(Layers(A, HumpFlickerL(B, hump_width)));
  4389. }
  4390. };
  4391.  
  4392. function HumpFlicker(A, B, hump_width) {
  4393. return new HumpFlickerClass(A, B, hump_width);
  4394. }
  4395.  
  4396. class FireConfigClass extends CONFIG {
  4397. constructor(INTENSITY_BASE, INTENSITY_RAND, COOLING) {
  4398. super("Fire configuration", Array.from(arguments));
  4399. this.add_arg("intensity_base", "INT", "intensity base");
  4400. this.add_arg("intensity_rand", "INT", "intensity random");
  4401. this.add_arg("cooling", "INT", "cooling");
  4402. }
  4403. getType() { return "FireConfig"; }
  4404. }
  4405.  
  4406. function FireConfig(B, R, C) {
  4407. return new FireConfigClass(B, R, C);
  4408. }
  4409.  
  4410. function FireConfigI(B, R, C) {
  4411. return new FireConfigClass(new INTEGER(B), new INTEGER(R), new INTEGER(C));
  4412. }
  4413.  
  4414. const FIRE_STATE_OFF = 0
  4415. const FIRE_STATE_ACTIVATING = 1;
  4416. const FIRE_STATE_ON = 2;
  4417.  
  4418. class StyleFireClass extends STYLE {
  4419. constructor(COLOR1, COLOR2, DELAY, SPEED, NORM, CLASH, LOCK, OFF) {
  4420. super("Too complicated to describe briefly", Array.from(arguments));
  4421. this.add_arg("COLOR1", "COLOR", "Warm color");
  4422. this.add_arg("COLOR2", "COLOR", "Hot color");
  4423. this.add_arg("DELAY", "INT", "Delay", 0);
  4424. this.add_arg("SPEED", "INT", "Speed", 2);
  4425. this.add_arg("NORM", "FireConfig", "Config when on", FireConfigI(0, 2000, 5));
  4426. this.add_arg("CLASH", "FireConfig", "Config during clash", FireConfigI(3000, 0, 0));
  4427. this.add_arg("LOCK", "FireConfig", "Config during lockup", FireConfigI(0, 5000, 10));
  4428. this.add_arg("OFF", "FireConfig", "Config when off", FireConfigI(0, 0, this.NORM.cooling.value));
  4429. this.heat = new Uint16Array(144 + 13);
  4430. this.state = FIRE_STATE_OFF;
  4431. this.last_update = 0;
  4432. this.clash_detector_ = new OneshotEffectDetector(EFFECT_CLASH);
  4433. }
  4434. On(blade) {
  4435. if (!blade.is_on()) {
  4436. this.state = FIRE_STATE_OFF;
  4437. return false;
  4438. }
  4439. if (this.state == FIRE_STATE_OFF) {
  4440. this.state = FIRE_STATE_ACTIVATING;
  4441. this.on_time = millis();
  4442. }
  4443. if (this.state = FIRE_STATE_ACTIVATING) {
  4444. if (millis() - this.on_time < this.DELAY) return false;
  4445. this.state = FIRE_STATE_ON;
  4446. }
  4447. return true;
  4448. }
  4449. run(blade) {
  4450. super.run(blade);
  4451. var m = millis();
  4452. if (m - this.last_update < 10)
  4453. return;
  4454. if (m - this.last_update < 40) {
  4455. this.last_update += 10;;
  4456. } else {
  4457. this.last_update = m;
  4458. }
  4459. var num_leds = blade.num_leds();
  4460. this.num_leds = num_leds;
  4461. var conf = this.OFF;
  4462. if (this.clash_detector_.Detect(blade)) {
  4463. conf = this.CLASH;
  4464. } else if (this.On(blade)) {
  4465. if (STATE_LOCKUP == 0) {
  4466. conf = this.NORM;
  4467. } else {
  4468. conf = this.LOCK;
  4469. }
  4470. }
  4471.  
  4472. for (var i = 0; i < this.SPEED; i++) {
  4473. this.heat[num_leds + i] = conf.intensity_base +
  4474. random(random(random(conf.intensity_rand)));
  4475. }
  4476. for (var i = 0; i < num_leds; i++) {
  4477. var x = (this.heat[i + this.SPEED-1] * 3 + this.heat[i + this.SPEED] * 10 + this.heat[i + this.SPEED +1] * 3) >> 4;
  4478. x -= random(conf.cooling);
  4479. this.heat[i] = max(0, min(x, 65535));
  4480. }
  4481. }
  4482. getColor(led) {
  4483. var h = this.heat[this.num_leds - 1 - led];
  4484. if (h < 256) {
  4485. return BLACK.mix(this.COLOR1.getColor(led), h / 255.0);
  4486. } else if (h < 512) {
  4487. return this.COLOR1.getColor(led).mix(this.COLOR2.getColor(led), (h-256)/255.0);
  4488. } else if (h < 768) {
  4489. return this.COLOR2.getColor(led).mix(WHITE, (h - 512) / 255.0);
  4490. } else {
  4491. return WHITE;
  4492. }
  4493. }
  4494. };
  4495.  
  4496. function StyleFire(COLOR1, COLOR2, DELAY, SPEED, NORM, CLASH, LOCK, OFF) {
  4497. return new StyleFireClass(COLOR1, COLOR2, DELAY, SPEED, NORM, CLASH, LOCK, OFF);
  4498. }
  4499.  
  4500. class StaticFireClass extends MACRO {
  4501. constructor(COLOR1, COLOR2, DELAY, SPEED, BASE, RAND, COOLING) {
  4502. super("Non-responsive fire style alias.", Array.from(arguments));
  4503. this.add_arg("COLOR1", "COLOR", "Warm color");
  4504. this.add_arg("COLOR2", "COLOR", "Hot color");
  4505. this.add_arg("DELAY", "INT", "Delay", 0);
  4506. this.add_arg("SPEED", "INT", "Speed", 2);
  4507. this.add_arg("BASE", "INT", "Base", 0);
  4508. this.add_arg("RAND", "INT", "Random", 2000);
  4509. this.add_arg("COOLING", "INT", "Cooling", 5);
  4510. this.SetExpansion(StyleFire(COLOR1, COLOR2, this.DELAY, this.SPEED,
  4511. FireConfig(this.BASE, this.RAND, this.COOLING),
  4512. FireConfig(this.BASE, this.RAND, this.COOLING),
  4513. FireConfig(this.BASE, this.RAND, this.COOLING),
  4514. FireConfig(this.BASE, this.RAND, this.COOLING)));
  4515. }
  4516. };
  4517.  
  4518. function StaticFire(COLOR1, COLOR2, DELAY, SPEED, BASE, RAND, COOLING) {
  4519. return new StaticFireClass(COLOR1, COLOR2, DELAY, SPEED, BASE, RAND, COOLING);
  4520. }
  4521.  
  4522. class RgbCycleClass extends STYLE {
  4523. constructor() {
  4524. super();
  4525. this.n = 0;
  4526. }
  4527. run(blade) {
  4528. this.n++;
  4529. if (this.n >= 3) this.n = 0;
  4530. if (this.n == 0) this.RET = Rgb(255,0,0);
  4531. if (this.n == 1) this.RET = Rgb(0,255,0);
  4532. if (this.n == 2) this.RET = Rgb(0,0,250);
  4533. }
  4534. getColor(led) {
  4535. return this.RET;
  4536. }
  4537. pp() {
  4538. return this.PP("RgbCycle", "alternates betwen red, green and blue.");
  4539. }
  4540. };
  4541.  
  4542. function RgbCycle() {
  4543. return new RgbCycleClass();
  4544. }
  4545.  
  4546. function AddBlast() {
  4547. blade.addEffect(EFFECT_BLAST, Math.random() * 0.7 + 0.2);
  4548. }
  4549. function AddForce() {
  4550. blade.addEffect(EFFECT_FORCE, Math.random() * 0.7 + 0.2);
  4551. }
  4552. var current_clash_value = 0;
  4553. var current_clash_strength = 0;
  4554. function AddClash() {
  4555. current_clash_value = 200 + random(1600);
  4556. current_clash_strength = 100 + random(current_clash_value);
  4557. blade.addEffect(EFFECT_CLASH, Math.random() * 0.7 + 0.2);
  4558. }
  4559. function AddStab() {
  4560. blade.addEffect(EFFECT_STAB, 1.0);
  4561. }
  4562. function AddNewfont() {
  4563. blade.addEffect(EFFECT_NEWFONT, Math.random() * 0.7 + 0.2);
  4564. }
  4565. function AddBoot() {
  4566. blade.addEffect(EFFECT_BOOT, Math.random() * 0.7 + 0.2);
  4567. }
  4568. function AddPreon() {
  4569. blade.addEffect(EFFECT_PREON, 0.0);
  4570. }
  4571.  
  4572. var blast_hump = [ 255,255,252,247,240,232,222,211,
  4573. 199,186,173,159,145,132,119,106,
  4574. 94,82,72,62,53,45,38,32,
  4575. 26,22,18,14,11,9,7,5,0 ];
  4576.  
  4577. class BlastFClass extends FUNCTION {
  4578. constructor(FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT_ARG) {
  4579. super("Blast effect function", Array.from(arguments));
  4580. this.add_arg("FADEOUT_MS", "INT", "fadeout time in milliseconds", 200);
  4581. this.add_arg("WAVE_SIZE", "INT", "wave size", 100);
  4582. this.add_arg("WAVE_MS", "INT", "wave speed", 400);
  4583. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4584. }
  4585. run(blade) {
  4586. this.T = micros();
  4587. this.num_leds_ = 1.0 * blade.num_leds();
  4588. this.effects_ = blade.GetEffects();
  4589. }
  4590. getInteger(led) {
  4591. var b = 0.0;
  4592. for (var i = 0; i < this.effects_.length; i++) {
  4593. if (this.effects_[i].type != this.EFFECT) continue;
  4594. var T = (this.T - this.effects_[i].start_micros);
  4595. var M = 1000 - T / this.FADEOUT_MS;
  4596. if (M > 0) {
  4597. var dist = Math.abs(this.effects_[i].location - led / this.num_leds_);
  4598. var N = Math.floor(Math.abs(dist - T / (this.WAVE_MS * 1000.0)) * this.WAVE_SIZE);
  4599. if (N < 32) {
  4600. b += blast_hump[N] * M / 1000.0 / 255.0;
  4601. }
  4602. }
  4603. }
  4604. return clamp(b * 32768, 0, 32768);
  4605. }
  4606. };
  4607.  
  4608. function BlastF(FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT) {
  4609. return new BlastFClass(FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT);
  4610. }
  4611.  
  4612. class BlastLClass extends MACRO {
  4613. constructor(BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT_ARG) {
  4614. super("Blast layer", Array.from(arguments));
  4615. this.add_arg("BLAST", "COLOR", "blast color");
  4616. this.add_arg("FADEOUT_MS", "INT", "fadeout time in milliseconds", 200);
  4617. this.add_arg("WAVE_SIZE", "INT", "wave size", 100);
  4618. this.add_arg("WAVE_MS", "INT", "wave speed", 400);
  4619. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4620. this.SetExpansion(AlphaL(BLAST, BlastF(FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT_ARG)));
  4621. }
  4622. argify(state) {
  4623. state.color_argument = BLAST_COLOR_ARG;
  4624. var ret = super.argify(state);
  4625. state.color_argument = null;
  4626. return ret;
  4627. }
  4628. };
  4629.  
  4630. function BlastL(BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT) {
  4631. return new BlastLClass(BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT);
  4632. }
  4633.  
  4634. class BlastClass extends MACRO {
  4635. constructor(BASE, BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT_ARG) {
  4636. super("Blast effect", Array.from(arguments));
  4637. this.add_arg("BASE", "COLOR", "base color");
  4638. this.add_arg("BLAST", "COLOR", "blast color");
  4639. this.add_arg("FADEOUT_MS", "INT", "fadeout time in milliseconds", 200);
  4640. this.add_arg("WAVE_SIZE", "INT", "wave size", 100);
  4641. this.add_arg("WAVE_MS", "INT", "wave speed", 400);
  4642. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4643. this.SetExpansion(Layers(BASE, BlastL(BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT_ARG)));
  4644. }
  4645. };
  4646.  
  4647. function Blast(BASE, BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT) {
  4648. return new BlastClass(BASE, BLAST, FADEOUT_MS, WAVE_SIZE, WAVE_MS, EFFECT);
  4649. }
  4650.  
  4651. class BlastFadeoutFClass extends FUNCTION {
  4652. constructor(FADEOUT_MS, EFFECT_ARG) {
  4653. super("Fadeout on blast function", Array.from(arguments));
  4654. this.add_arg("FADEOUT_MS", "INT", "fadeout time in milliseconds", 200);
  4655. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4656. }
  4657. run(blade) {
  4658. super.run(blade);
  4659. this.T = micros();
  4660. this.effects_ = blade.GetEffects();
  4661. }
  4662. getInteger(led) {
  4663. var b = 0.0;
  4664. for (var i = 0; i < this.effects_.length; i++) {
  4665. if (this.effects_[i].type != this.EFFECT) continue;
  4666. var T = (this.T - this.effects_[i].start_micros);
  4667. var M = 1000 - T / this.FADEOUT_MS;
  4668. if (M > 0) {
  4669. b += M / 1000.0;
  4670. }
  4671. }
  4672. return clamp(b * 32768.0, 0, 32768.0);
  4673. }
  4674. };
  4675.  
  4676. function BlastFadeoutF(FADEOUT_MS, EFFECT) {
  4677. return new BlastFadeoutFClass(FADEOUT_MS, EFFECT);
  4678. }
  4679.  
  4680.  
  4681. class BlastFadeoutLClass extends MACRO {
  4682. constructor(BLAST, FADEOUT_MS, EFFECT_ARG) {
  4683. super("BlastFadeout layers", Array.from(arguments));
  4684. this.add_arg("BLAST", "COLOR", "blast color");
  4685. this.add_arg("FADEOUT_MS", "INT", "fadeout time in milliseconds", 200);
  4686. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4687. this.SetExpansion(AlphaL(BLAST, BlastFadeoutF(FADEOUT_MS, EFFECT_ARG)));
  4688. }
  4689. };
  4690.  
  4691. function BlastFadeoutL(BLAST, FADEOUT_MS, EFFECT) {
  4692. return new BlastFadeoutLClass(BLAST, FADEOUT_MS, EFFECT);
  4693. }
  4694.  
  4695.  
  4696. class BlastFadeoutClass extends MACRO {
  4697. constructor(BASE, BLAST, FADEOUT_MS, EFFECT_ARG) {
  4698. super("BlastFadeout effect", Array.from(arguments));
  4699. this.add_arg("BASE", "COLOR", "base color");
  4700. this.add_arg("BLAST", "COLOR", "blast color");
  4701. this.add_arg("FADEOUT_MS", "INT", "fadeout time in milliseconds", 200);
  4702. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4703. this.SetExpansion(Layers(BASE, BlastFadeoutL(BLAST, FADEOUT_MS, EFFECT_ARG)));
  4704. }
  4705. };
  4706.  
  4707. function BlastFadeout(BASE, BLAST, FADEOUT_MS, EFFECT) {
  4708. return new BlastFadeoutClass(BASE, BLAST, FADEOUT_MS, EFFECT);
  4709. }
  4710.  
  4711. class OriginalBlastFClass extends FUNCTION {
  4712. constructor(BASE, BLAST, EFFECT_ARG) {
  4713. super("Original blast effect", Array.from(arguments));
  4714. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4715. }
  4716. run(blade) {
  4717. super.run(blade);
  4718. this.T = micros();
  4719. this.num_leds_ = 1.0 * blade.num_leds();
  4720. this.effects_ = blade.GetEffects();
  4721. }
  4722. getInteger(led) {
  4723. var b = 0.0;
  4724. for (var i = 0; i < this.effects_.length; i++) {
  4725. if (this.effects_[i].type != this.EFFECT) continue;
  4726. var x = (this.effects_[i].location - led/this.num_leds_) * 30.0;
  4727. var T = (this.T - this.effects_[i].start_micros);
  4728. var t = 0.5 + T / 200000.0;
  4729. if (x == 0.0) {
  4730. b += 1.0 / (t * t);
  4731. } else {
  4732. b += sin(x / (t*t)) / x;
  4733. }
  4734. }
  4735. return min(b, 1.0) * 32768;
  4736. }
  4737. };
  4738.  
  4739. function OriginalBlastF(EFFECT) {
  4740. return new OriginalBlastFClass(EFFECT);
  4741. }
  4742.  
  4743. class OriginalBlastLClass extends MACRO {
  4744. constructor(BLAST, EFFECT_ARG) {
  4745. super("Original blast effect", Array.from(arguments));
  4746. this.add_arg("BLAST", "COLOR", "blast color");
  4747. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4748. this.SetExpansion(AlphaL(BLAST, OriginalBlastF(this.EFFECT)));
  4749. }
  4750. };
  4751.  
  4752. function OriginalBlastL(BLAST, EFFECT) {
  4753. return new OriginalBlastLClass(BLAST, EFFECT);
  4754. }
  4755.  
  4756. class OriginalBlastClass extends MACRO {
  4757. constructor(BASE, BLAST, EFFECT_ARG) {
  4758. super("Original blast effect", Array.from(arguments));
  4759. this.add_arg("BASE", "COLOR", "base color");
  4760. this.add_arg("BLAST", "COLOR", "blast color");
  4761. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  4762. this.SetExpansion(Layers(BASE, OriginalBlastL(BLAST, this.EFFECT)));
  4763. }
  4764. };
  4765.  
  4766. function OriginalBlast(BASE, BLAST, EFFECT) {
  4767. return new OriginalBlastClass(BASE, BLAST, EFFECT);
  4768. }
  4769.  
  4770. class BlinkingFClass extends FUNCTION {
  4771. constructor(BLINK_MILLIS, BLINK_PROMILLE) {
  4772. super("Blinks between 0 and 32768", Array.from(arguments));
  4773. this.add_arg("BLINK_MILLIS", "FUNCTION", "milliseconds between blinks");
  4774. this.add_arg("BLINK_PROMILLE", "FUNCTION", "0 = off, 1000 = on");
  4775. this.on_ = false;
  4776. this.pulse_start_micros_ = 0;
  4777. }
  4778. run(blade) {
  4779. super.run(blade);
  4780. var now = micros();
  4781. var pulse_millis = this.BLINK_MILLIS.getInteger(0);
  4782. if (pulse_millis <= 0) return;
  4783. var pulse_progress_micros = now - this.pulse_start_micros_;
  4784. if (pulse_progress_micros > pulse_millis * 1000) {
  4785. // Time to start a new pulse
  4786. if (pulse_progress_micros < pulse_millis * 2000) {
  4787. this.pulse_start_micros_ += pulse_millis * 1000;
  4788. } else {
  4789. this.pulse_start_micros_ = now;
  4790. }
  4791. pulse_progress_micros = now - this.pulse_start_micros_;
  4792. }
  4793. var pulse_progress_promille = pulse_progress_micros / pulse_millis;
  4794. this.value_ = pulse_progress_promille <= this.BLINK_PROMILLE.getInteger(0) ? 0 : 32768;
  4795. }
  4796. getInteger(led) {
  4797. return this.value_;
  4798. }
  4799. };
  4800.  
  4801. function BlinkingF(BM, BP) {
  4802. return new BlinkingFClass(BM, BP);
  4803. }
  4804.  
  4805. class BlinkingLClass extends MACRO {
  4806. constructor(COLOR, BLINK_MILLIS, BLINK_PROMILLE) {
  4807. super("Blinks transparent/opaque COLOR", Array.from(arguments));
  4808. this.add_arg("COLOR", "COLOR", "COLOR");
  4809. this.add_arg("BLINK_MILLIS", "FUNCTION", "milliseconds between blinks");
  4810. this.add_arg("BLINK_PROMILLE", "FUNCTION", "0 = off, 1000 = on");
  4811. this.SetExpansion(AlphaL(COLOR, BlinkingF(BLINK_MILLIS, BLINK_PROMILLE)));
  4812. }
  4813. };
  4814.  
  4815. function BlinkingL(A, B, BM, BP) {
  4816. return new BlinkingLClass(A, B, BM, BP);
  4817. }
  4818.  
  4819. class BlinkingXClass extends MACRO {
  4820. constructor(COLOR1, COLOR2, BLINK_MILLIS, BLINK_PROMILLE) {
  4821. super("Blinks between A and B", Array.from(arguments));
  4822. this.add_arg("COLOR1", "COLOR", "A");
  4823. this.add_arg("COLOR2", "COLOR", "B");
  4824. this.add_arg("BLINK_MILLIS", "FUNCTION", "milliseconds between blinks");
  4825. this.add_arg("BLINK_PROMILLE", "FUNCTION", "0 = off, 1000 = on");
  4826. this.SetExpansion(Layers(COLOR1, BlinkingL(COLOR2, BLINK_MILLIS, BLINK_PROMILLE)));
  4827. }
  4828. };
  4829.  
  4830. function BlinkingX(A, B, BM, BP) {
  4831. return new BlinkingXClass(A, B, BM, BP);
  4832. }
  4833.  
  4834. class BlinkingClass extends MACRO {
  4835. constructor(COLOR1, COLOR2, BLINK_MILLIS, BLINK_PROMILLE) {
  4836. super("Blinks between A and B", Array.from(arguments));
  4837. this.add_arg("COLOR1", "COLOR", "A");
  4838. this.add_arg("COLOR2", "COLOR", "B");
  4839. this.add_arg("BLINK_MILLIS", "INT", "milliseconds between blinks");
  4840. this.add_arg("BLINK_PROMILLE", "INT", "0 = off, 1000 = on");
  4841. this.SetExpansion(BlinkingX(COLOR1, COLOR2, Int(BLINK_MILLIS), Int(BLINK_PROMILLE)));
  4842. }
  4843. };
  4844.  
  4845. function Blinking(A, B, BM, BP) {
  4846. return new BlinkingClass(A, B, BM, BP);
  4847. }
  4848.  
  4849. class SimpleClashLClass extends STYLE {
  4850. constructor(T, CLASH, CLASH_MILLIS, EFFECT_ARG, STAB_SHAPE) {
  4851. super("Implements the clash effect", Array.from(arguments));
  4852. this.add_arg("CLASH", "COLOR", "Clash color");
  4853. this.add_arg("CLASH_MILLIS", "INT", "How many MS to show the clash color for.", 40);
  4854. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_CLASH));
  4855. this.add_arg("STAB_SHAPE", "FUNCTION", "Stab shape", SmoothStep(Int(16384), Int(24000)));
  4856. this.effect_ = new OneshotEffectDetector(this.EFFECT);
  4857. this.clash_ = false;
  4858. this.stab_ = false;
  4859. }
  4860. run(blade) {
  4861. super.run(blade);
  4862.  
  4863. if (this.clash_ && micros() - this.effect_.last_detected_ > this.CLASH_MILLIS * 1000) {
  4864. this.clash_ = false;
  4865. }
  4866. var e = this.effect_.Detect(blade);
  4867. if (e) {
  4868. this.clash_ = true;
  4869. this.stab_ = this.EFFECT == EFFECT_CLASH && e.type == EFFECT_STAB && blade.num_leds() > 1;
  4870. }
  4871. }
  4872. getColor(led) {
  4873. var ret = Transparent();
  4874. if (this.clash_) {
  4875. var ret = this.CLASH.getColor(led);
  4876. if (this.stab_) {
  4877. ret = ret.multiply(this.STAB_SHAPE.getInteger(led) / 32768.0);
  4878. }
  4879. }
  4880. return ret;
  4881. }
  4882. IS_RUNNING() {
  4883. return this.clash_;
  4884. }
  4885. argify(state) {
  4886. state.color_argument = effect_to_argument(this.EFFECT);
  4887. console.log("STATE IN SIMPLECLASHL:");
  4888. console.log(state);
  4889. var ret = super.argify(state);
  4890. state.color_argument = null;
  4891. return ret;
  4892. }
  4893. };
  4894.  
  4895. function SimpleClashL(T, CLASH, MILLIS, EF, SS) {
  4896. return new SimpleClashLClass(T, CLASH, MILLIS, EF, SS);
  4897. }
  4898.  
  4899. class SimpleClashClass extends MACRO {
  4900. constructor(T, CLASH, CLASH_MILLIS, EFFECT_ARG, STAB_SHAPE) {
  4901. super("Implements the clash effect", Array.from(arguments));
  4902. this.add_arg("T", "COLOR", "base color");
  4903. this.add_arg("CLASH", "COLOR", "Clash color");
  4904. this.add_arg("CLASH_MILLIS", "INT", "How many MS to show the clash color for.", 40);
  4905. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_CLASH));
  4906. this.add_arg("STAB_SHAPE", "FUNCTION", "Stab shape", SmoothStep(Int(16384), Int(24000)));
  4907. this.SetExpansion(Layers(T, SimpleClashL(CLASH, this.CLASH_MILLIS, this.EFFECT, this.STAB_SHAPE)));
  4908. }
  4909. };
  4910.  
  4911. function SimpleClash(T, CLASH, MILLIS, EF, SS) {
  4912. return new SimpleClashClass(T, CLASH, MILLIS, EF, SS);
  4913. }
  4914.  
  4915.  
  4916. class LocalizedClashLClass extends STYLE {
  4917. constructor(CLASH_COLOR, CLASH_MILLIS, CLASH_WIDTH_PERCENT, EFFECT_ARG) {
  4918. super("Localized clash", arguments);
  4919. this.add_arg("CLASH_COLOR", "COLOR", "Clash color", WHITE.DOCOPY());
  4920. this.add_arg("CLASH_MILLIS", "INT", "Clash duration in milliseconds", 40);
  4921. this.add_arg("CLASH_WIDTH_PERCENT", "INT", "Clash width in percent of entire blade", 50);
  4922. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_CLASH));
  4923. this.effect_ = new OneshotEffectDetector(this.EFFECT);
  4924. }
  4925. run(blade) {
  4926. super.run(blade);
  4927.  
  4928. var m = millis();
  4929. var clashing = 0;
  4930. var e = this.effect_.Detect(blade);
  4931. if (e) {
  4932. this.clash = true;
  4933. this.mult = blast_hump.length * 2 * 102400 / this.CLASH_WIDTH_PERCENT / blade.num_leds();
  4934. this.clash_location = e.location * blade.num_leds() * this.mult;
  4935. } else if (micros() - this.effect_.last_detected_ < this.CLASH_MILLIS.getInteger(0) * 1000) {
  4936. this.clash = true;
  4937. } else {
  4938. this.clash = false;
  4939. }
  4940. }
  4941. getColor(led) {
  4942. var ret = Transparent();
  4943. if (this.clash) {
  4944. var dist = Math.floor(Math.abs(led * this.mult - this.clash_location) / 1024);
  4945. if (dist < blast_hump.length) {
  4946. var ret = this.CLASH_COLOR.getColor(led);
  4947. ret = ret.multiply(blast_hump[dist] / 255.0);
  4948. }
  4949. }
  4950. return ret;
  4951. }
  4952. IS_RUNNING() {
  4953. return this.clash;
  4954. }
  4955. argify(state) {
  4956. state.color_argument = effect_to_argument(this.EFFECT);
  4957. var ret = super.argify(state);
  4958. state.color_argument = null;
  4959. return ret;
  4960. }
  4961. }
  4962.  
  4963. function LocalizedClashL(CLASH_COLOR, CLASH_MILLIS, CLASH_WIDTH_PERCENT, EF) {
  4964. return new LocalizedClashLClass(CLASH_COLOR, CLASH_MILLIS, CLASH_WIDTH_PERCENT, EF);
  4965. }
  4966.  
  4967. class LocalizedClashClass extends MACRO {
  4968. constructor(T, CLASH_COLOR, CLASH_MILLIS, CLASH_WIDTH_PERCENT, EFFECT_ARG) {
  4969. super("Localized clash", arguments);
  4970. this.add_arg("T", "COLOR", "base color");
  4971. this.add_arg("CLASH_COLOR", "COLOR", "Clash color", WHITE.DOCOPY());
  4972. this.add_arg("CLASH_MILLIS", "INT", "Clash duration in milliseconds", 40);
  4973. this.add_arg("CLASH_WIDTH_PERCENT", "INT", "Clash width in percent of entire blade", 50);
  4974. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_CLASH));
  4975. this.SetExpansion(Layers(T, LocalizedClashL(this.CLASH_COLOR, this.CLASH_MILLIS, this.CLASH_WIDTH_PERCENT, this.EFFECT)));
  4976. }
  4977. }
  4978.  
  4979. function LocalizedClash(T, CLASH_COLOR, CLASH_MILLIS, CLASH_WIDTH_PERCENT, EF) {
  4980. return new LocalizedClashClass(T, CLASH_COLOR, CLASH_MILLIS, CLASH_WIDTH_PERCENT, EF);
  4981. }
  4982.  
  4983. class LockupLClass extends STYLE {
  4984. isEffect() { return true; }
  4985. constructor(LOCKUP, DRAG_COLOR, LOCKUP_SHAPE, DRAG_SHAPE, LB_SHAPE) {
  4986. super("Implements the lockup and drag effects.", arguments);
  4987. this.add_arg("LOCKUP", "COLOR", "lockup color");
  4988. this.add_arg("DRAG_COLOR", "COLOR", "drag color", this.LOCKUP.DOCOPY());
  4989. this.add_arg("LOCKUP_SHAPE", "FUNCTION", "Lockup shape", Int(32768));
  4990. this.add_arg("DRAG_SHAPE", "FUNCTION", "Drag shape", SmoothStep(Int(28671), Int(4096)));
  4991. this.add_arg("LB_SHAPE", "FUNCTION", "Lightning block shape",
  4992. LayerFunctions(Bump(Scale(SlowNoise(Int(2000)),Int(3000),Int(16000)),
  4993. Scale(BrownNoiseF(Int(10)),Int(14000),Int(8000))),
  4994. Bump(Scale(SlowNoise(Int(2300)),Int(26000),Int(8000)),
  4995. Scale(NoisySoundLevel(),Int(5000),Int(10000))),
  4996. Bump(Scale(SlowNoise(Int(2300)),Int(20000),Int(30000)),
  4997. Scale(IsLessThan(SlowNoise(Int(1500)),Int(8000)),Scale(NoisySoundLevel(),Int(5000),Int(0)),Int(0)))));
  4998. }
  4999. run(blade) {
  5000. super.run(blade);
  5001. this.single_pixel_ = blade.num_leds() == 1;
  5002. this.handled = IsHandledLockup(STATE_LOCKUP);
  5003. }
  5004. getColor(led) {
  5005. var ret = Transparent();
  5006. if (this.handled) return ret;
  5007. if (STATE_LOCKUP == LOCKUP_LIGHTNING_BLOCK) {
  5008. ret = ret.mix(this.LOCKUP.getColor(led), this.LB_SHAPE.getInteger(led) / 32768.0);
  5009. }
  5010. if (STATE_LOCKUP == LOCKUP_DRAG) {
  5011. var blend = this.single_pixel_ ? 32768 : this.DRAG_SHAPE.getInteger(led);
  5012. ret = ret.mix(this.DRAG_COLOR.getColor(led), blend / 32768.0);
  5013. }
  5014. if (STATE_LOCKUP == LOCKUP_NORMAL) {
  5015. ret = ret.mix(this.LOCKUP.getColor(led), this.LOCKUP_SHAPE.getInteger(led) / 32768.0);
  5016. }
  5017. return ret;
  5018. }
  5019. IS_RUNNING() {
  5020. if (this.handled) return false;
  5021. if (STATE_LOCKUP == LOCKUP_LIGHTNING_BLOCK) true;
  5022. if (STATE_LOCKUP == LOCKUP_DRAG) return true;
  5023. if (STATE_LOCKUP == LOCKUP_NORMAL) return true;
  5024. return false;
  5025. }
  5026. argify(state) {
  5027. state.color_argument = LOCKUP_COLOR_ARG;
  5028. this.LOCKUP = this.LOCKUP.argify(state);
  5029. state.color_argument = DRAG_COLOR_ARG;
  5030. this.DRAG_COLOR = this.DRAG_COLOR.argify(state);
  5031. state.color_argument = null;
  5032. return this;
  5033. }
  5034. };
  5035.  
  5036. function LockupL(LOCKUP, DRAG, LOCKUP_SHAPE, DRAG_SHAPE, LB_SHAPE) {
  5037. return new LockupLClass(LOCKUP, DRAG, LOCKUP_SHAPE, DRAG_SHAPE, LB_SHAPE);
  5038. }
  5039.  
  5040. class LockupClass extends MACRO {
  5041. constructor(BASE, LOCKUP, DRAG_COLOR, LOCKUP_SHAPE, DRAG_SHAPE) {
  5042. super("Implements the lockup and drag effects.", arguments);
  5043. this.add_arg("BASE", "COLOR", "base color");
  5044. this.add_arg("LOCKUP", "COLOR", "lockup color");
  5045. this.add_arg("DRAG_COLOR", "COLOR", "drag color", this.LOCKUP.DOCOPY());
  5046. this.add_arg("LOCKUP_SHAPE", "FUNCTION", "Lockup shape", Int(32768));
  5047. this.add_arg("DRAG_SHAPE", "FUNCTION", "Drag shape", SmoothStep(Int(28671), Int(4096)));
  5048. this.SetExpansion(Layers(BASE, LockupL(LOCKUP, DRAG_COLOR, LOCKUP_SHAPE, DRAG_SHAPE)));
  5049. }
  5050. };
  5051.  
  5052. function Lockup(BASE, LOCKUP, DRAG, LOCKUP_SHAPE, DRAG_SHAPE) {
  5053. return new LockupClass(BASE, LOCKUP, DRAG, LOCKUP_SHAPE, DRAG_SHAPE);
  5054. }
  5055.  
  5056.  
  5057. class LockupTrLClass extends STYLE {
  5058. constructor(COLOR, BeginTr, EndTr, LOCKUP_TYPE) {
  5059. super("Transition based lockup effect.", arguments);
  5060. this.add_arg("COLOR", "COLOR", "Effect color.");
  5061. this.add_arg("BEGIN_TR", "TRANSITION", "Begin lockup transition.");
  5062. this.add_arg("END_TR", "TRANSITION", "End lockup transition.");
  5063. this.add_arg("LOCKUP_TYPE", "LOCKUP_TYPE", "Lockup type");
  5064. this.add_arg("CONDITION", "FUNCTION", "Lockup is postponed if conditition is zero.", Int(1));
  5065. HandleLockup(LOCKUP_TYPE);
  5066. this.active = "inactive";
  5067. this.begin_active = false;
  5068. this.end_active = false;
  5069. }
  5070. run(blade) {
  5071. super.run(blade);
  5072. switch (this.active) {
  5073. case "inactive":
  5074. if (STATE_LOCKUP == this.LOCKUP_TYPE) {
  5075. if (this.CONDITION.getInteger(0)) {
  5076. this.active = "active";
  5077. this.BEGIN_TR.begin();
  5078. this.begin_active = true;
  5079. } else {
  5080. this.active = "skipped";
  5081. }
  5082. }
  5083. break;
  5084. case "active":
  5085. if (STATE_LOCKUP != this.LOCKUP_TYPE) {
  5086. this.END_TR.begin();
  5087. this.end_active = true;
  5088. this.active = "inactive";
  5089. }
  5090. break;
  5091. case "skipped":
  5092. if (STATE_LOCKUP != this.LOCKUP_TYPE) {
  5093. this.active = "inactive";
  5094. }
  5095. break;
  5096. }
  5097.  
  5098.  
  5099. if (this.begin_active) {
  5100. this.BEGIN_TR.run(blade);
  5101. if (this.BEGIN_TR.done()) this.begin_active = false;
  5102. }
  5103. if (this.end_active) {
  5104. this.END_TR.run(blade);
  5105. if (this.END_TR.done()) this.end_active = false;
  5106. }
  5107. }
  5108. runBegin(a, b, led) {
  5109. if (this.begin_active) {
  5110. return this.BEGIN_TR.getColor(a, b, led);
  5111. } else {
  5112. return b;
  5113. }
  5114. }
  5115. runEnd(a, b, led) {
  5116. if (this.end_active) {
  5117. return this.END_TR.getColor(a, b, led);
  5118. } else {
  5119. return b;
  5120. }
  5121. }
  5122. getColor(led) {
  5123. var off_color = Transparent();
  5124. if (!this.begin_active && !this.end_active) {
  5125. if (this.active == "active") {
  5126. return this.COLOR.getColor(led);
  5127. } else {
  5128. return off_color;
  5129. }
  5130. } else {
  5131. var on_color = this.COLOR.getColor(led);
  5132. if (this.active == "active") {
  5133. return this.runBegin(this.runEnd(on_color, off_color, led), on_color, led);
  5134. } else {
  5135. return this.runEnd(this.runBegin(off_color, on_color, led), off_color, led);
  5136. }
  5137. }
  5138. }
  5139. IS_RUNNING() {
  5140. return this.active == "active";
  5141. }
  5142. argify(state) {
  5143. state.color_argument = lockup_to_argument(this.LOCKUP_TYPE);
  5144. var ret = super.argify(state);
  5145. state.color_argument = null;
  5146. return ret;
  5147. }
  5148. };
  5149.  
  5150. function LockupTrL(COLOR, BeginTr, EndTr, LOCKUP_TYPE, CONDITION) {
  5151. return new LockupTrLClass(COLOR, BeginTr, EndTr, LOCKUP_TYPE, CONDITION);
  5152. }
  5153.  
  5154. class LockupTrClass extends MACRO {
  5155. constructor(BASE, COLOR, BeginTr, EndTr, LOCKUP_TYPE, CONDITION) {
  5156. super("Transition based lockup effect.", arguments);
  5157. this.add_arg("BASE", "COLOR", "Base color.");
  5158. this.add_arg("COLOR", "COLOR", "Effect color.");
  5159. this.add_arg("BEGIN_TR", "TRANSITION", "Begin lockup transition.");
  5160. this.add_arg("END_TR", "TRANSITION", "End lockup transition.");
  5161. this.add_arg("LOCKUP_TYPE", "LOCKUP_TYPE", "Lockup type");
  5162. this.add_arg("CONDITION", "FUNCTION", "Lockup is postponed if conditition is zero.", Int(1));
  5163. this.SetExpansion(Layers(BASE, LockupTrL(COLOR, BeginTr, EndTr, LOCKUP_TYPE, this.CONDITITION)));
  5164. }
  5165. argify(state) {
  5166. state.color_argument = lockup_to_argument(this.LOCKUP_TYPE);
  5167. var ret = super.argify(state);
  5168. state.color_argument = null;
  5169. return ret;
  5170. }
  5171. }
  5172.  
  5173. function LockupTr(BASE, COLOR, BeginTr, EndTr, LOCKUP_TYPE, CONDITION) {
  5174. return new LockupTrClass(BASE, COLOR, BeginTr, EndTr, LOCKUP_TYPE, CONDITION);
  5175. }
  5176.  
  5177. const start_millis = new Date().getTime();
  5178. function actual_millis() {
  5179. return new Date().getTime() - start_millis;
  5180. }
  5181. var current_micros = 0;
  5182. var current_micros_internal = 0;
  5183. function micros() {
  5184. return current_micros;
  5185. }
  5186.  
  5187. function millis() {
  5188. return current_micros / 1000;
  5189. }
  5190.  
  5191. function fract(v) {
  5192. return v - Math.floor(v);
  5193. }
  5194.  
  5195. function FIND(id) {
  5196. ret = document.getElementById(id);
  5197. if (!ret) {
  5198. // console.log("Failed to find " + id);
  5199. }
  5200. return ret;
  5201. }
  5202.  
  5203. var max = Math.max;
  5204. var min = Math.min;
  5205. var sin = Math.sin;
  5206. function random(i) {
  5207. return Math.floor(Math.random() * i);
  5208. }
  5209. function clamp(a, b, c) {
  5210. if (a < b) return b;
  5211. if (a > c) return c;
  5212. return a;
  5213. }
  5214.  
  5215. class Blade {
  5216. constructor() {
  5217. this.effects_ = [];
  5218. }
  5219. is_on() {
  5220. return STATE_ON;
  5221. }
  5222. num_leds() {
  5223. return STATE_NUM_LEDS;
  5224. }
  5225. addEffect(type, location) {
  5226. console.log("Add effect " + type + " @ " + location);
  5227. this.effects_.push(new BladeEffect(type, micros(), location));
  5228. }
  5229. GetEffects() {
  5230. while (this.effects_.length > 0 && micros() - this.effects_[0].start_micros >= 5000000) {
  5231. this.effects_.shift();
  5232. }
  5233. return this.effects_;
  5234. }
  5235. };
  5236.  
  5237. var last_detected_blade_effect;
  5238.  
  5239. var handled_types = {};
  5240. function PushHandledTypes() {
  5241. ret = [ handled_types, handled_lockups ];
  5242. handled_types = {};
  5243. handled_lockups = {};
  5244. return ret;
  5245. }
  5246. function PopHandledTypes(old) {
  5247. handled_types = old[0];
  5248. handled_lockups = old[1];
  5249. }
  5250. function HandleEffectType(t) {
  5251. if (t.getInteger) t = t.getInteger(0);
  5252. handled_types[t] = 1;
  5253. }
  5254. function IsHandledEffectType(t) {
  5255. return current_style.__handled_types[EFFECT_STAB];
  5256. }
  5257.  
  5258. class OneshotEffectDetector {
  5259. constructor(type) {
  5260. this.last_detected_ = 0;
  5261. if (type.getInteger) {
  5262. type = type.getInteger(0);
  5263. }
  5264. this.type_ = type;
  5265. HandleEffectType(type);
  5266. }
  5267. Detect(blade) {
  5268. var mask = {};
  5269. mask[this.type_] = 1;
  5270. if (this.type_ == EFFECT_CLASH && !(current_style.__handled_types[EFFECT_STAB])) {
  5271. mask[EFFECT_STAB] = 1;
  5272. }
  5273.  
  5274. var effects = blade.GetEffects();
  5275. for (var i = effects.length -1 ; i >=0 ; i--) {
  5276. if (mask[effects[i].type]) {
  5277. if (effects[i].start_micros == this.last_detected_)
  5278. return 0;
  5279. this.last_detected_ = effects[i].start_micros;
  5280. last_detected_blade_effect = effects[i];
  5281. return effects[i];
  5282. }
  5283. }
  5284. return 0;
  5285. }
  5286. getDetected(blade) {
  5287. var mask = {};
  5288. mask[this.type_] = 1;
  5289. var effects = blade.GetEffects();
  5290. for (var i = effects.length -1 ; i >=0 ; i--)
  5291. if (mask[effects[i].type])
  5292. if (effects[i].start_micros == this.last_detected_)
  5293. return effects[i];
  5294. return 0;
  5295. }
  5296. };
  5297.  
  5298. var focus_catcher;
  5299. var focus_trace = [undefined];
  5300.  
  5301. function Focus(T) {
  5302. console.log("FOCUS=" + T);
  5303. console.log(T);
  5304. focus_catcher = T;
  5305. focus_trace = [T];
  5306. return T;
  5307. }
  5308.  
  5309. function StylePtr(T) {
  5310. return T;
  5311. }
  5312.  
  5313. class EasyBladeClass extends MACRO {
  5314. constructor(COLOR, CLASH_COLOR, LOCKUP_FLICKER_COLOR) {
  5315. super("Adds clash/lockup/blast/drag", arguments);
  5316. this.add_arg("COLOR","COLOR","Main color");
  5317. this.add_arg("CLASH_COLOR", "COLOR", "Clash color");
  5318. this.add_arg("LOCKUP_FLICKER_COLOR", "COLOR", "lockup flicker color", WHITE.DOCOPY());
  5319.  
  5320. this.SetExpansion(
  5321. SimpleClash(Lockup(Blast(this.COLOR, WHITE.DOCOPY()), AudioFlicker(this.COLOR.DOCOPY(), this.LOCKUP_FLICKER_COLOR)), this.CLASH_COLOR)
  5322. );
  5323. }
  5324. };
  5325.  
  5326. function EasyBlade(color, clash_color, lockup_flicker_color) {
  5327. return new EasyBladeClass(color, clash_color, lockup_flicker_color);
  5328. }
  5329.  
  5330. class StyleNormalPtrClass extends MACRO {
  5331. constructor(base_color, clash_color, out_ms, in_ms, lockup_flicker_color, blast_color) {
  5332. super("Blade to color.", arguments);
  5333. this.add_arg("BASE_COLOR","COLOR","Main color");
  5334. this.add_arg("CLASH_COLOR", "COLOR", "Clash color");
  5335. this.add_arg("OUT_MS", "INT", "extension length in milliseconds");
  5336. this.add_arg("IN_MS", "INT", "retraction length in milliseconds");
  5337. this.add_arg("LOCKUP_FLICKER_COLOR", "COLOR", "lockup flicker color", WHITE.DOCOPY());
  5338. this.add_arg("BLAST_COLOR", "COLOR", "Blast color", WHITE.DOCOPY());
  5339.  
  5340. var tmp = AudioFlicker(this.BASE_COLOR, this.LOCKUP_FLICKER_COLOR);
  5341. var tmp2 = Blast(this.BASE_COLOR.DOCOPY(), this.BLAST_COLOR);
  5342. tmp = Lockup(tmp2, tmp);
  5343. tmp = SimpleClash(tmp, this.CLASH_COLOR);
  5344. this.SetExpansion(InOutHelper(tmp, this.OUT_MS, this.IN_MS));
  5345. }
  5346. }
  5347.  
  5348. function StyleNormalPtr(base_color, clash_color, out_ms, in_ms, lockup_flicker_color, blast_color) {
  5349. return new StyleNormalPtrClass(base_color, clash_color, out_ms, in_ms, lockup_flicker_color, blast_color);
  5350. }
  5351.  
  5352. class StyleRainbowPtrClass extends MACRO {
  5353. constructor(OUT_MS, IN_MS, CLASH_COLOR, LOCKUP_FLICKER_COLOR) {
  5354. super("Rainbow style template", arguments);
  5355. this.add_arg("OUT_MS", "INT", "extension length in milliseconds");
  5356. this.add_arg("IN_MS", "INT", "retraction length in milliseconds");
  5357. this.add_arg("CLASH_COLOR", "COLOR", "Clash color", WHITE.DOCOPY());
  5358. this.add_arg("LOCKUP_FLICKER_COLOR", "COLOR", "lockup flicker color", WHITE.DOCOPY());
  5359.  
  5360. var tmp = AudioFlicker(Rainbow(), this.LOCKUP_FLICKER_COLOR);
  5361. tmp = Lockup(Rainbow(), tmp);
  5362. tmp = SimpleClash(tmp, this.CLASH_COLOR);
  5363. this.SetExpansion(InOutHelper(tmp, this.OUT_MS, this.IN_MS));
  5364. }
  5365. };
  5366.  
  5367. function StyleRainbowPtr(out_ms, in_ms, clash_color, lockup_flicker_color) {
  5368. return new StyleRainbowPtrClass(out_ms, in_ms, clash_color, lockup_flicker_color);
  5369. }
  5370.  
  5371.  
  5372. class StyleStrobePtrClass extends MACRO {
  5373. constructor(STROBE_COLOR, CLASH_COLOR, FREQUENCY, OUT_MS, IN_MS) {
  5374. super("Rainbow style template", arguments);
  5375. this.add_arg("STROBE_COLOR","COLOR","Strobe color");
  5376. this.add_arg("CLASH_COLOR", "COLOR", "Clash color");
  5377. this.add_arg("FREQUENCY", "INT", "frequency");
  5378. this.add_arg("OUT_MS", "INT", "extension length in milliseconds");
  5379. this.add_arg("IN_MS", "INT", "retraction length in milliseconds");
  5380.  
  5381. var strobe = Strobe(BLACK.DOCOPY(), this.STROBE_COLOR, this.FREQUENCY, 1);
  5382. var fast_strobe = Strobe(BLACK.DOCOPY(), this.STROBE_COLOR.DOCOPY(), this.FREQUENCY * 3, 1);
  5383. var tmp = Lockup(strobe, fast_strobe);
  5384. tmp = SimpleClash(tmp, this.CLASH_COLOR);
  5385. this.SetExpansion(InOutHelper(tmp, this.OUT_MS, this.IN_MS));
  5386. }
  5387. };
  5388.  
  5389. function StyleStrobePtr(strobe_color, clash_color, frequency, out_ms, in_ms) {
  5390. return new StyleStrobePtrClass(strobe_color, clash_color, frequency, out_ms, in_ms);
  5391. }
  5392.  
  5393. class StyleFirePtrClass extends MACRO {
  5394. constructor(COLOR1, COLOR2,
  5395. BLADE_NUM, DELAY, SPEED,
  5396. NORM_BASE, NORM_RAND, NORM_COOL,
  5397. CLSH_BASE, CLSH_RAND, CLSH_COOL,
  5398. LOCK_BASE, LOCK_RAND, LOCK_COOL,
  5399. OFF_BASE, OFF_RAND, OFF_COOL) {
  5400. super("Fire Blade", arguments);
  5401. this.add_arg("COLOR1", "COLOR", "Warm color.");
  5402. this.add_arg("COLOR2", "COLOR", "Hot color.");
  5403. this.add_arg("BLADE_NUM", "INT", "Ignored", INT(1));
  5404. this.add_arg("DELAY", "INT", "ignition delay", INT(0));
  5405. this.add_arg("SPEED", "INT", "fire speed", INT(2));
  5406. this.add_arg("NORM_BASE", "INT", "constant heat added in normal mode", INT(0));
  5407. this.add_arg("NORM_RAND", "INT", "random heat added in normal mode", INT(2000));
  5408. this.add_arg("NORM_COOL", "INT", "cooling in normal mode", INT(5));
  5409.  
  5410. this.add_arg("CLSH_BASE", "INT", "constant heat added in clash mode", INT(3000));
  5411. this.add_arg("CLSH_RAND", "INT", "random heat added in clash mode", INT(0));
  5412. this.add_arg("CLSH_COOL", "INT", "cooling in clash mode", INT(0));
  5413.  
  5414. this.add_arg("LOCK_BASE", "INT", "constant heat added in lockup mode", INT(0));
  5415. this.add_arg("LOCK_RAND", "INT", "random heat added in lockup mode", INT(5000));
  5416. this.add_arg("LOCK_COOL", "INT", "cooling in lockup mode", INT(10));
  5417.  
  5418. this.add_arg("OFF_BASE", "INT", "constant heat added in off mode", INT(0));
  5419. this.add_arg("OFF_RAND", "INT", "random heat added in off mode", INT(0));
  5420. this.add_arg("OFF_COOL", "INT", "cooling in off mode", INT(10));
  5421. this.SetExpansion(StyleFire(
  5422. this.COLOR1, this.COLOR2,
  5423. this.DELAY, this.SPEED,
  5424. FireConfig(this.NORM_BASE, this.NORM_RAND, this.NORM_COOL),
  5425. FireConfig(this.CLSH_BASE, this.CLSH_RAND, this.CLSH_COOL),
  5426. FireConfig(this.LOCK_BASE, this.LOCK_RAND, this.LOCK_COOL),
  5427. FireConfig(this.OFF_BASE, this.OFF_RAND, this.OFF_COOL)));
  5428. }
  5429. };
  5430.  
  5431. function StyleFirePtr(COLOR1, COLOR2,
  5432. BLADE_NUM, DELAY, SPEED,
  5433. NORM_BASE, NORM_RAND, NORM_COOL,
  5434. CLSH_BASE, CLSH_RAND, CLSH_COOL,
  5435. LOCK_BASE, LOCK_RAND, LOCK_COOL,
  5436. OFF_BASE, OFF_RAND, OFF_COOL) {
  5437. return new StyleFirePtrClass(COLOR1, COLOR2,
  5438. BLADE_NUM, DELAY, SPEED,
  5439. NORM_BASE, NORM_RAND, NORM_COOL,
  5440. CLSH_BASE, CLSH_RAND, CLSH_COOL,
  5441. LOCK_BASE, LOCK_RAND, LOCK_COOL,
  5442. OFF_BASE, OFF_RAND , OFF_COOL);
  5443. }
  5444.  
  5445. class InOutHelperFClass extends FUNCTION {
  5446. constructor(T, EXTENSION, OFF_COLOR, ALLOW_DISABLE) {
  5447. super("0=retracted, 32768=extended", arguments);
  5448. this.add_arg("EXTENSION", "FUNCTION", "extension amount");
  5449. this.add_arg("ALLOW_DISABLE", "INT", "allow disable?", 1);
  5450. }
  5451. run(blade) {
  5452. super.run(blade);
  5453. this.thres = (this.EXTENSION.getInteger(0) * blade.num_leds());
  5454. }
  5455. getInteger(led) {
  5456. return 32768 - clamp(this.thres - led * 32768, 0, 32768);
  5457. }
  5458. }
  5459.  
  5460. function InOutHelperF(EX, AD) {
  5461. return new InOutHelperFClass(EX, AD);
  5462. }
  5463.  
  5464. class InOutHelperLClass extends MACRO {
  5465. isEffect() { return true; }
  5466. constructor(EXTENSION, OFF_COLOR, ALLOW_DISABLE) {
  5467. super("0=retracted, 32768=extended", arguments);
  5468. this.add_arg("EXTENSION", "FUNCTION", "extension amount");
  5469. this.add_arg("OFF_COLOR", "COLOR", "color when retracted", BLACK.DOCOPY());
  5470. this.add_arg("ALLOW_DISABLE", "INT", "allow disable?", 1);
  5471. this.SetExpansion(AlphaL(this.OFF_COLOR, InOutHelperF(EXTENSION, this.ALLOW_DISABLE)));
  5472. }
  5473. }
  5474.  
  5475. function InOutHelperL(EX, O, AD) {
  5476. return new InOutHelperLClass(EX, O, AD);
  5477. }
  5478.  
  5479.  
  5480. class InOutHelperXClass extends MACRO {
  5481. constructor(T, EXTENSION, OFF_COLOR, ALLOW_DISABLE) {
  5482. super("0=retracted, 32768=extended", arguments);
  5483. this.add_arg("T", "COLOR", "base color");
  5484. this.add_arg("EXTENSION", "FUNCTION", "extension amount");
  5485. this.add_arg("OFF_COLOR", "COLOR", "color when retracted", BLACK.DOCOPY());
  5486. this.add_arg("ALLOW_DISABLE", "INT", "allow disable?", 1);
  5487. this.SetExpansion(Layers(T, InOutHelperL(EXTENSION, this.OFF_COLOR, this.ALLOW_DISABLE)));
  5488. }
  5489. }
  5490.  
  5491. function InOutHelperX(T, EX, O, AD) {
  5492. return new InOutHelperXClass(T, EX, O, AD);
  5493. }
  5494.  
  5495.  
  5496. //--
  5497. class InOutHelperClass extends MACRO {
  5498. constructor(T, OUT_MILLIS, IN_MILLIS, OFF_COLOR) {
  5499. super("Extend/extract blade", arguments);
  5500. this.add_arg("T", "COLOR", "Base color");
  5501. this.add_arg("OUT_MILLIS", "INT", "Time to extend.");
  5502. this.add_arg("IN_MILLIS", "INT", "Time to retract.");
  5503. this.add_arg("OFF_COLOR", "COLOR", "color when retracted", BLACK.DOCOPY());
  5504. this.SetExpansion(InOutHelperX(T, InOutFunc(OUT_MILLIS, IN_MILLIS), this.OFF_COLOR));
  5505. }
  5506. };
  5507.  
  5508. function InOutHelper(T, I, O, OFF) {
  5509. return new InOutHelperClass(T, I, O, OFF);
  5510. }
  5511.  
  5512.  
  5513. class InOutSparkTipClass extends STYLE {
  5514. constructor(T, OUT_MILLIS, IN_MILLIS, OFF_COLOR) {
  5515. super("Implements extention/retraction", arguments);
  5516. this.add_arg("T", "COLOR", "base color");
  5517. this.add_arg("OUT_MILLIS", "INT", "extentions length in ms");
  5518. this.add_arg("IN_MILLIS", "INT", "retraction length in ms");
  5519. this.add_arg("SPARK_COLOR", "COLOR", "color of spark tip", WHITE.DOCOPY());
  5520. this.last_micros_ = 0;
  5521. this.extension = 0;
  5522. }
  5523. run(blade) {
  5524. this.T.run(blade);
  5525. this.SPARK_COLOR.run(blade);
  5526.  
  5527. var now = micros();
  5528. var delta = now - this.last_micros_;
  5529. this.last_micros_ = now;
  5530. if (blade.is_on()) {
  5531. if (this.extension == 0.0) {
  5532. // We might have been off for a while, so delta might
  5533. // be insanely high.
  5534. this.extension = 0.00001;
  5535. } else {
  5536. this.extension += delta / (this.OUT_MILLIS * 1000.0);
  5537. this.extension = Math.min(this.extension, 1.0);
  5538. }
  5539. } else {
  5540. this.extension -= delta / (this.IN_MILLIS * 1000.0);
  5541. this.extension = Math.max(this.extension, 0.0);
  5542. }
  5543. var thres = this.extension * (blade.num_leds() + 5) * 256;
  5544. this.thres1 = Math.floor(thres);
  5545. if (blade.is_on()) {
  5546. this.thres2 = Math.floor(thres) - 1024;
  5547. } else {
  5548. this.thres2 = Math.floor(thres) + 1024;
  5549. }
  5550. }
  5551. getColor(led) {
  5552. var x1 = led + 1 - this.thres1 / 256.0;
  5553. x1 = min(x1, 1.0);
  5554. x1 = max(x1, 0.0);
  5555. var x2 = led + 1 - this.thres2 / 256.0;
  5556. x2 = min(x2, 1.0);
  5557. x2 = max(x2, 0.0);
  5558. var c = this.T.getColor(led);
  5559. var spark_color = this.SPARK_COLOR.getColor(led);
  5560. var off = Rgb(0,0,0);
  5561. return c.mix(spark_color, x2).mix(off, x1);
  5562. }
  5563. };
  5564.  
  5565. function InOutSparkTip(T, I, O, S) {
  5566. return new InOutSparkTipClass(T, I, O, S);
  5567. }
  5568.  
  5569. class ColorChangeClass extends MACRO {
  5570. constructor(ARGS) {
  5571. super("Change color based on variation", ARGS);
  5572. this.COLORS = Array.from(ARGS).slice(1);
  5573. this.add_arg("TRANSITION", "TRANSITION","Transition");
  5574. for (var i = 1; i < this.COLORS.length + 1; i++)
  5575. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  5576. this.SetExpansion(new ColorSelectClass([Variation(), this.TRANSITION].concat(this.COLORS)));
  5577. }
  5578. };
  5579.  
  5580. function ColorChange(T, A, B) {
  5581. return new ColorChangeClass(Array.from(arguments));
  5582. }
  5583.  
  5584. class ColorSelectClass extends STYLE {
  5585. constructor(ARGS) {
  5586. super("Change color based on function", ARGS);
  5587. this.COLORS = Array.from(ARGS).slice(2);
  5588. this.add_arg("F", "FUNCTION","Selector function");
  5589. this.add_arg("TRANSITION", "TRANSITION","Transition");
  5590. for (var i = 1; i < this.COLORS.length + 1; i++)
  5591. this.add_arg("COLOR" + i, "COLOR", "COLOR " + i);
  5592. this.selection = this.F.getInteger(0);
  5593. this.old_selection = this.selection;
  5594. if (this.F.pp() == "Variation") {
  5595. HandleEffectType(EFFECT_CHANGE);
  5596. }
  5597. }
  5598. run(blade) {
  5599. this.F.run(blade);
  5600. for (var i = 0; i < this.COLORS.length; i++)
  5601. this.COLORS[i].run(blade);
  5602. var f = this.F.getInteger(0);
  5603. while (f < 0) f += this.COLORS.length * 256;
  5604. var selection = f % this.COLORS.length;
  5605. if (selection != this.selection) {
  5606. // Start transition
  5607. this.old_selection = this.selection;
  5608. this.selection = selection;
  5609. this.TRANSITION.begin();
  5610. }
  5611. if (this.selection != this.old_selection) {
  5612. this.TRANSITION.run(blade);
  5613. if (this.TRANSITION.done()) {
  5614. this.old_selection = this.selection;
  5615. }
  5616. }
  5617. }
  5618. getColor(led) {
  5619. var ret = this.COLORS[this.selection + 0].getColor(led);
  5620. if (this.selection != this.old_selection) {
  5621. var old = this.COLORS[this.old_selection].getColor(led);
  5622. ret = this.TRANSITION.getColor(old, ret, led);
  5623. }
  5624. return ret;
  5625. }
  5626. };
  5627.  
  5628. function ColorSelect(F, T, A, B) {
  5629. return new ColorSelectClass(Array.from(arguments));
  5630. }
  5631.  
  5632. class IntSelectClass extends FUNCTION {
  5633. constructor(ARGS) {
  5634. super("Select number based on function", ARGS);
  5635. this.INTS = Array.from(ARGS).slice(1);
  5636. this.add_arg("F", "FUNCTION","Selector function");
  5637. for (var i = 1; i <= this.INTS.length; i++)
  5638. this.add_arg("INT" + i, "INT", "Integer " + i);
  5639. }
  5640. run(blade) {
  5641. this.F.run(blade);
  5642. var f = this.F.getInteger(0);
  5643. while (f < 0) f += this.COLORS.length * 256;
  5644. f = f % this.INTS.length;
  5645. this.value = this.INTS[f];
  5646. }
  5647. getInteger(led) {
  5648. return this.value;
  5649. }
  5650. };
  5651.  
  5652. function IntSelect(ARGS) {
  5653. return new IntSelectClass(Array.from(arguments));
  5654. }
  5655.  
  5656. class TransitionLoopLClass extends STYLE {
  5657. constructor(TRANSITION) {
  5658. super("Continiously loop a transition",arguments);
  5659. this.add_arg("TRANSITION", "TRANSITION", "Transition");
  5660. this.TRANSITION.begin();
  5661. }
  5662. run(blade) {
  5663. if (this.TRANSITION.done()) this.TRANSITION.begin();
  5664. super.run(blade);
  5665. }
  5666. getColor(led) {
  5667. return this.TRANSITION.getColor(Transparent(), Transparent(), led);
  5668. }
  5669. };
  5670.  
  5671. function TransitionLoopL(T) { return new TransitionLoopLClass(T); }
  5672.  
  5673. class TransitionLoopClass extends MACRO {
  5674. constructor(COLOR, TRANSITION) {
  5675. super("Continiously loop a transition",arguments);
  5676. this.add_arg("COLOR", "COLOR", "Color");
  5677. this.add_arg("TRANSITION", "TRANSITION", "Transition");
  5678. this.SetExpansion(Layers(COLOR, TransitionLoopL(TRANSITION)));
  5679. }
  5680. };
  5681.  
  5682. function TransitionLoop(C, T) { return new TransitionLoopClass(C, T); }
  5683.  
  5684. class MultiTransitionEffectLClass extends STYLE {
  5685. constructor(TRANSITION, EFFECT_ARG, N) {
  5686. super("Trigger transitions on an effect.", arguments);
  5687. this.add_arg("TRANSITION", "TRANSITION", "from EFFECT_COLOR T");
  5688. this.add_arg("EFFECT", "EFFECT", "Effect type");
  5689. this.add_arg("N", "INT", "Simultaneous effects.", 3);
  5690. this.effect_ = new OneshotEffectDetector(this.EFFECT);
  5691. this.TRANSITIONS=[];
  5692. this.running=[];
  5693. this.pos = 0;
  5694. for (var i = 0; i < this.N; i++) {
  5695. this.TRANSITIONS.push(this.TRANSITION.DOCOPY());
  5696. this.running.push(false);
  5697. }
  5698. HandleEffectType(EFFECT_ARG);
  5699. }
  5700. run(blade) {
  5701. var e = this.effect_.Detect(blade);
  5702. if (e) {
  5703. this.TRANSITIONS[this.pos].begin();
  5704. this.running[this.pos] = true;
  5705. this.pos++;
  5706. if (this.pos == this.N) this.pos = 0;
  5707. }
  5708. for (var i = 0; i < this.N; i++) {
  5709. if (this.running[i]) {
  5710. this.TRANSITIONS[i].run(blade);
  5711. if (this.TRANSITIONS[i].done()) {
  5712. this.running[i] = false;
  5713. }
  5714. }
  5715. }
  5716. }
  5717. getColor(led) {
  5718. var ret = Transparent();
  5719. var P = this.pos + 1;
  5720. for (var i = 0; i < this.N; i++) {
  5721. if (P > this.N) P = 0;
  5722. if (this.running[P]) {
  5723. ret = this.TRANSITIONS[P].getColor(ret, ret, led);
  5724. }
  5725. P++;
  5726. }
  5727. return ret;
  5728. }
  5729. IS_RUNNING() {
  5730. for (var i = 0; i < this.N; i++) {
  5731. if(this.running[i]) return true;
  5732. }
  5733. return false;
  5734. }
  5735.  
  5736. argify(state) {
  5737. state.color_argument = effect_to_argument(this.EFFECT);
  5738. var ret = super.argify(state);
  5739. state.color_argument = null;
  5740. return ret;
  5741. }
  5742. };
  5743.  
  5744. function MultiTransitionEffectL(T, E, N) {
  5745. return new MultiTransitionEffectLClass(T, E, N);
  5746. }
  5747.  
  5748. class MultiTransitionEffectClass extends MACRO {
  5749. constructor(T, EFFECT_COLOR, TRANSITION1, TRANSITION2, EFFECT, N) {
  5750. super("Trigger transitions on an effect.", arguments);
  5751. this.add_arg("T", "COLOR", "Base color.");
  5752. this.add_arg("EFFECT_COLOR", "COLOR", "Effect color.");
  5753. this.add_arg("TRANSITION1", "TRANSITION", "from T to EFFECT_COLOR");
  5754. this.add_arg("TRANSITION2", "TRANSITION", "from EFFECT_COLOR T");
  5755. this.add_arg("EFFECT", "EFFECT", "Effect type");
  5756. this.add_arg("N", "INT", "Number of simultaneous effects.", 3);
  5757. this.SetExpansion(Layers(this.T, MultiTransitionEffectL(TrConcat(this.TRANSITION1, this.EFFECT_COLOR, this.TRANSITION2), this.EFFECT, this.N)));
  5758. }
  5759. };
  5760.  
  5761. function MultiTransitionEffect(T, EC, T1, T2, E, N) {
  5762. return new MultiTransitionEffectClass(T, EC, T1, T2, E, N);
  5763. }
  5764.  
  5765. class TransitionEffectLClass extends STYLE {
  5766. constructor(EFFECT_COLOR, TRANSITION1, TRANSITION2, EFFECT_ARG) {
  5767. super("Trigger transitions on an effect.", arguments);
  5768. this.add_arg("TRANSITION", "TRANSITION", "from EFFECT_COLOR T");
  5769. this.add_arg("EFFECT", "EFFECT", "Effect type");
  5770. this.effect_ = new OneshotEffectDetector(this.EFFECT);
  5771. this.run_ = false;
  5772. }
  5773. run(blade) {
  5774. var e = this.effect_.Detect(blade);
  5775. if (e) {
  5776. this.TRANSITION.begin();
  5777. this.run_ = true;
  5778. }
  5779. this.TRANSITION.run(blade);
  5780. if (this.run_ && this.TRANSITION.done()) {
  5781. this.run_ = false;
  5782. }
  5783. }
  5784. getColor(led) {
  5785. var ret = Transparent();
  5786. if (this.run_) {
  5787. ret = this.TRANSITION.getColor(ret, ret, led);
  5788. }
  5789. return ret;
  5790. }
  5791. IS_RUNNING() { return this.run_; }
  5792.  
  5793. argify(state) {
  5794. state.color_argument = effect_to_argument(this.EFFECT);
  5795. var ret = super.argify(state);
  5796. state.color_argument = null;
  5797. return ret;
  5798. }
  5799. };
  5800.  
  5801. function TransitionEffectL(T, E) {
  5802. return new TransitionEffectLClass(T, E);
  5803. }
  5804.  
  5805. class TransitionEffectClass extends MACRO {
  5806. constructor(T, EFFECT_COLOR, TRANSITION1, TRANSITION2, EFFECT_ARG) {
  5807. super("Trigger transitions on an effect.", arguments);
  5808. this.add_arg("T", "COLOR", "Base color.");
  5809. this.add_arg("EFFECT_COLOR", "COLOR", "Effect color.");
  5810. this.add_arg("TRANSITION1", "TRANSITION", "from T to EFFECT_COLOR");
  5811. this.add_arg("TRANSITION2", "TRANSITION", "from EFFECT_COLOR T");
  5812. this.add_arg("EFFECT", "EFFECT", "Effect type");
  5813. this.SetExpansion(Layers(this.T, TransitionEffectL(TrConcat(this.TRANSITION1, this.EFFECT_COLOR, this.TRANSITION2), this.EFFECT)));
  5814. }
  5815. };
  5816.  
  5817. function TransitionEffect(T, EC, T1, T2, E) {
  5818. return new TransitionEffectClass(T, EC, T1, T2, E);
  5819. }
  5820.  
  5821. class TransitionPulseLClass extends STYLE {
  5822. constructor(TRANSITION, PULSE) {
  5823. super("Triggers transition when pulse occurs.", arguments);
  5824. this.add_arg("TRANSITION", "TRANSITION", "Transition.");
  5825. this.add_arg("PULSE", "FUNCTION", "Pulse.");
  5826. this.run_ = false;
  5827. }
  5828.  
  5829. run(blade) {
  5830. this.PULSE.run(blade);
  5831. if (this.PULSE.getInteger(0)) {
  5832. this.TRANSITION.begin();
  5833. this.run_ = true;
  5834. }
  5835. if (this.run_) {
  5836. this.TRANSITION.run(blade);
  5837. if (this.TRANSITION.done()) this.run_ = false;
  5838. }
  5839. }
  5840.  
  5841. getColor(led) {
  5842. if (this.run_) {
  5843. return this.TRANSITION.getColor(Transparent(), Transparent(), led);
  5844. } else {
  5845. return Transparent();
  5846. }
  5847. }
  5848. }
  5849.  
  5850. function TransitionPulseL(TRANSITION, PULSE) {
  5851. return new TransitionPulseLClass(TRANSITION, PULSE);
  5852. }
  5853.  
  5854. class InOutTrLClass extends STYLE {
  5855. isEffect() { return true; }
  5856. constructor(OUT_TR, IN_TR, OFF, AD) {
  5857. super("In-out based on transitions", arguments);
  5858. this.add_arg("OUT_TR", "TRANSITION", "IN-OUT transition");
  5859. this.add_arg("IN_TR", "TRANSITION", "OUT-IN transition");
  5860. this.add_arg("OFF", "COLOR", "Color when off", BLACK.DOCOPY());
  5861. this.add_arg("ALLOW_DISABLE", "INT", "allow disable?", 1);
  5862. this.on_ = false;
  5863. this.out_active_ = false;
  5864. this.in_active_ = false;
  5865. }
  5866. run(blade) {
  5867. this.OFF.run(blade);
  5868.  
  5869. if (this.on_ != blade.is_on()) {
  5870. this.on_ = blade.is_on();
  5871. if (this.on_) {
  5872. this.OUT_TR.begin();
  5873. this.out_active_ = true;
  5874. } else {
  5875. this.IN_TR.begin();
  5876. this.in_active_ = true;
  5877. }
  5878. }
  5879.  
  5880. if (this.out_active_) {
  5881. this.OUT_TR.run(blade);
  5882. if (this.OUT_TR.done()) {
  5883. this.out_active_ = false;
  5884. }
  5885. }
  5886.  
  5887. if (this.in_active_) {
  5888. this.IN_TR.run(blade);
  5889. if (this.IN_TR.done()) {
  5890. this.in_active_ = false;
  5891. }
  5892. }
  5893. }
  5894.  
  5895. runIn(A, B, led) {
  5896. if (this.in_active_) {
  5897. return this.IN_TR.getColor(A, B, led);
  5898. } else {
  5899. return B;
  5900. }
  5901. }
  5902.  
  5903. runOut(A, B, led) {
  5904. if (this.out_active_) {
  5905. return this.OUT_TR.getColor(A, B, led);
  5906. } else {
  5907. return B;
  5908. }
  5909. }
  5910.  
  5911. getColor(led) {
  5912. if (!this.out_active_ && !this.in_active_) {
  5913. if (this.on_) {
  5914. return Transparent();
  5915. } else {
  5916. return this.OFF.getColor(led);
  5917. }
  5918. } else {
  5919. var on = Transparent();
  5920. var off = this.OFF.getColor(led);
  5921. if (this.on_) {
  5922. return this.runOut(this.runIn(on, off, led), on, led);
  5923. } else {
  5924. return this.runIn(this.runOut(off, on, led), off, led);
  5925. }
  5926. }
  5927. }
  5928. };
  5929.  
  5930. function InOutTrL(OUT_TR, IN_TR, OFF, AD) {
  5931. return new InOutTrLClass(OUT_TR, IN_TR, OFF, AD);
  5932. }
  5933.  
  5934. class InOutTrClass extends MACRO {
  5935. constructor(ON, OUT_TR, IN_TR, OFF, AD) {
  5936. super("In-out based on transitions", arguments);
  5937. this.add_arg("ON", "COLOR", "Color when on.");
  5938. this.add_arg("OUT_TR", "TRANSITION", "IN-OUT transition");
  5939. this.add_arg("IN_TR", "TRANSITION", "OUT-IN transition");
  5940. this.add_arg("OFF", "COLOR", "Color when off", BLACK.DOCOPY());
  5941. this.add_arg("ALLOW_DISABLE", "INT", "allow disable?", 1);
  5942. this.SetExpansion(Layers(ON, InOutTrL(OUT_TR, IN_TR, this.OFF, this.ALLOW_DISABLE)));
  5943. }
  5944. };
  5945.  
  5946. function InOutTr(ON, OUT_TR, IN_TR, OFF, AD) {
  5947. return new InOutTrClass(ON, OUT_TR, IN_TR, OFF, AD);
  5948. }
  5949.  
  5950. class RotateColorsXClass extends STYLE {
  5951. constructor(ROTATION, COLOR) {
  5952. super("Rotate colors", arguments);
  5953. this.add_arg("ROTATION", "FUNCTION", "Rotation");
  5954. this.add_arg("COLOR", "COLOR", "Color");
  5955. }
  5956. getColor(led) {
  5957. var ret = this.COLOR.getColor(led);
  5958. return ret.rotate((this.ROTATION.getInteger(led) & 0x7fff) * 3);
  5959. }
  5960. argify(state) {
  5961. if (this.ROTATION.constructor == VariationClass) {
  5962. return this.COLOR.argify(state);
  5963. }
  5964. return super.argify(state);
  5965. }
  5966. };
  5967.  
  5968. function RotateColorsX(R, C) { return new RotateColorsXClass(R, C); }
  5969.  
  5970. class RotateColorsClass extends MACRO {
  5971. constructor(ROTATION, COLOR) {
  5972. super("Rotate colors", arguments);
  5973. this.add_arg("ROTATION", "INT", "Rotation");
  5974. this.add_arg("COLOR", "COLOR", "Color");
  5975. this.SetExpansion(RotateColorsX(Int(this.ROTATION), this.COLOR));
  5976. }
  5977. };
  5978.  
  5979. function RotateColors(R, C) { return new RotateColorsClass(R, C); }
  5980.  
  5981. class HueXClass extends MACRO {
  5982. constructor(ROTATION, COLOR) {
  5983. super("Rotate colors", arguments);
  5984. this.add_arg("HUE", "FUNCTION", "Hue");
  5985. this.SetExpansion(RotateColorsX(this.HUE, RED.DOCOPY()));
  5986. }
  5987. };
  5988.  
  5989. function HueX(H) { return new HueXClass(H); }
  5990.  
  5991. class HueClass extends MACRO {
  5992. constructor(ROTATION, COLOR) {
  5993. super("Rotate colors", arguments);
  5994. this.add_arg("HUE", "INT", "Hue");
  5995. this.SetExpansion(HueX(Int(this.HUE)));
  5996. }
  5997. };
  5998.  
  5999. function Hue(H) { return new HueClass(H); }
  6000.  
  6001. // TRANSITIONS
  6002.  
  6003. function AddBend(O, t, len, scale) {
  6004. if (O.bend) {
  6005. return O.bend(t, len, scale);
  6006. } else {
  6007. return scale * t / len;
  6008. }
  6009. }
  6010.  
  6011. class BendTimePowXClass extends TIME_FUNCTION {
  6012. constructor(MILLIS, BEND_FUNCTION) {
  6013. super("Bends time like a gamma function.", arguments);
  6014. this.add_arg("MILLIS", "TIME_FUNCTION", "millis");
  6015. this.add_arg("BEND_FUNCTION", "FUNCTION", "bend, 32768 = 1.0");
  6016. }
  6017. getInteger(led) { return this.MILLIS.getInteger(led); }
  6018. bend(t, len, scale) {
  6019. var exponent = this.BEND_FUNCTION.getInteger(0) / 32768.0;
  6020. return scale * Math.pow(AddBend(this.MILLIS, t, len, 1.0), exponent);
  6021. }
  6022. }
  6023.  
  6024. function BendTimePowX(MILLIS, BEND_FUNCTION) {
  6025. return new BendTimePowXClass(MILLIS, BEND_FUNCTION);
  6026. }
  6027.  
  6028. class ReverseTimeXClass extends TIME_FUNCTION {
  6029. constructor(MILLIS) {
  6030. super("Reverses time in a transition.", arguments);
  6031. this.add_arg("MILLIS", "TIME_FUNCTION", "millis");
  6032. }
  6033. getInteger(led) { return this.MILLIS.getInteger(led); }
  6034. bend(t, len, scale) {
  6035. return scale - AddBend(this.MILLIS, t, len, scale);
  6036. }
  6037. }
  6038.  
  6039. function ReverseTimeX(MILLIS) {
  6040. return new ReverseTimeXClass(MILLIS);
  6041. }
  6042.  
  6043. class BendTimePowInvXClass extends MACRO {
  6044. constructor(MILLIS, BEND_FUNCTION) {
  6045. super("Bends time like an inverted gamma function.", arguments);
  6046. this.add_arg("MILLIS", "TIME_FUNCTION", "millis");
  6047. this.add_arg("BEND_FUNCTION", "FUNCTION", "bend, 32768 = 1.0");
  6048. this.SetExpansion(ReverseTimeX(BendTimePowX(ReverseTimeX(MILLIS.DOCOPY()), BEND_FUNCTION.DOCOPY())));
  6049. }
  6050. }
  6051.  
  6052. function BendTimePowInvX(MILLIS, BEND_FUNCTION) {
  6053. return new BendTimePowInvXClass(MILLIS, BEND_FUNCTION);
  6054. }
  6055.  
  6056.  
  6057. class BendTimePowClass extends MACRO {
  6058. constructor(MILLIS, BEND_FUNCTION) {
  6059. super("Bends time like an gamma function.", arguments);
  6060. this.add_arg("MILLIS", "INT", "millis");
  6061. this.add_arg("BEND_FUNCTION", "INT", "bend, 32768 = 1.0");
  6062. this.SetExpansion(BendTimePowX(Int(MILLIS), Int(BEND_FUNCTION)));
  6063. }
  6064. }
  6065.  
  6066. function BendTimePow(MILLIS, BEND_FUNCTION) {
  6067. return new BendTimePowClass(MILLIS, BEND_FUNCTION);
  6068. }
  6069.  
  6070. class BendTimePowInvClass extends MACRO {
  6071. constructor(MILLIS, BEND_FUNCTION) {
  6072. super("Bends time like an inverted gamma function.", arguments);
  6073. this.add_arg("MILLIS", "INT", "millis");
  6074. this.add_arg("BEND_FUNCTION", "INT", "bend, 32768 = 1.0");
  6075. this.SetExpansion(BendTimePowInvX(Int(MILLIS), Int(BEND_FUNCTION)));
  6076. }
  6077. }
  6078.  
  6079. function BendTimePowInv(MILLIS, BEND_FUNCTION) {
  6080. return new BendTimePowInvClass(MILLIS, BEND_FUNCTION);
  6081. }
  6082.  
  6083. class ReverseTimeClass extends MACRO {
  6084. constructor(MILLIS) {
  6085. super("Reverse time in transitions.", arguments);
  6086. this.add_arg("MILLIS", "INT", "millis");
  6087. this.SetExpansion(ReverseTimeX(Int(MILLIS)));
  6088. }
  6089. }
  6090.  
  6091. function ReverseTime(MILLIS) {
  6092. return new ReverseTimeClass(MILLIS);
  6093. }
  6094.  
  6095.  
  6096. class TrInstantClass extends TRANSITION {
  6097. constructor() {
  6098. super("Instant transition");
  6099. }
  6100. run(blade) {}
  6101. begin() {}
  6102. done() { return true; }
  6103. getColor(A, B, led) { return B; }
  6104. };
  6105.  
  6106. function TrInstant() { return new TrInstantClass(); }
  6107.  
  6108. class TRANSITION_BASE extends TRANSITION {
  6109. constructor(comment, args) {
  6110. super(comment, args);
  6111. this.add_arg("MILLIS", "TIME_FUNCTION", "transition time in milliseconds");
  6112. this.restart_ = false;
  6113. this.start_millis = 0;
  6114. this.len_ = 0;
  6115. }
  6116. begin() { this.restart_ = true; }
  6117. done() { return this.len_ == 0; }
  6118. run(blade) {
  6119. super.run(blade);
  6120. if (this.restart_) {
  6121. this.start_millis_ = millis();
  6122. this.len_ = this.MILLIS.getInteger(0);
  6123. this.restart_ = false;
  6124. }
  6125. }
  6126.  
  6127. update(scale) {
  6128. if (this.len_ == 0) return scale;
  6129. var ms = millis() - this.start_millis_;
  6130. if (ms > this.len_) {
  6131. this.len_ = 0;
  6132. return scale;
  6133. }
  6134. var ret = AddBend(this.MILLIS, ms, this.len_, scale);
  6135. return ret;
  6136. }
  6137. restart() { return this.restart_; }
  6138. };
  6139.  
  6140. // Same as TRANSITION_BASE, but with INT argument instead
  6141. // of FUNCTION
  6142. class TRANSITION_BASE2 extends TRANSITION {
  6143. constructor(comment, args) {
  6144. super(comment, args);
  6145. this.add_arg("MILLIS", "INT", "WipeIn time in milliseconds");
  6146. this.restart_ = false;
  6147. this.start_millis = 0;
  6148. this.len_ = 0;
  6149. }
  6150. begin() { this.restart_ = true; }
  6151. done() { return this.len_ == 0; }
  6152. run(blade) {
  6153. this.MILLIS.run(blade);
  6154. if (this.restart_) {
  6155. this.start_millis_ = millis();
  6156. this.len_ = this.MILLIS.getInteger(0);
  6157. this.restart_ = false;
  6158. }
  6159. }
  6160.  
  6161. update(scale) {
  6162. if (this.len_ == 0) return scale;
  6163. var ms = millis() - this.start_millis_;
  6164. if (ms > this.len_) {
  6165. this.len_ = 0;
  6166. return scale;
  6167. }
  6168. return ms * scale / this.len_;
  6169. }
  6170. };
  6171.  
  6172. class TrFadeXClass extends TRANSITION_BASE {
  6173. constructor(MILLIS) {
  6174. super("Fading transition", arguments);
  6175. }
  6176. run(blade) {
  6177. super.run(blade);
  6178. this.fade_ = this.update(1.0);
  6179. }
  6180. getColor(A, B, led) {
  6181. return A.mix(B, this.fade_);
  6182. }
  6183. };
  6184.  
  6185. function TrFadeX(MILLIS) { return new TrFadeXClass(MILLIS); }
  6186.  
  6187. class TrFadeClass extends MACRO {
  6188. constructor(MILLIS) {
  6189. super("Fading transition", arguments);
  6190. this.add_arg("MILLIS","INT", "Fade time in milliseconds.");
  6191. this.SetExpansion(TrFadeX(Int(MILLIS)));
  6192. }
  6193. }
  6194.  
  6195. function TrFade(MILLIS) { return new TrFadeClass(MILLIS); }
  6196.  
  6197. class TrSmoothFadeXClass extends TRANSITION_BASE {
  6198. constructor(MILLIS) {
  6199. super("Smooth fading transition", arguments);
  6200. }
  6201. run(blade) {
  6202. super.run(blade);
  6203. this.fade_ = this.update(1.0);
  6204. this.fade_ = this.fade_ * this.fade_ * (3 - 2 * this.fade_);
  6205. }
  6206. getColor(A, B, led) {
  6207. return A.mix(B, this.fade_);
  6208. }
  6209. };
  6210.  
  6211. function TrSmoothFadeX(MILLIS) { return new TrSmoothFadeXClass(MILLIS); }
  6212.  
  6213. class TrSmoothFadeClass extends MACRO {
  6214. constructor(MILLIS) {
  6215. super("Smooth fading transition", arguments);
  6216. this.add_arg("MILLIS","INT", "SmoothFade time in milliseconds.");
  6217. this.SetExpansion(TrSmoothFadeX(Int(MILLIS)));
  6218. }
  6219. }
  6220.  
  6221. function TrSmoothFade(MILLIS) { return new TrSmoothFadeClass(MILLIS); }
  6222.  
  6223. class TrDelayXClass extends TRANSITION_BASE {
  6224. constructor(MILLIS) {
  6225. super("Delay transition", arguments);
  6226. }
  6227. run(blade) {
  6228. super.run(blade);
  6229. this.update(1.0);
  6230. }
  6231. getColor(A, B, led) {
  6232. if (this.done()) return B;
  6233. return A;
  6234. }
  6235. }
  6236.  
  6237. function TrDelayX(MILLIS) { return new TrDelayXClass(MILLIS); }
  6238.  
  6239. class TrDelayClass extends MACRO {
  6240. constructor(MILLIS) {
  6241. super("Delay transition", arguments);
  6242. this.add_arg("MILLIS", "INT", "Delay time in milliseconds.");
  6243. this.SetExpansion(TrDelayX(Int(MILLIS)));
  6244. }
  6245. }
  6246.  
  6247. function TrDelay(MILLIS) { return new TrDelayClass(MILLIS); }
  6248.  
  6249. class TrBoingXClass extends TRANSITION_BASE {
  6250. constructor(MILLIS, N) {
  6251. super("Boing transition", arguments);
  6252. this.add_arg("N", "INT", "Number of back-and-forth");
  6253. }
  6254. run(blade) {
  6255. this.N.run(blade);
  6256. super.run(blade);
  6257. this.fade_ = this.update(2 * this.N.getInteger(0) + 1) % 2.0;
  6258. if (this.fade_ > 1.0) {
  6259. this.fade_ = 2.0 - this.fade_;
  6260. }
  6261. }
  6262. getColor(A, B, led) {
  6263. return A.mix(B, this.fade_);
  6264. }
  6265. }
  6266.  
  6267. function TrBoingX(MILLIS, N) { return new TrBoingXClass(MILLIS, N); }
  6268.  
  6269. class TrBoingClass extends MACRO {
  6270. constructor(MILLIS, N) {
  6271. super("Boing transition", arguments);
  6272. this.add_arg("MILLIS", "INT", "Boing time in milliseconds.");
  6273. this.add_arg("N", "INT", "Number of back-and-forth");
  6274. this.SetExpansion(TrBoingX(Int(MILLIS), N));
  6275. }
  6276. }
  6277.  
  6278. function TrBoing(MILLIS, N) { return new TrBoingClass(MILLIS, N); }
  6279.  
  6280.  
  6281. class TrWipeXClass extends TRANSITION_BASE {
  6282. constructor(MILLIS) {
  6283. super("Wipe transition", arguments);
  6284. }
  6285. run(blade) {
  6286. super.run(blade);
  6287. this.num_leds_ = blade.num_leds();
  6288. this.fade_ = this.update(this.num_leds_);
  6289. }
  6290. getColor(A, B, led) {
  6291. var mix = (new Range(0, this.fade_).Intersect(new Range(led, (led + 1)))).Size();
  6292. return A.mix(B, mix);
  6293. }
  6294. }
  6295.  
  6296. function TrWipeX(MILLIS) { return new TrWipeXClass(MILLIS); }
  6297.  
  6298. class TrWipeClass extends MACRO {
  6299. constructor(MILLIS) {
  6300. super("Wipe transition", arguments);
  6301. this.add_arg("MILLIS", "INT", "Wipe time in milliseconds.");
  6302. this.SetExpansion(TrWipeX(Int(MILLIS)));
  6303. }
  6304. }
  6305.  
  6306. function TrWipe(MILLIS) { return new TrWipeClass(MILLIS); }
  6307.  
  6308. class TrWipeInXClass extends TRANSITION_BASE {
  6309. constructor(MILLIS) {
  6310. super("WipeIn transition", arguments);
  6311. }
  6312. run(blade) {
  6313. super.run(blade);
  6314. this.num_leds_ = blade.num_leds();
  6315. this.fade_ = new Range(this.num_leds_-
  6316. this.update(this.num_leds_),
  6317. this.num_leds_);
  6318. }
  6319. getColor(A, B, led) {
  6320. var mix = this.fade_.Intersect(new Range(led, (led + 1))).Size();
  6321. return A.mix(B, mix);
  6322. }
  6323. }
  6324.  
  6325. function TrWipeInX(MILLIS) { return new TrWipeInXClass(MILLIS); }
  6326.  
  6327. class TrWipeInClass extends MACRO {
  6328. constructor(MILLIS) {
  6329. super("WipeIn transition", arguments);
  6330. this.add_arg("MILLIS", "INT", "WipeIn time in milliseconds.");
  6331. this.SetExpansion(TrWipeInX(Int(MILLIS)));
  6332. }
  6333. }
  6334.  
  6335. function TrWipeIn(MILLIS) { return new TrWipeInClass(MILLIS); }
  6336.  
  6337. ///// CenterWipe
  6338.  
  6339. class TrCenterWipeXClass extends TRANSITION_BASE {
  6340. constructor(MILLIS, POS) {
  6341. super("Center Wipe transition", arguments);
  6342. this.add_arg("POS", "TIME_FUNCTION", "Position", Int(16384));
  6343. }
  6344. run(blade) {
  6345. super.run(blade);
  6346. var center = (this.POS.getInteger(0) * blade.num_leds()) / 32768.0;
  6347. var fade_top = this.update(blade.num_leds() - center);
  6348. var fade_bottom = this.update(center);
  6349. var top = clamp(center + fade_top, center, blade.num_leds());
  6350. var bottom = clamp(center - fade_bottom, 0, center);
  6351. this.range = new Range(bottom, top);
  6352. }
  6353. getColor(A, B, led) {
  6354. var mix = this.range.Intersect(new Range(led, (led + 1))).Size();
  6355. return A.mix(B, mix);
  6356. }
  6357. }
  6358.  
  6359. function TrCenterWipeX(MILLIS, POS) { return new TrCenterWipeXClass(MILLIS, POS); }
  6360.  
  6361. class TrCenterWipeClass extends MACRO {
  6362. constructor(MILLIS, POS) {
  6363. super("WipeIn transition", arguments);
  6364. this.add_arg("MILLIS", "INT", "Center Wipe time in milliseconds.");
  6365. this.add_arg("POS", "INT", "Position", 16384);
  6366. this.SetExpansion(TrCenterWipeX(Int(MILLIS), Int(this.POS)));
  6367. }
  6368. }
  6369.  
  6370. function TrCenterWipe(MILLIS, POS) { return new TrCenterWipeClass(MILLIS, POS); }
  6371.  
  6372. class TrCenterWipeSparkXClass extends MACRO {
  6373. constructor(COLOR, MILLIS, POS) {
  6374. super("WipeIn transition", arguments);
  6375. this.add_arg("COLOR", "COLOR", "Color");
  6376. this.add_arg("MILLIS", "FUNCTION", "Center Wipe time in milliseconds.");
  6377. this.add_arg("POS", "INT", "Position", Int(16384));
  6378. this.SetExpansion(TrJoin(TrCenterWipeX(MILLIS, this.POS),TrWaveX(COLOR, Sum(MILLIS, MILLIS, MILLIS, MILLIS), Int(200), Sum(MILLIS, MILLIS), this.POS)));
  6379. }
  6380. }
  6381.  
  6382. function TrCenterWipeSparkX(COLOR, MILLIS, POS) { return new TrCenterWipeSparkXClass(COLOR, MILLIS, POS); }
  6383.  
  6384. class TrCenterWipeSparkClass extends MACRO {
  6385. constructor(COLOR, MILLIS, POS) {
  6386. super("WipeIn transition", arguments);
  6387. this.add_arg("COLOR", "COLOR", "Color");
  6388. this.add_arg("MILLIS", "INT", "Center Wipe time in milliseconds.");
  6389. this.add_arg("POS", "INT", "Position", 16384);
  6390. this.SetExpansion(TrJoin(TrCenterWipeX(Int(MILLIS), Int(this.POS)),TrWaveX(COLOR, Sum(Int(MILLIS), Int(MILLIS), Int(MILLIS), Int(MILLIS)), Int(200), Sum(Int(MILLIS), Int(MILLIS)), Int(this.POS))));
  6391. }
  6392. }
  6393.  
  6394. function TrCenterWipeSpark(COLOR, MILLIS, POS) { return new TrCenterWipeSparkClass(COLOR, MILLIS, POS); }
  6395.  
  6396. ///// CenterWipeIn
  6397.  
  6398. class TrCenterWipeInXClass extends TRANSITION_BASE {
  6399. constructor(MILLIS, POS) {
  6400. super("Center Wipe-in transition", arguments);
  6401. this.add_arg("POS", "FUNCTION", "Position", Int(16384));
  6402. }
  6403. run(blade) {
  6404. super.run(blade);
  6405. var center = (this.POS.getInteger(0) * blade.num_leds()) / 32768.0;
  6406. var fade_top = this.update(blade.num_leds() - center);
  6407. var fade_bottom = this.update(center);
  6408. var top = clamp(blade.num_leds() - fade_top, center, blade.num_leds());
  6409. var bottom = clamp(fade_bottom, 0, center);
  6410. this.range = new Range(bottom, top);
  6411. }
  6412. getColor(A, B, led) {
  6413. var mix = this.range.Intersect(new Range(led, (led + 1))).Size();
  6414. return B.mix(A, mix);
  6415. }
  6416. }
  6417.  
  6418. function TrCenterWipeInX(MILLIS, POS) { return new TrCenterWipeInXClass(MILLIS, POS); }
  6419.  
  6420. class TrCenterWipeInClass extends MACRO {
  6421. constructor(MILLIS, POS) {
  6422. super("WipeIn transition", arguments);
  6423. this.add_arg("MILLIS", "INT", "Center Wipe time in milliseconds.");
  6424. this.add_arg("POS", "INT", "Position", 16384);
  6425. this.SetExpansion(TrCenterWipeInX(Int(MILLIS), Int(this.POS)));
  6426. }
  6427. }
  6428.  
  6429. function TrCenterWipeIn(MILLIS, POS) { return new TrCenterWipeInClass(MILLIS, POS); }
  6430.  
  6431. class TrCenterWipeInSparkXClass extends MACRO {
  6432. constructor(COLOR, MILLIS, POS) {
  6433. super("WipeIn transition", arguments);
  6434. this.add_arg("COLOR", "COLOR", "Color");
  6435. this.add_arg("MILLIS", "TIME_FUNCTION", "Center Wipe time in milliseconds.");
  6436. this.add_arg("POS", "INT", "Position", Int(16384));
  6437. this.SetExpansion(TrJoin(TrCenterWipeInX(MILLIS, this.POS),TrJoin(TrWaveX(COLOR, MILLIS.DOCOPY(), Int(200), Sum(MILLIS, MILLIS), Int(0)),TrWaveX(COLOR, MILLIS, Int(200), Sum(MILLIS, MILLIS), Int(32768)))));
  6438.  
  6439. }
  6440. }
  6441.  
  6442. function TrCenterWipeInSparkX(COLOR, MILLIS, POS) { return new TrCenterWipeInSparkXClass(COLOR, MILLIS, POS); }
  6443.  
  6444. class TrCenterWipeInSparkClass extends MACRO {
  6445. constructor(COLOR, MILLIS, POS) {
  6446. super("WipeIn transition", arguments);
  6447. this.add_arg("COLOR", "COLOR", "Color");
  6448. this.add_arg("MILLIS", "INT", "Center Wipe time in milliseconds.");
  6449. this.add_arg("POS", "INT", "Position", 16384);
  6450. this.SetExpansion(TrJoin(TrCenterWipeInX(Int(MILLIS), Int(this.POS)),TrJoin(TrWaveX(COLOR, Int(MILLIS), Int(200), Sum(Int(MILLIS), Int(MILLIS)), Int(0)),TrWaveX(COLOR, Int(MILLIS), Int(200), Sum(Int(MILLIS), Int(MILLIS)), Int(32768)))));
  6451. }
  6452. }
  6453.  
  6454. function TrCenterWipeInSpark(COLOR, MILLIS, POS) { return new TrCenterWipeInSparkClass(COLOR, MILLIS, POS); }
  6455.  
  6456. /////
  6457.  
  6458. class TrWipeSparkTipXClass extends MACRO {
  6459. constructor(SPARK_COLOR, MILLIS, SIZE) {
  6460. super("TrWipe with sparktip", arguments);
  6461. this.add_arg("SPARK_COLOR", "COLOR", "Spark color");
  6462. this.add_arg("MILLIS", "TIME_FUNCTION", "wipe milliseconds");
  6463. this.add_arg("SIZE", "FUNCTION", "Size of spark.", Int(400));
  6464. this.SetExpansion(TrJoin(TrWipeX(MILLIS),TrSparkX(SPARK_COLOR,this.SIZE,MILLIS,Int(0))));
  6465. }
  6466. };
  6467.  
  6468. function TrWipeSparkTipX(C, M, S) { return new TrWipeSparkTipXClass(C, M, S); }
  6469.  
  6470. class TrWipeSparkTipClass extends MACRO {
  6471. constructor(SPARK_COLOR, MILLIS, SIZE) {
  6472. super("TrWipe with sparktip", arguments);
  6473. this.add_arg("SPARK_COLOR", "COLOR", "Spark color");
  6474. this.add_arg("MILLIS", "INT", "wipe milliseconds");
  6475. this.add_arg("SIZE", "INT", "Size of spark.", 400);
  6476. this.SetExpansion(TrJoin(TrWipe(MILLIS),TrSparkX(SPARK_COLOR,Int(this.SIZE),Int(MILLIS),Int(0))));
  6477. }
  6478. };
  6479.  
  6480. function TrWipeSparkTip(C, M, S) { return new TrWipeSparkTipClass(C, M, S); }
  6481.  
  6482. class TrWipeInSparkTipXClass extends MACRO {
  6483. constructor(SPARK_COLOR, MILLIS, SIZE) {
  6484. super("TrWipeIn with sparktip", arguments);
  6485. this.add_arg("SPARK_COLOR", "COLOR", "Spark color");
  6486. this.add_arg("MILLIS", "TIME_FUNCTION", "wipe milliseconds");
  6487. this.add_arg("SIZE", "FUNCTION", "Size of spark.", Int(400));
  6488. this.SetExpansion(TrJoin(TrWipeInX(MILLIS),TrSparkX(SPARK_COLOR,this.SIZE,MILLIS,Int(32768))));
  6489. }
  6490. };
  6491.  
  6492. function TrWipeInSparkTipX(C, M, S) { return new TrWipeInSparkTipXClass(C, M, S); }
  6493.  
  6494. class TrWipeInSparkTipClass extends MACRO {
  6495. constructor(SPARK_COLOR, MILLIS, SIZE) {
  6496. super("TrWipeIn with sparktip", arguments);
  6497. this.add_arg("SPARK_COLOR", "COLOR", "Spark color");
  6498. this.add_arg("MILLIS", "INT", "wipe milliseconds");
  6499. this.add_arg("SIZE", "INT", "Size of spark.", 400);
  6500. this.SetExpansion(TrJoin(TrWipeIn(MILLIS),TrSparkX(SPARK_COLOR,Int(this.SIZE),Int(MILLIS),Int(32768))));
  6501. }
  6502. };
  6503.  
  6504. function TrWipeInSparkTip(C, M, S) { return new TrWipeInSparkTipClass(C, M, S); }
  6505.  
  6506.  
  6507. class TrWaveXClass extends TRANSITION {
  6508. constructor(COLOR, FADEOUT_MS, WAVE_SIZE, WAVE_MS, WAVE_CENTER) {
  6509. super("Wave travelling outwards.", arguments);
  6510. this.add_arg("COLOR", "COLOR", "Wave color.");
  6511. this.add_arg("FADEOUT_MS", "FUNCTION", "Fadeout time in milliseconds.", Int(200));
  6512. this.add_arg("WAVE_SIZE", "FUNCTION", "Wave size.", Int(100));
  6513. this.add_arg("WAVE_MS", "FUNCTION", "Wave millis.", Int(400));
  6514. this.add_arg("WAVE_CENTER", "FUNCTION", "Wave center.", Int(16384));
  6515. this.restart_ = false;
  6516. this.start_millis = 0;
  6517. this.len_ = 0;
  6518. }
  6519. begin() { this.restart_ = true; }
  6520. done() { return this.len_ == 0; }
  6521. run(blade) {
  6522. super.run(blade);
  6523.  
  6524. if (this.restart_) {
  6525. this.center_ = this.WAVE_CENTER.getInteger(0);
  6526. this.size_ = this.WAVE_SIZE.getInteger(0);
  6527.  
  6528. this.start_millis_ = millis();
  6529. this.len_ = this.FADEOUT_MS.getInteger(0);
  6530. this.restart_ = false;
  6531. }
  6532.  
  6533. this.mix_ = 32768 - this.update(32768);
  6534. this.num_leds_ = blade.num_leds();
  6535. this.offset_ = (millis() - this.start_millis_) * 32768 / this.WAVE_MS.getInteger(0);
  6536. }
  6537. getColor(A, B, led) {
  6538. var dist = Math.abs(this.center_ - led * 32768 / this.num_leds_);
  6539. var N = Math.abs(dist - this.offset_) * this.size_ >> 15;
  6540. var mix;
  6541. if (N <= 32) {
  6542. mix = blast_hump[N] * this.mix_ >> 8;
  6543. } else {
  6544. mix = 0;
  6545. }
  6546. return A.mix(this.COLOR.getColor(led), mix / 32768.0);
  6547. }
  6548.  
  6549. update(scale) {
  6550. if (this.len_ == 0) return scale;
  6551. var ms = millis() - this.start_millis_;
  6552. if (ms > this.len_) {
  6553. this.len_ = 0;
  6554. return scale;
  6555. }
  6556. return ms * scale / this.len_;
  6557. }
  6558. };
  6559.  
  6560. function TrWaveX(COLOR, FADEOUT_MS, WAVE_SIZE, WAVE_MS, WAVE_CENTER) {
  6561. return new TrWaveXClass(COLOR, FADEOUT_MS, WAVE_SIZE, WAVE_MS, WAVE_CENTER);
  6562. }
  6563.  
  6564. class TrSparkXClass extends TRANSITION {
  6565. constructor(COLOR, SPARK_SIZE, SPARK_MS, SPARK_CENTER) {
  6566. super("Spark wave transition", arguments);
  6567. this.add_arg("COLOR", "COLOR", "Color.");
  6568. this.add_arg("SPARK_SIZE", "FUNCTION", "Spark size.", Int(100));
  6569. this.add_arg("SPARK_MS", "TIME_FUNCTION", "Spark MS", Int(100));
  6570. this.add_arg("SPARK_CENTER", "FUNCTION", "Spark center.", Int(16384));
  6571. this.millis = new TRANSITION_BASE("millis", [this.SPARK_MS]);
  6572. }
  6573. begin() {
  6574. this.millis.begin();
  6575. }
  6576. run(blade) {
  6577. super.run(blade);
  6578. if (this.millis.restart()) {
  6579. this.center = this.SPARK_CENTER.getInteger(0);
  6580. this.size = this.SPARK_SIZE.getInteger(0);
  6581. }
  6582. this.millis.run(blade);
  6583. this.num_leds = blade.num_leds();
  6584. this.offset = this.millis.update(32768);
  6585. }
  6586. done() {
  6587. return this.millis.done();
  6588. }
  6589. getColor(A, B, led) {
  6590. var dist = Math.abs(this.center - led * 32768 / this.num_leds);
  6591. var N = Math.abs(dist - this.offset) * this.size >> 15;
  6592. var mix;
  6593. if (N <= 32) {
  6594. mix = blast_hump[N] << 7;
  6595. } else {
  6596. mix = 0;
  6597. }
  6598. return A.mix(this.COLOR.getColor(led), mix / 32768.0);
  6599. }
  6600. };
  6601.  
  6602. function TrSparkX(COLOR, SPARK_SIZE, SPARK_MS, SPARK_CENTER) {
  6603. return new TrSparkXClass(COLOR, SPARK_SIZE, SPARK_MS, SPARK_CENTER);
  6604. }
  6605.  
  6606. class TrColorCycleXClass extends TRANSITION_BASE {
  6607. constructor(MILLIS, START_RPM, END_RPM) {
  6608. super("ColorCycle transition", arguments);
  6609. this.add_arg("START_RPM", "INT", "RPM at the beginning of transition", 0);
  6610. this.add_arg("END_RPM", "INT", "RPM at the end of transition", 6000);
  6611. this.pos_ = 0.0;
  6612. this.last_micros_ = 0.0;
  6613. }
  6614. run(blade) {
  6615. super.run(blade);
  6616. var now = micros();
  6617. var delta = now - this.last_micros_;
  6618. this.last_micros_ = now;
  6619. if (delta > 1000000) delta = 1;
  6620.  
  6621. this.fade_ = this.update(1.0);
  6622.  
  6623. var current_rpm = this.START_RPM.getInteger(0) * (1 - this.fade_) + this.END_RPM.getInteger(0) * this.fade_;
  6624. var current_percentage = 100.0 * this.fade_;
  6625. this.pos_ = fract(this.pos_ + delta / 60000000.0 * current_rpm);
  6626. this.num_leds_ = blade.num_leds();
  6627. this.start_ = this.pos_ * this.num_leds_;
  6628. if (current_percentage == 100.0) {
  6629. this.start_ = 0;
  6630. this.end_ = this.num_leds_;
  6631. } else if (current_percentage == 0.0) {
  6632. this.start_ = 0;
  6633. this.end_ = 0;
  6634. } else {
  6635. this.end_ = fract(this.pos_ + current_percentage / 100.0) * this.num_leds_;
  6636. }
  6637. }
  6638. getColor(A, B, led) {
  6639. var led_range = new Range(led, led + 1);
  6640. var mix = 0;
  6641. if (this.start_ <= this.end_) {
  6642. mix = (new Range(this.start_, this.end_).Intersect(led_range)).Size();
  6643. } else {
  6644. mix = (new Range(0, this.end_).Intersect(led_range)).Size() +
  6645. (new Range(this.start_, this.num_leds_).Intersect(led_range)).Size();
  6646. }
  6647. return A.mix(B, mix);
  6648. }
  6649. }
  6650.  
  6651. function TrColorCycleX(MILLIS, START_RPM, END_RPM) { return new TrColorCycleXClass(MILLIS, START_RPM, END_RPM); }
  6652.  
  6653. class TrColorCycleClass extends MACRO {
  6654. constructor(MILLIS, START_RPM, END_RPM) {
  6655. super("ColorCycle transition", arguments);
  6656. this.add_arg("MILLIS", "INT", "Transition length in milliseconds.")
  6657. this.add_arg("START_RPM", "INT", "RPM at the beginning of transition", 0);
  6658. this.add_arg("END_RPM", "INT", "RPM at the end of transition", 6000);
  6659. this.SetExpansion(TrColorCycleX(Int(MILLIS), this.START_RPM, this.END_RPM))
  6660. }
  6661. }
  6662.  
  6663. function TrColorCycle(MILLIS, START_RPM, END_RPM) { return new TrColorCycleClass(MILLIS, START_RPM, END_RPM); }
  6664.  
  6665. class TrConcatClass extends TRANSITION {
  6666. constructor(ARGS) {
  6667. super("Concatenate transitions", ARGS);
  6668. this.ARGS = Array.from(ARGS);
  6669. this.add_arg("TRANSITION", "TRANSITION","Transition");
  6670. for (var i = 1; i < this.ARGS.length - 1; i++) {
  6671. if (this.ARGS[i].getType() == "TRANSITION") {
  6672. this.add_arg("TRANSITION" + i, "TRANSITION", "Transiton " + i);
  6673. } else {
  6674. this.add_arg("COLOR" + i, "COLOR", "Color " + i);
  6675. }
  6676. }
  6677. // Last argument must be a transition.
  6678. this.add_arg("TRANSITION" + i, "TRANSITION", "Transiton " + i);
  6679. this.pos_ = this.ARGS.length;
  6680. }
  6681. begin() {
  6682. this.pos_ = 0;
  6683. this.ARGS[0].begin();
  6684. }
  6685. done() {
  6686. return this.pos_ >= this.ARGS.length;
  6687. }
  6688. run(blade) {
  6689. for (var i = 0; i < this.ARGS.length; i++) {
  6690. if (this.ARGS[i].getType() != "TRANSITION") {
  6691. this.ARGS[i].run(blade);
  6692. }
  6693. }
  6694. while (this.pos_ < this.ARGS.length) {
  6695. this.ARGS[this.pos_].run(blade);
  6696. if (!this.ARGS[this.pos_].done()) break;
  6697. this.pos_++;
  6698. if (this.done()) break;
  6699. if (this.ARGS[this.pos_].getType() != "TRANSITION") this.pos_++;
  6700. if (this.done()) break;
  6701. this.ARGS[this.pos_].begin();
  6702. }
  6703. }
  6704. getColor(A, B, led) {
  6705. if (this.done()) return B;
  6706. if (this.pos_ != 0 && this.ARGS[this.pos_-1].getType() != "TRANSITION")
  6707. A = this.ARGS[this.pos_-1].getColor(led);
  6708. if (this.pos_ < this.ARGS.length - 1 && this.ARGS[this.pos_+1].getType() != "TRANSITION")
  6709. B = this.ARGS[this.pos_+1].getColor(led);
  6710. return this.ARGS[this.pos_].getColor(A, B, led);
  6711. }
  6712. };
  6713.  
  6714. function TrConcat(ARGS) {
  6715. return new TrConcatClass(Array.from(arguments));
  6716. }
  6717.  
  6718. class TrJoinClass extends TRANSITION {
  6719. constructor(ARGS) {
  6720. super("Join transitions", ARGS);
  6721. this.ARGS = Array.from(ARGS);
  6722. for (var i = 0; i < this.ARGS.length; i++) {
  6723. this.add_arg("TRANSITION" + i, "TRANSITION", "Transiton " + i);
  6724. }
  6725. }
  6726. begin() {
  6727. for (var i = 0; i < this.ARGS.length; i++) this.ARGS[i].begin();
  6728. }
  6729. done() {
  6730. for (var i = 0; i < this.ARGS.length; i++) if (!this.ARGS[i].done()) return false;
  6731. return true;
  6732. }
  6733. getColor(A, B, led) {
  6734. for (var i = 0; i < this.ARGS.length; i++) {
  6735. A = this.ARGS[i].getColor(A, B, led);
  6736. }
  6737. return A;
  6738. }
  6739. };
  6740.  
  6741. function TrJoin(ARGS) { return new TrJoinClass(arguments); }
  6742.  
  6743. class TrJoinRClass extends TRANSITION {
  6744. constructor(ARGS) {
  6745. super("Right join transitions", ARGS);
  6746. this.ARGS = Array.from(ARGS);
  6747. for (var i = 0; i < this.ARGS.length; i++) {
  6748. this.add_arg("TRANSITION" + i, "TRANSITION", "Transiton " + i);
  6749. }
  6750. }
  6751. begin() {
  6752. for (var i = 0; i < this.ARGS.length; i++) this.ARGS[i].begin();
  6753. }
  6754. done() {
  6755. for (var i = 0; i < this.ARGS.length; i++) if (!this.ARGS[i].done()) return false;
  6756. return true;
  6757. }
  6758. getColor(A, B, led) {
  6759. for (var i = 0; i < this.ARGS.length; i++) {
  6760. B = this.ARGS[i].getColor(A, B, led);
  6761. }
  6762. return B;
  6763. }
  6764. };
  6765.  
  6766. function TrJoinR(ARGS) { return new TrJoinRClass(arguments); }
  6767.  
  6768. class TrRandomClass extends TRANSITION {
  6769. constructor(ARGS) {
  6770. super("Random transitions", ARGS);
  6771. this.ARGS = Array.from(ARGS);
  6772. for (var i = 0; i < this.ARGS.length; i++) {
  6773. this.add_arg("TRANSITION" + i, "TRANSITION", "Transiton " + i);
  6774. }
  6775. this.pos_ = random(this.ARGS.length);
  6776. }
  6777. begin() {
  6778. this.pos_ = random(this.ARGS.length);
  6779. this.ARGS[this.pos_].begin();
  6780. }
  6781. done() {
  6782. return this.ARGS[this.pos_].done();
  6783. }
  6784. getColor(A, B, led) {
  6785. return this.ARGS[this.pos_].getColor(A, B, led);
  6786. }
  6787. };
  6788.  
  6789. function TrRandom(ARGS) { return new TrRandomClass(Array.from(arguments)); }
  6790.  
  6791. class TrSelectClass extends TRANSITION {
  6792. constructor(ARGS) {
  6793. super("Select transitions", ARGS);
  6794. this.TRANSITIONS = Array.from(ARGS).slice(1);
  6795. this.add_arg("F", "FUNCTION", "Transition to select");
  6796. for (var i = 1; i <= max(this.TRANSITIONS.length, 1); i++) {
  6797. this.add_arg("TRANSITION" + i, "TRANSITION", "Transiton " + i);
  6798. }
  6799. this.begin_ = true;
  6800. }
  6801. begin() {
  6802. this.begin_ = true;
  6803. }
  6804. run(blade) {
  6805. this.F.run(blade);
  6806. if (this.begin_) {
  6807. var f = this.F.getInteger(0) + 0;
  6808. while (f < 0) f += this.TRANSITIONS.length * 255;
  6809. f %= this.TRANSITIONS.length;
  6810. this.selected = this.TRANSITIONS[f % this.TRANSITIONS.length];
  6811. this.selected.begin();
  6812. this.begin_ = false;
  6813. }
  6814. this.selected.run(blade);
  6815. }
  6816. done() {
  6817. return this.selected && this.selected.done();
  6818. }
  6819. getColor(A, B, led) {
  6820. return this.selected.getColor(A, B, led);
  6821. }
  6822. };
  6823.  
  6824. function TrSelect(ARGS) {
  6825. return new TrSelectClass(Array.from(arguments));
  6826. }
  6827.  
  6828. class TrExtendXClass extends TRANSITION {
  6829. constructor(MILLIS, TRANSITION) {
  6830. super("Extend a transition.", arguments);
  6831. this.add_arg("MILLIS", "TIME_FUNCTION", "How much to extend the transition.");
  6832. this.add_arg("TRANSITION", "TRANSITION", "Transition to extend.");
  6833. this.extending = false;
  6834. this.millis = new TRANSITION_BASE("millis", [this.MILLIS]);
  6835. }
  6836. begin() {
  6837. this.extending = false;
  6838. this.TRANSITION.begin();
  6839. }
  6840. run(blade) {
  6841. this.TRANSITION.run(blade);
  6842. if (!this.extending && this.TRANSITION.done()) {
  6843. this.extending = true;
  6844. this.millis.begin();
  6845. }
  6846. if (this.extending) {
  6847. this.millis.run(blade);
  6848. this.millis.update(0);
  6849. }
  6850. }
  6851. done() { return this.extending && this.millis.done(); }
  6852. getColor(A, B, led) {
  6853. return this.TRANSITION.getColor(A, B, led);
  6854. }
  6855. };
  6856.  
  6857. function TrExtendX(MILLIS, TRANSACTION) {
  6858. return new TrExtendXClass(MILLIS, TRANSACTION);
  6859. }
  6860.  
  6861. class TrExtendClass extends MACRO {
  6862. constructor(MILLIS, TRANSITION) {
  6863. super("Extend a transition.", arguments);
  6864. this.add_arg("MILLIS", "INT", "How much to extend the transition.");
  6865. this.add_arg("TRANSITION", "TRANSITION", "Transition to extend.");
  6866. this.SetExpansion(TrExtendX(Int(MILLIS), TRANSITION));
  6867. }
  6868. };
  6869.  
  6870. function TrExtend(MILLIS, TRANSACTION) {
  6871. return new TrExtendClass(MILLIS, TRANSACTION);
  6872. }
  6873.  
  6874. class TrBlinkXClass extends TRANSITION_BASE {
  6875. constructor(MILLIS, N, WIDTH) {
  6876. super("Blink N times", arguments);
  6877. this.add_arg("N", "INT", "How many times to blink.");
  6878. this.add_arg("WIDTH", "FUNCTION", "Blink pulse width, 16384 = 50%", Int(16384));
  6879. this.blink = false
  6880. }
  6881. run(blade) {
  6882. super.run(blade);
  6883. this.blink = (this.update(32768 * this.N) & 0x7fff) < this.WIDTH.getInteger(0);
  6884. }
  6885. getColor(a, b, led) {
  6886. if (this.blink) return a;
  6887. return b;
  6888. }
  6889. }
  6890.  
  6891. function TrBlinkX(MILLIS, N, WIDTH) {
  6892. return new TrBlinkXClass(MILLIS, N, WIDTH);
  6893. }
  6894.  
  6895. class TrBlinkClass extends MACRO {
  6896. constructor(MILLIS, N, WIDTH) {
  6897. super("Blink N times", arguments);
  6898. this.add_arg("MILLIS", "INT", "Transition length in milliseconds.")
  6899. this.add_arg("N", "INT", "How many times to blink.");
  6900. this.add_arg("WIDTH", "INT", "Blink pulse width, 16384 = 50%", 16384);
  6901. this.SetExpansion(TrBlinkX(Int(MILLIS), N, Int(this.WIDTH)));
  6902. }
  6903. }
  6904.  
  6905. function TrBlink(MILLIS, N, WIDTH) {
  6906. return new TrBlinkClass(MILLIS, N, WIDTH);
  6907. }
  6908.  
  6909. class TrDoEffectAlwaysXClass extends TRANSITION {
  6910. constructor(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  6911. super("Do effect", arguments);
  6912. this.add_arg("TRANSITION","TRANSITION", "Wrapped transition");
  6913. this.add_arg("EFFECT", "EFFECT", "Effect to trigger.");
  6914. this.add_arg("WAVNUM", "FUNCTION", "Select wave number.");
  6915. this.add_arg("LOCATION", "FUNCTION", "Effect location.");
  6916. this.begin_ = false;
  6917. }
  6918. begin() {
  6919. this.TRANSITION.begin();
  6920. this.begin_ = true;
  6921. }
  6922. run(blade) {
  6923. super.run(blade);
  6924. if (this.begin_) {
  6925. var location = this.LOCATION.getInteger(0);
  6926. if (location == -1) location = random(32768)/32768.0;
  6927. blade.addEffect(this.EFFECT, location);
  6928. this.begin_ = false;
  6929. }
  6930. }
  6931. done() { return this.TRANSITION.done(); }
  6932. getColor(a, b, led) { return this.TRANSITION.getColor(a, b, led); }
  6933. }
  6934.  
  6935. function TrDoEffectAlwaysX(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  6936. return new TrDoEffectAlwaysXClass(TRANSITION, EFFECT, WAVNUM, LOCATION);
  6937. }
  6938.  
  6939. class TrDoEffectAlwaysClass extends MACRO {
  6940. constructor(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  6941. super("Do effect", arguments);
  6942. this.add_arg("TRANSITION","TRANSITION", "Wrapped transition");
  6943. this.add_arg("EFFECT", "EFFECT", "Effect to trigger.");
  6944. this.add_arg("WAVNUM", "INT", "Select wave number.", -1);
  6945. this.add_arg("LOCATION", "INT", "Effect location.", -1);
  6946. this.SetExpansion(TrDoEffectAlwaysX(TRANSITION, EFFECT, Int(this.WAVNUM), Int(this.LOCATION)));
  6947. }
  6948. }
  6949.  
  6950. function TrDoEffectAlways(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  6951. return new TrDoEffectAlwaysClass(TRANSITION, EFFECT, WAVNUM, LOCATION);
  6952. }
  6953.  
  6954.  
  6955. class TrDoEffectXClass extends TRANSITION {
  6956. constructor(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  6957. super("Do effect", arguments);
  6958. this.add_arg("TRANSITION","TRANSITION", "Wrapped transition");
  6959. this.add_arg("EFFECT", "EFFECT", "Effect to trigger.");
  6960. this.add_arg("WAVNUM", "FUNCTION", "Select wave number.", Int(-1));
  6961. this.add_arg("LOCATION", "FUNCTION", "Effect location.", Int(-1));
  6962. this.begin_ = false;
  6963. this.done_ = false;
  6964. }
  6965. begin() {
  6966. this.TRANSITION.begin();
  6967. this.begin_ = true;
  6968. this.done_ = false;
  6969. }
  6970. run(blade) {
  6971. super.run(blade);
  6972. if (this.begin_) {
  6973. if (blade.is_on()) {
  6974. var location = this.LOCATION.getInteger(0);
  6975. if (location == -1) location = random(32768)/32768.0;
  6976. blade.addEffect(this.EFFECT.value, location);
  6977. }
  6978. this.begin_ = false;
  6979. }
  6980. if (!this.done_) {
  6981. /*
  6982. if (!blade.is_on() && !blade.is_powered()) {
  6983. this.done_ = true;
  6984. }
  6985. */
  6986. }
  6987. }
  6988. done() { return this.done_ || this.TRANSITION.done(); }
  6989. getColor(a, b, led) {
  6990. if (this.done_) return b;
  6991. return this.TRANSITION.getColor(a, b, led);
  6992. }
  6993. }
  6994.  
  6995. function TrDoEffectX(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  6996. return new TrDoEffectXClass(TRANSITION, EFFECT, WAVNUM, LOCATION);
  6997. }
  6998.  
  6999. class TrDoEffectClass extends MACRO {
  7000. constructor(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  7001. super("Do effect", arguments);
  7002. this.add_arg("TRANSITION","TRANSITION", "Wrapped transition");
  7003. this.add_arg("EFFECT", "EFFECT", "Effect to trigger.");
  7004. this.add_arg("WAVNUM", "INT", "Select wave number.", -1);
  7005. this.add_arg("LOCATION", "INT", "Effect location.", -1);
  7006. this.SetExpansion(TrDoEffectX(TRANSITION, EFFECT, Int(this.WAVNUM), Int(this.LOCATION)));
  7007. }
  7008. }
  7009.  
  7010. function TrDoEffect(TRANSITION, EFFECT, WAVNUM, LOCATION) {
  7011. return new TrDoEffectClass(TRANSITION, EFFECT, WAVNUM, LOCATION);
  7012. }
  7013.  
  7014.  
  7015. class TrLoopClass extends TRANSITION {
  7016. constructor(TRANSITION) {
  7017. super("Loop transition", arguments);
  7018. this.add_arg("TRANSITION", "TRANSITION", "Transition to loop");
  7019. }
  7020. run(blade) {
  7021. if (this.TRANSITION.done()) this.TRANSITION.begin();
  7022. super.run(blade);
  7023. }
  7024. begin() { this.TRANSITION.begin(); }
  7025. done() { return false; }
  7026. getColor(a, b, led) { return this.TRANSITION.getColor(a, b, led); }
  7027. }
  7028.  
  7029. function TrLoop(TRANSITION) {
  7030. return new TrLoopClass(TRANSITION);
  7031. }
  7032.  
  7033. class TrLoopNXClass extends TRANSITION {
  7034. constructor(N, TRANSITION) {
  7035. super("Loop transition", arguments);
  7036. this.add_arg("N","FUNCTION", "How many loops.");
  7037. this.add_arg("TRANSITION", "TRANSITION", "Transition to loop");
  7038. this.loops = 0;
  7039. }
  7040. run(blade) {
  7041. this.N.run(blade);
  7042. if (this.loops < 0) this.loops = this.N.getInteger(0) + 1;
  7043. if (this.loops > 0 && this.TRANSITION.done()) {
  7044. if (this.loops > 1) this.TRANSITION.begin();
  7045. this.loops --;
  7046. }
  7047. this.TRANSITION.run(blade);
  7048. }
  7049. begin() {
  7050. this.TRANSITION.begin();
  7051. this.loops = -1;
  7052. }
  7053. done() { return this.loops == 0; }
  7054. getColor(a, b, led) { return this.TRANSITION.getColor(a, b, led); }
  7055. }
  7056.  
  7057. function TrLoopNX(N, TRANSITION) {
  7058. return new TrLoopNXClass(N, TRANSITION);
  7059. }
  7060.  
  7061. class TrLoopNClass extends MACRO {
  7062. constructor(N, TRANSITION) {
  7063. super("Loop transition", arguments);
  7064. this.add_arg("N","INT", "How many loops.");
  7065. this.add_arg("TRANSITION", "TRANSITION", "Transition to loop");
  7066. this.SetExpansion(TrLoopNX(Int(N), TRANSITION))
  7067. }
  7068. }
  7069.  
  7070. function TrLoopN(N, TRANSITION) {
  7071. return new TrLoopNClass(N, TRANSITION);
  7072. }
  7073.  
  7074. class TrLoopUntilClass extends TRANSITION {
  7075. constructor(PULSE, TR, OUT) {
  7076. super("Loop transition until pulse occurs.", arguments);
  7077. this.add_arg("PULSE", "FUNCTION", "Pulse");
  7078. this.add_arg("TR", "TRANSITION", "Transition");
  7079. this.add_arg("OUT", "TRANSITION", "Fade-out transition");
  7080. this.pulsed = false;
  7081. }
  7082. begin() {
  7083. this.TR.begin();
  7084. this.pulsed = false;
  7085. }
  7086. done() {
  7087. return this.pulsed && this.OUT.done();
  7088. }
  7089. run(blade) {
  7090. this.PULSE.run(blade);
  7091. if (this.TR.done()) {
  7092. this.TR.begin();
  7093. }
  7094. this.TR.run(blade);
  7095. if (!this.pulsed) {
  7096. if (this.PULSE.getInteger(0)) {
  7097. this.OUT.begin();
  7098. this.pulsed = true;
  7099. }
  7100. }
  7101. if (this.pulsed) {
  7102. this.OUT.run(blade);
  7103. }
  7104. }
  7105. getColor(a, b, led) {
  7106. var ret = this.TR.getColor(a, a, led);
  7107. if (this.pulsed) {
  7108. ret = this.TR.getColor(ret, b, led);
  7109. }
  7110. return ret;
  7111. }
  7112. }
  7113.  
  7114. function TrLoopUntil(PULSE, TR, OUT) {
  7115. return new TrLoopUntilClass(PULSE, TR, OUT);
  7116. }
  7117.  
  7118. // FUNCTIONS
  7119.  
  7120. var BATTERY_LEVEL=24000
  7121.  
  7122. class BatteryLevelClass extends FUNCTION {
  7123. constructor() {
  7124. super("Returns 0-32768 based on battery level.", []);
  7125. }
  7126. run(blade) {}
  7127. getInteger(led) { return 32768 - ((millis() * 3) & 0x7fff); }
  7128. };
  7129.  
  7130. function BatteryLevel() {
  7131. return new BatteryLevelClass();
  7132. }
  7133.  
  7134. class VolumeLevelClass extends FUNCTION {
  7135. constructor() {
  7136. super("Returns 0-32768 based on volume level.", []);
  7137. }
  7138. run(blade) {}
  7139. // getInteger(led) { return 0 + ((millis() * 7) & 0x7fff); }
  7140. getInteger(led) { return 0 + ((millis() / 500) % 11) * 32767 / 10; }
  7141. };
  7142.  
  7143. function VolumeLevel() {
  7144. return new VolumeLevelClass();
  7145. }
  7146.  
  7147. class BumpClass extends FUNCTION {
  7148. constructor() {
  7149. super("Function returning a bump shape", arguments);
  7150. this.add_arg("BUMP_POSITION", "FUNCTION", "0=bump at hilt, 32768=bump at tip");
  7151. this.add_arg("BUMP_WIDTH_FRACTION", "FUNCTION", "bump width", Int(16384));
  7152. }
  7153. run(blade) {
  7154. this.BUMP_POSITION.run(blade);
  7155. this.BUMP_WIDTH_FRACTION.run(blade);
  7156. var fraction = this.BUMP_WIDTH_FRACTION.getInteger(0);
  7157. if (fraction == 0) {
  7158. this.mult = 1;
  7159. this.location = -10000;
  7160. return;
  7161. }
  7162. this.mult = 32 * 2.0 * 128 * 32768 / fraction / blade.num_leds();
  7163. this.location = (this.BUMP_POSITION.getInteger(0) * blade.num_leds() * this.mult) / 32768;
  7164. }
  7165. getInteger(led) {
  7166. var dist = Math.abs(led * this.mult - this.location);
  7167. var p = dist >> 7;
  7168. if (p >= 32) return 0;
  7169. var m = dist & 0x3f;
  7170. return blast_hump[p] * (128 - m) + blast_hump[p+1] * m;
  7171. }
  7172. };
  7173.  
  7174. function Bump(P, F) {
  7175. return new BumpClass(P, F);
  7176. }
  7177.  
  7178.  
  7179. class ChangeSlowlyClass extends FUNCTION {
  7180. constructor(F, SPEED) {
  7181. super("Changes F by no more than SPEED values per second.", arguments);
  7182. this.add_arg("F", "FUNCTION", "Function to moderate");
  7183. this.add_arg("SPEED", "FUNCTION", "maximum change speed");
  7184. this.last_micros = micros();
  7185. this.value = 0;
  7186. }
  7187. run(blade) {
  7188. super.run(blade);
  7189. var now = micros();
  7190. var delta = now - this.last_micros;
  7191. if (delta > 1000000) delta = 1;
  7192. this.last_micros = now;
  7193. delta *= this.SPEED.getInteger(0);
  7194. delta /= 1000000;
  7195. var target = this.F.getInteger(0);
  7196. if (delta > Math.abs(this.value - target)) {
  7197. this.value = target;
  7198. } else if (this.value < target) {
  7199. this.value += delta;
  7200. } else {
  7201. this.value -= delta;
  7202. }
  7203. }
  7204. getInteger(led) { return this.value; }
  7205. }
  7206.  
  7207. function ChangeSlowly(F, SPEED) {
  7208. return new ChangeSlowlyClass(F, SPEED);
  7209. }
  7210.  
  7211. class IfonClass extends FUNCTION {
  7212. constructor(A, B) {
  7213. super("A if on, B if off.", arguments);
  7214. this.add_arg("A", "FUNCTION", "A");
  7215. this.add_arg("B", "FUNCTION", "B");
  7216. }
  7217. run(blade) {
  7218. this.A.run(blade);
  7219. this.B.run(blade);
  7220. this.on = blade.is_on();
  7221. }
  7222. getInteger(led) {
  7223. if (this.on) return this.A.getInteger(led);
  7224. return this.B.getInteger(led);
  7225. }
  7226. };
  7227.  
  7228. function Ifon(A, B) { return new IfonClass(A, B); }
  7229.  
  7230. class InOutFuncXClass extends FUNCTION {
  7231. constructor(OUT_MILLIS, IN_MILLIS) {
  7232. super("0 when off, 32768 when on, OUT_MILLIS/IN_MILLIS determines speed in between.", arguments);
  7233. this.add_arg("OUT_MILLIS", "FUNCTION", "millis to ramp up");
  7234. this.add_arg("IN_MILLIS", "FUNCTION", "millis to ramp down");
  7235. this.last_micros = 0;
  7236. this.extension = 0.0;
  7237. }
  7238. run(blade) {
  7239. super.run(blade);
  7240. var now = micros();
  7241. var delta = now - this.last_micros;
  7242. if (delta < 0 || delta > 1000000) delta = 1;
  7243. this.last_micros = now;
  7244. if (blade.is_on()) {
  7245. if (this.extension == 0.0) {
  7246. this.extension = 0.00001;
  7247. } else {
  7248. this.extension += delta / (this.OUT_MILLIS.getInteger(0) * 1000.0);
  7249. this.extension = Math.min(this.extension, 1.0);
  7250. }
  7251. } else {
  7252. this.extension -= delta / (this.IN_MILLIS.getInteger(0) * 1000.0);
  7253. this.extension = Math.max(this.extension, 0.0);
  7254. }
  7255. this.ret = this.extension * 32768;
  7256. }
  7257. getInteger(led) { return this.ret; }
  7258. argify(status) {
  7259. state.int_argument = IGNITION_TIME_ARG;
  7260. this.OUT_MILLIS = this.OUT_MILLIS.argify(status);
  7261.  
  7262. state.int_argument = RETRACTION_TIME_ARG;
  7263. this.IN_MILLIS = this.IN_MILLIS.argify(status);
  7264.  
  7265. return this;
  7266. }
  7267. };
  7268.  
  7269. function InOutFuncX(O, I) {
  7270. return new InOutFuncXClass(O, I);
  7271. }
  7272. function InOutFunc(O, I) {
  7273. return InOutFuncX(Int(O), Int(I));
  7274. }
  7275.  
  7276. // TODO: InOutFuncTD
  7277.  
  7278. class IntClass extends FUNCTION {
  7279. constructor(N) {
  7280. super("Constant integer function", arguments);
  7281. this.add_arg("N","INT","number to return.");
  7282. }
  7283. getInteger(led) { return this.N; }
  7284. pp() {
  7285. if (pp_is_url) {
  7286. if (this.super_short_desc) return "$";
  7287. return this.gencomment() + "Int<" + this.N + ">";
  7288. }
  7289. return this.PPshort("Int<" + this.N +">", "VALUE");
  7290. }
  7291. argify(state) {
  7292. if (state.int_argument) {
  7293. ret = IntArg_(ArgumentName(state.int_argument), this.N);
  7294. state.int_argument = false;
  7295. return ret;
  7296. } else {
  7297. return this;
  7298. }
  7299. }
  7300. };
  7301.  
  7302. function Int(n) { return new IntClass(Math.round(n)); }
  7303.  
  7304. class IntArgClass extends FUNCTION {
  7305. constructor(ARG, N) {
  7306. super("Dynamic Integer argument", arguments);
  7307. this.add_arg("ARG","ArgumentName","argument number.");
  7308. this.add_arg("DEFAULT","INT","default.");
  7309. }
  7310. run(blade) {
  7311. super.run(blade);
  7312. this.value = parseInt(getARG(this.ARG, "" + this.DEFAULT));
  7313. }
  7314. getInteger(led) { return this.value; }
  7315. argify(state) {
  7316. if (state.int_argument == this.ARG) {
  7317. state.int_argument = false;
  7318. }
  7319. return this;
  7320. }
  7321. };
  7322.  
  7323. function IntArg_(ARG, N) {
  7324. return new IntArgClass(ARG, N);
  7325. }
  7326.  
  7327. class RgbArgClass extends STYLE {
  7328. constructor(ARG, N) {
  7329. super("Dynamic Color argument", arguments);
  7330. this.add_arg("ARG","ArgumentName","number to return.");
  7331. this.add_arg("DEFAULT","COLOR","default.");
  7332. }
  7333. run(blade) {
  7334. super.run(blade);
  7335. var d = Math.round(this.DEFAULT.r * 65535) + "," + Math.round(this.DEFAULT.g * 65535)+ "," +Math.round(this.DEFAULT.b * 65535);
  7336. var v = getARG(this.ARG, d).split(",");
  7337. this.value = Rgb16(parseInt(v[0]), parseInt(v[1]), parseInt(v[2]));
  7338. }
  7339. getColor(led) { return this.value; }
  7340. argify(state) {
  7341. if (state.color_argument == this.ARG) {
  7342. state.color_argument = false;
  7343. }
  7344. return this;
  7345. }
  7346. };
  7347.  
  7348. function RgbArg_(ARG, COLOR) {
  7349. return new RgbArgClass(ARG, COLOR);
  7350. }
  7351.  
  7352. class ScaleClass extends FUNCTION {
  7353. constructor(F, A, B) {
  7354. super("Changes values in range 0-32768 to A-B.", arguments);
  7355. this.add_arg("F","FUNCTION","input");
  7356. this.add_arg("A","FUNCTION","lower output limit");
  7357. this.add_arg("B","FUNCTION","upper output limit");
  7358. }
  7359. run(blade) {
  7360. super.run(blade);
  7361. var a = this.A.getInteger(0);
  7362. var b = this.B.getInteger(0);
  7363. this.mul = (b - a);
  7364. this.add = a;
  7365. }
  7366. getInteger(led) {
  7367. return (this.F.getInteger(led) * this.mul >> 15) + this.add;
  7368. }
  7369. };
  7370.  
  7371. function Scale(F, A, B) { return new ScaleClass(F, A, B); }
  7372.  
  7373. class InvertFClass extends MACRO {
  7374. constructor(F) {
  7375. super("Invert input function", arguments);
  7376. this.add_arg("F", "FUNCTION", "Function to invert.");
  7377. this.SetExpansion(Scale(this.F, Int(32768), Int(0)));
  7378. }
  7379. };
  7380.  
  7381. function InvertF(F) { return new InvertFClass(F); }
  7382.  
  7383.  
  7384. class SinClass extends FUNCTION {
  7385. constructor(RPM, LOW, HIGH) {
  7386. super("Pulses between LOW and HIGH RPM times per minute.", arguments);
  7387. this.add_arg("RPM", "FUNCTION", "Revolutions per minute");
  7388. this.add_arg("HIGH", "FUNCTION", "upper output limit", Int(32768));
  7389. this.add_arg("LOW", "FUNCTION", "lower output limit", Int(0));
  7390. this.pos = 0.0;
  7391. this.last_micros = 0;
  7392. }
  7393. run(blade) {
  7394. super.run(blade);
  7395. var now = micros();
  7396. var delta = now - this.last_micros;
  7397. this.last_micros = now;
  7398. this.pos = fract(this.pos + delta / 60000000.0 * this.RPM.getInteger(0));
  7399. var high = this.HIGH.getInteger(0);
  7400. var low = this.LOW.getInteger(0);
  7401. var tmp = Math.sin(this.pos * Math.PI * 2.0) / 2.0;
  7402. this.value = Math.floor( (tmp + 0.5) * (high - low) + low );
  7403. }
  7404. getInteger(led) { return this.value; }
  7405. };
  7406.  
  7407. function Sin(RPM, LOW, HIGH) { return new SinClass(RPM, LOW, HIGH); }
  7408.  
  7409. class SawClass extends FUNCTION {
  7410. constructor(RPM, LOW, HIGH) {
  7411. super("Pulses between LOW and HIGH RPM times per minute.", arguments);
  7412. this.add_arg("RPM", "FUNCTION", "Revolutions per minute");
  7413. this.add_arg("HIGH", "FUNCTION", "upper output limit", Int(32768));
  7414. this.add_arg("LOW", "FUNCTION", "lower output limit", Int(0));
  7415. this.pos = 0.0;
  7416. this.last_micros = 0;
  7417. }
  7418. run(blade) {
  7419. super.run(blade);
  7420. var now = micros();
  7421. var delta = now - this.last_micros;
  7422. this.last_micros = now;
  7423. this.pos = fract(this.pos + delta / 60000000.0 * this.RPM.getInteger(0));
  7424. var high = this.HIGH.getInteger(0);
  7425. var low = this.LOW.getInteger(0);
  7426. this.value = low + this.pos * (high - low);
  7427. }
  7428. getInteger(led) { return this.value; }
  7429. };
  7430.  
  7431. function Saw(RPM, LOW, HIGH) { return new SawClass(RPM, LOW, HIGH); }
  7432.  
  7433. const TRIGGER_DELAY = 0;
  7434. const TRIGGER_ATTACK = 1;
  7435. const TRIGGER_SUSTAIN = 2;
  7436. const TRIGGER_RELEASE = 3;
  7437. const TRIGGER_OFF = 4;
  7438.  
  7439. class TriggerClass extends FUNCTION {
  7440. constructor(EFFECT, FADE_IN_MILLIS, SUSTAIN_MILLIS, FADE_OUT_MILLIS, DELAY_MILLIS) {
  7441. super("When EFFECT occurs, DELAY_MILLIS controls a pause, then we ramp up from 0 to 32768, stay there for SUSTAIN_MILLIS, then ramp down again.", arguments);
  7442. this.add_arg("EFFECT", "EFFECT", "Trigger event");
  7443. this.add_arg("FADE_IN_MILLIS", "FUNCTION", "How long it takes to ramp to 32768");
  7444. this.add_arg("SUSTAIN_MILLIS", "FUNCTION", "Stay at 32768 for this long.");
  7445. this.add_arg("FADE_OUT_MILLIS", "FUNCTION", "How long it takes to ramp back down to zero.");
  7446. this.add_arg("DELAY_MILLIS", "FUNCTION", "How long to delay before trigger starts.", Int(0));
  7447.  
  7448. this.trigger_state = TRIGGER_OFF;
  7449. console.log("EFFECT INIT");
  7450. this.effect = new OneshotEffectDetector(this.EFFECT);
  7451. this.start_time = 0;
  7452. }
  7453. run(blade) {
  7454. super.run(blade);
  7455. if (this.effect.Detect(blade)) {
  7456. this.start_time = micros();
  7457. this.trigger_state = TRIGGER_DELAY;
  7458. }
  7459. if (this.trigger_state == this.TRIGGER_OFF) {
  7460. this.value = 0;
  7461. return;
  7462. }
  7463. var t = micros() - this.start_time;
  7464. while (true) {
  7465. var micros_for_state = this.get_millis_for_state() * 1000;
  7466. if (t < micros_for_state) {
  7467. switch (this.trigger_state) {
  7468. case TRIGGER_DELAY:
  7469. this.value = 0;
  7470. return;
  7471. case TRIGGER_ATTACK:
  7472. this.value = t * 32768.0 / micros_for_state;
  7473. return;
  7474. case TRIGGER_SUSTAIN:
  7475. this.value = 32768;
  7476. return;
  7477. case TRIGGER_RELEASE:
  7478. this.value = 32768 - t * 32768 / micros_for_state;
  7479. return;
  7480. case TRIGGER_OFF:
  7481. this.value = 0;
  7482. return;
  7483. }
  7484. }
  7485. if (this.TRIGGER_STATE >= 4) throw "Weird state?";
  7486. this.trigger_state++;
  7487. t -= micros_for_state;
  7488. this.start_time += micros_for_state;
  7489. }
  7490. }
  7491. get_millis_for_state() {
  7492. switch (this.trigger_state) {
  7493. case TRIGGER_DELAY: return this.DELAY_MILLIS.getInteger(0);
  7494. case TRIGGER_ATTACK: return this.FADE_IN_MILLIS.getInteger(0);
  7495. case TRIGGER_SUSTAIN: return this.SUSTAIN_MILLIS.getInteger(0);
  7496. case TRIGGER_RELEASE: return this.FADE_OUT_MILLIS.getInteger(0);
  7497. case TRIGGER_OFF:
  7498. }
  7499. return 10000000;
  7500. }
  7501. getInteger(led) { return this.value; }
  7502. IS_RUNNING() {
  7503. return this.trigger_state != TRIGGER_OFF;
  7504. }
  7505. };
  7506.  
  7507. function Trigger(EFFECT, FADE_IN_MILLIS, SUSTAIN_MILLIS, FADE_OUT_MILLIS, DELAY_MILLIS) {
  7508. return new TriggerClass(EFFECT, FADE_IN_MILLIS, SUSTAIN_MILLIS, FADE_OUT_MILLIS, DELAY_MILLIS);
  7509. }
  7510.  
  7511. class SmoothStepClass extends FUNCTION {
  7512. constructor(POS, WIDTH) {
  7513. super("SmoothStep function", arguments);
  7514. this.add_arg("POS", "FUNCTION", "Position 0=hilt, 32768=tip");
  7515. this.add_arg("WIDTH", "FUNCTION", "Step width 32768=length of blade");
  7516. }
  7517. run(blade) {
  7518. super.run(blade);
  7519. var width=this.WIDTH.getInteger(0);
  7520. if (width == 0) {
  7521. this.mult = 32768;
  7522. } else {
  7523. this.mult = 32768 * 32768 / width / blade.num_leds();
  7524. }
  7525. this.location = blade.num_leds() * this.mult * (this.POS.getInteger(0) - width/2) / 32768;
  7526. }
  7527. getInteger(led) {
  7528. var x = led * this.mult - this.location;
  7529. if (x < 0) return 0;
  7530. if (x > 32768) return 32768;
  7531. return (((x * x) >> 14) * ((3<<14) - x)) >> 15;
  7532. }
  7533. };
  7534.  
  7535. function SmoothStep(POS, WIDTH) { return new SmoothStepClass(POS, WIDTH); }
  7536.  
  7537. class RampFClass extends FUNCTION {
  7538. constructor() {
  7539. super("0 at base, 32768 at tip", arguments);
  7540. }
  7541. run(blade) {
  7542. this.num_leds = blade.num_leds();
  7543. }
  7544. getInteger(led) {
  7545. return led * 32768 / this.num_leds;
  7546. }
  7547. }
  7548.  
  7549. function RampF() {
  7550. return new RampFClass();
  7551. }
  7552.  
  7553. class MultClass extends FUNCTION {
  7554. constructor(ARGS) {
  7555. super("Multiply values", ARGS);
  7556. this.FUNCTIONS = Array.from(ARGS);
  7557. for (var i = 1; i < this.FUNCTIONS.length + 1; i++)
  7558. this.add_arg("FUNCTION" + i, "FUNCTION", "COLOR " + i);
  7559. }
  7560. getInteger(led) {
  7561. var ret = this.FUNCTIONS[0].getInteger(led);
  7562. for (var i = 1; i < this.FUNCTIONS.length; i++) {
  7563. ret = (ret * this.FUNCTIONS[i].getInteger(led)) >> 15;
  7564. }
  7565. return ret;
  7566. }
  7567. }
  7568.  
  7569. function Mult(ARGS) {
  7570. return new MultClass(Array.from(arguments));
  7571. }
  7572.  
  7573. class PercentageClass extends MACRO {
  7574. constructor(F, P) {
  7575. super("Returns P % of F.", arguments);
  7576. this.add_arg("F","FUNCTION","F");
  7577. this.add_arg("P","INT", "Percent")
  7578. this.SetExpansion(Mult(this.F.DOCOPY(), Int(this.P * 32768 / 100)));
  7579. }
  7580. }
  7581.  
  7582. function Percentage(F, P) {
  7583. return new PercentageClass(F, P);
  7584. }
  7585.  
  7586. class NoisySoundLevelClass extends FUNCTION {
  7587. constructor() {
  7588. super("Noisy sound level.", arguments);
  7589. }
  7590. run(blade) {
  7591. this.var_ = (Math.random() * Math.random()) * 32768;
  7592. }
  7593. getInteger(led) { return this.var_; }
  7594. };
  7595.  
  7596. function NoisySoundLevel() { return new NoisySoundLevelClass(); }
  7597.  
  7598. class NoisySoundLevelCompatClass extends FUNCTION {
  7599. constructor() {
  7600. super("Noisy sound level.", arguments);
  7601. }
  7602. run(blade) {
  7603. this.var_ = clamp((Math.random() * Math.random()) * 32768 * 2, 0, 32768);
  7604. }
  7605. getInteger(led) { return this.var_; }
  7606. };
  7607.  
  7608. function NoisySoundLevelCompat() { return new NoisySoundLevelCompatClass(); }
  7609.  
  7610. class SmoothSoundLevelClass extends FUNCTION {
  7611. constructor() {
  7612. super("Noisy sound level.", arguments);
  7613. this.var_ = 0.0;
  7614. }
  7615. run(blade) {
  7616. var v = Math.random() * 20000.0;
  7617. v *= v;
  7618. this.var_ = (this.var_ + v) / 100.0 ;
  7619. }
  7620. getInteger(led) { return this.var_; }
  7621. };
  7622.  
  7623. function SmoothSoundLevel() { return new SmoothSoundLevelClass(); }
  7624.  
  7625. class WavLenClass extends FUNCTION {
  7626. constructor() {
  7627. super("Length of associated wav file in MS", arguments);
  7628. this.add_arg("EFFECT", "EFFECT", "Which effect to get the length of.", EFFECT(EFFECT_NONE));
  7629. }
  7630. // WavLen value can be set in settings panel
  7631. setLength(value) {
  7632. this.wavlenValue = value;
  7633. console.log("Updated WavLen: ", this.wavlenValue);
  7634. }
  7635. getInteger(led) {
  7636. return myWavLen.wavlenValue;
  7637. }
  7638. };
  7639.  
  7640. function WavLen(EFFECT) { return new WavLenClass(EFFECT); }
  7641.  
  7642. class SwingSpeedXClass extends FUNCTION {
  7643. constructor() {
  7644. super("Swing Speed", arguments);
  7645. this.add_arg("MAX", "FUNCTION", "What swing speed returns 32768.");
  7646. this.var_ = 0.0;
  7647. }
  7648. run(blade) {
  7649. super.run(blade);
  7650. var speed = get_swing_speed();
  7651. var v = speed / this.MAX.getInteger(0);
  7652. this.var_ = clamp(v * 32768, 0, 32768);
  7653. }
  7654. getInteger(led) { return this.var_; }
  7655. };
  7656.  
  7657. function SwingSpeedX(MAX) { return new SwingSpeedXClass(MAX); }
  7658.  
  7659. class SwingSpeedClass extends MACRO {
  7660. constructor() {
  7661. super("Swing Speed", arguments);
  7662. this.add_arg("MAX", "INT", "What swing speed returns 32768.");
  7663. this.SetExpansion(SwingSpeedX(Int(this.MAX)));
  7664. }
  7665. };
  7666.  
  7667. function SwingSpeed(MAX) { return new SwingSpeedClass(MAX); }
  7668.  
  7669. class ClashImpactFXClass extends FUNCTION {
  7670. constructor(MIN, MAX) {
  7671. super("Returns clash strength.", arguments);
  7672. this.add_arg("MIN", "FUNCTION", "Minimum, translates to zero", Int(200));
  7673. this.add_arg("MAX", "FUNCTION", "Maximum, translates to 32768", Int(1600));
  7674. this.value = 0;
  7675. }
  7676. run(blade) {
  7677. super.run(blade);
  7678. current_clash_strength = max(current_clash_strength, random(current_clash_value));
  7679. current_clash_value -= random(random(current_clash_value));
  7680. this.value = clamp((current_clash_strength - this.MIN.getInteger(0)) * 32768 / this.MAX.getInteger(0), 0, 32768);
  7681. }
  7682. getInteger(led) {
  7683. return this.value;
  7684. }
  7685. };
  7686.  
  7687. function ClashImpactFX(MIN, MAX) {
  7688. return new ClashImpactFXClass(MIN, MAX);
  7689. }
  7690.  
  7691. class ClashImpactFClass extends MACRO {
  7692. constructor(MIN, MAX) {
  7693. super("Returns clash strength.", arguments);
  7694. this.add_arg("MIN", "INT", "Minimum, translates to zero", 200);
  7695. this.add_arg("MAX", "INT", "Maximum, translates to 32768", 1600);
  7696. this.SetExpansion(ClashImpactFX(Int(this.MIN), Int(this.MAX)));
  7697. }
  7698. }
  7699.  
  7700. function ClashImpactF(MIN, MAX) {
  7701. return new ClashImpactFClass(MIN, MAX);
  7702. }
  7703.  
  7704. class SwingAccelerationXClass extends FUNCTION {
  7705. constructor() {
  7706. super("Swing Acceleration", arguments);
  7707. this.add_arg("MAX", "FUNCTION", "What swing speed returns 32768.", Int(130));
  7708. this.var_ = 0.0;
  7709. }
  7710. run(blade) {
  7711. super.run(blade);
  7712. var accel = get_swing_accel();
  7713. var v = accel / this.MAX.getInteger(0);
  7714. this.var_ = clamp(v * 32768, 0, 32768);
  7715. }
  7716. getInteger(led) { return this.var_; }
  7717. };
  7718.  
  7719. function SwingAccelerationX(MAX) { return new SwingAccelerationXClass(MAX); }
  7720.  
  7721. class SwingAccelerationClass extends MACRO {
  7722. constructor() {
  7723. super("Swing Speed", arguments);
  7724. this.add_arg("MAX", "INT", "What swing speed returns 32768.", 130);
  7725. this.SetExpansion(SwingAccelerationX(Int(this.MAX)));
  7726. }
  7727. };
  7728.  
  7729. function SwingAcceleration(MAX) { return new SwingAccelerationClass(MAX); }
  7730.  
  7731.  
  7732. class LayerFunctionsClass extends FUNCTION {
  7733. constructor(ARGS) {
  7734. super("Mix functions", ARGS);
  7735. this.LAYERS = Array.from(ARGS);
  7736. for (var i = 1; i < this.LAYERS.length + 1; i++)
  7737. this.add_arg("FUNCTION" + i, "FUNCTION", "FUNCTION " + i);
  7738. }
  7739. getInteger(led) {
  7740. var ret = 0;
  7741. for (var i = 0; i < this.LAYERS.length; i++) {
  7742. ret = 32768 - ((((32768 - ret) * (32768 - this.LAYERS[i].getInteger(led)))) >> 15);
  7743. }
  7744. return ret;
  7745. }
  7746. };
  7747.  
  7748. function LayerFunctions(Layer1, Layer2) {
  7749. return new LayerFunctionsClass(Array.from(arguments));
  7750. }
  7751.  
  7752. class SlowNoiseClass extends FUNCTION {
  7753. constructor(SPEED) {
  7754. super("Returns a value between 0 and 32768, which slowly changes up and down randomly.", Array.from(arguments));
  7755. this.add_arg("SPEED", "FUNCTION", "Change speed");
  7756. this.value = random(32768);
  7757. }
  7758. run(blade) {
  7759. super.run(blade);
  7760. var now = millis();
  7761. var delta = now - this.last_millis;
  7762. this.last_millis = now;
  7763. if (delta > 100) delta = 1;
  7764. var speed = this.SPEED.getInteger(0);
  7765. // console.log("DELTA = " + delta + " SPEED = " + speed + " VALUE="+this.value);
  7766. while (delta > 0) {
  7767. this.value = clamp(this.value + random(speed * 2 + 1) - speed, 0, 32768);
  7768. delta--;
  7769. }
  7770. }
  7771. getInteger(led) { return this.value; }
  7772. };
  7773.  
  7774. function SlowNoise(SPEED) {
  7775. return new SlowNoiseClass(SPEED);
  7776. }
  7777.  
  7778. class IsLessThanClass extends FUNCTION {
  7779. constructor(F, V) {
  7780. super("Returns 32768 if F < V, 0 otherwise.", arguments);
  7781. this.add_arg("F", "FUNCTION", "F");
  7782. this.add_arg("V", "FUNCTION", "V");
  7783. }
  7784. getInteger(led) {
  7785. return this.F.getInteger(led) < this.V.getInteger(led) ? 32768 : 0;
  7786. }
  7787. };
  7788.  
  7789. function IsLessThan(F, V) {
  7790. return new IsLessThanClass(F, V);
  7791. }
  7792.  
  7793. class IsGreaterThanClass extends MACRO {
  7794. constructor(F, V) {
  7795. super("Returns 32768 if F > V, 0 otherwise.", arguments);
  7796. this.add_arg("F", "FUNCTION", "F");
  7797. this.add_arg("V", "FUNCTION", "V");
  7798. this.SetExpansion(IsLessThan(V.DOCOPY(), F.DOCOPY()));
  7799. }
  7800. };
  7801.  
  7802. function IsGreaterThan(F, V) {
  7803. return new IsGreaterThanClass(F, V);
  7804. }
  7805.  
  7806. class IsBetweenClass extends FUNCTION {
  7807. constructor(F, BOTTOM, TOP) {
  7808. super("Returns 32768 if BOTTOM < F < TOP, 0 otherwise.", arguments);
  7809. this.add_arg("F", "FUNCTION", "F");
  7810. this.add_arg("BOTTOM", "FUNCTION", "BOTTOM");
  7811. this.add_arg("TOP", "FUNCTION", "TOP");
  7812. }
  7813. getInteger(led) {
  7814. var f = this.F.getInteger(led);
  7815. return this.BOTTOM.getInteger(led) < f && f < this.TOP.getInteger(led) ? 32768 : 0;
  7816. }
  7817. };
  7818.  
  7819. function IsBetween(F, BOTTOM, TOP) {
  7820. return new IsBetweenClass(F, BOTTOM, TOP);
  7821. }
  7822.  
  7823. class ClampFXClass extends FUNCTION {
  7824. constructor(F, MIN, MAX) {
  7825. super("Returns F, clamped to MIN...MAX", arguments);
  7826. this.add_arg("F", "FUNCTION", "F");
  7827. this.add_arg("MIN", "FUNCTION", "MIN");
  7828. this.add_arg("MAX", "FUNCTION", "MAX");
  7829. }
  7830. getInteger(led) {
  7831. return clamp(this.F.getInteger(led), this.MIN.getInteger(led), this.MAX.getInteger(led));
  7832. }
  7833. };
  7834.  
  7835. function ClampFX(F, MIN, MAX) {
  7836. return new ClampFXClass(F, MIN, MAX);
  7837. }
  7838.  
  7839. class ClampFClass extends MACRO {
  7840. constructor(F, MIN, MAX) {
  7841. super("Returns F, clamped to MIN...MAX", arguments);
  7842. this.add_arg("F", "FUNCTION", "F");
  7843. this.add_arg("MIN", "INT", "MIN");
  7844. this.add_arg("MAX", "INT", "MAX");
  7845. this.SetExpansion(ClampFX(F.DOCOPY(), Int(MIN), Int(MAX)));
  7846. }
  7847. };
  7848.  
  7849. function ClampF(F, MIN, MAX) {
  7850. return new ClampFClass(F, MIN, MAX);
  7851. }
  7852.  
  7853. class VariationClass extends FUNCTION {
  7854. constructor() {
  7855. super("Returns the current variation", arguments);
  7856. }
  7857. getInteger(led) {
  7858. return Variant() & 0x7fff;
  7859. }
  7860. };
  7861.  
  7862. function Variation() {
  7863. return new VariationClass();
  7864. }
  7865.  
  7866. class AltFClass extends FUNCTION {
  7867. constructor() {
  7868. super("Returns the current alt", arguments);
  7869. }
  7870. getInteger(led) {
  7871. return Alt() & 0x7fff;
  7872. }
  7873. };
  7874.  
  7875. function AltF() {
  7876. return new AltFClass();
  7877. }
  7878.  
  7879. function MOD(x, m) {
  7880. if (x >= 0) return x % m;
  7881. return m + ~((~x) % m)
  7882. }
  7883.  
  7884. class SyncAltToVarianceFClass extends FUNCTION {
  7885. constructor() {
  7886. super("Synchronizes Alt and Variance.", arguments);
  7887. this.last_ = 0x7fffffff;
  7888. }
  7889. run(blade) {
  7890. super.run(blade)
  7891. if (num_alternatives == 0) return;
  7892. var VAR = MOD(Variant(), num_alternatives);
  7893. if (VAR == this.last_ && Alt() == this.last_) return;
  7894. if (this.last_ == 0x7fffffff) {
  7895. console.log("SYNC FIRST");
  7896. FIND("ALT").value = VAR;
  7897. } else if (VAR != this.last_) {
  7898. if (isNaN(VAR)) VAR = 0;
  7899. console.log("SYNC ALT: " + VAR);
  7900. FIND("ALT").value = VAR;
  7901. blade.addEffect(EFFECT_ALT_SOUND, 0.0);
  7902. } else {
  7903. VAR = Alt();
  7904. if (isNaN(VAR)) VAR = 0;
  7905. console.log("SYNC VAR: " + VAR);
  7906. FIND("VARIANT_VALUE").value = VAR;
  7907. }
  7908. this.last_ = VAR;
  7909. }
  7910. getInteger(led) { return 0; }
  7911. }
  7912.  
  7913. function SyncAltToVarianceF() {
  7914. return new SyncAltToVarianceFClass();
  7915. }
  7916.  
  7917. class SyncAltToVarianceLClass extends MACRO {
  7918. constructor() {
  7919. super("Invisble layer for synchronizing Alt and Variance.", arguments);
  7920. this.SetExpansion(AlphaL(BLACK, SyncAltToVarianceF()));
  7921. }
  7922. }
  7923.  
  7924. function SyncAltToVarianceL() {
  7925. return new SyncAltToVarianceLClass();
  7926. }
  7927.  
  7928. class EffectPulseFClass extends FUNCTION {
  7929. constructor() {
  7930. super("Generate a pulse every time an effect occurs", arguments);
  7931. this.add_arg("EFFECT", "EFFECT", "Effect to trigger new random.");
  7932. this.effect = new OneshotEffectDetector(this.EFFECT);
  7933. this.value = 0;
  7934. }
  7935. run(blade) {
  7936. super.run(blade);
  7937. if (this.effect.Detect(blade)) {
  7938. this.value = 32768;
  7939. } else {
  7940. this.value = 0;
  7941. }
  7942. }
  7943. getInteger(led) { return this.value; }
  7944. };
  7945.  
  7946. function EffectPulseF(EFFECT) {
  7947. return new EffectPulseFClass(EFFECT);
  7948. }
  7949.  
  7950. class IncrementWithResetClass extends FUNCTION {
  7951. constructor(PULSE, RESET_PULSE, MAX, I) {
  7952. super("Increment by I each time PULSE occurs.", arguments);
  7953. this.add_arg("PULSE", "FUNCTION", "Pulse.");
  7954. this.add_arg("RESET_PULSE", "FUNCTION", "Reset pulse.", Int(0));
  7955. this.add_arg("MAX", "FUNCTION", "Max value", Int(32768));
  7956. this.add_arg("I", "FUNCTION", "Increment", Int(1));
  7957. this.value = 0;
  7958. }
  7959. run(blade) {
  7960. super.run(blade);
  7961. if (this.RESET_PULSE.getInteger(0)) {
  7962. this.value = 0;
  7963. }
  7964. if (this.PULSE.getInteger(0)) {
  7965. this.value = min(this.value + this.I.getInteger(0), this.MAX.getInteger(0));;
  7966. }
  7967. }
  7968. getInteger(led) { return this.value; }
  7969. }
  7970.  
  7971. function IncrementWithReset(PULSE, RESET_PULSE, MAX, I) {
  7972. return new IncrementWithResetClass(PULSE, RESET_PULSE, MAX, I);
  7973. }
  7974.  
  7975. class IncrementModuloFClass extends FUNCTION {
  7976. constructor(PULSE, MAX, I) {
  7977. super("Increment by I each time PULSE occurs.", arguments);
  7978. this.add_arg("PULSE", "FUNCTION", "Pulse.");
  7979. this.add_arg("MAX", "FUNCTION", "Max value", Int(32768));
  7980. this.add_arg("I", "FUNCTION", "Increment", Int(1));
  7981. this.value = 0;
  7982. }
  7983. run(blade) {
  7984. super.run(blade);
  7985. if (this.PULSE.getInteger(0)) {
  7986. this.value = (this.value + this.I.getInteger(0)) % this.MAX.getInteger(0);
  7987. }
  7988. }
  7989. getInteger(led) { return this.value; }
  7990. }
  7991.  
  7992. function IncrementModuloF(PULSE, MAX, I) {
  7993. return new IncrementModuloFClass(PULSE, MAX, I);
  7994. }
  7995.  
  7996. class ThresholdPulseFClass extends FUNCTION {
  7997. constructor(F, THRESHOLD, HYST_PERCENT) {
  7998. super("Generate a Pulse when F > THRESHOLD.", arguments);
  7999. this.add_arg("F", "FUNCTION", "Input");
  8000. this.add_arg("THRESHOLD", "FUNCTION", "Threshold", Int(32768));
  8001. this.add_arg("HYST_PERCENT", "FUNCTION", "Hysteresis percent", Int(66));
  8002. this.value = 0;
  8003. this.triggered = 0;
  8004. }
  8005. run(blade) {
  8006. super.run(blade);
  8007. var f = this.F.getInteger(0);
  8008. var threshold = this.THRESHOLD.getInteger(0);
  8009. this.value = 0;
  8010. if (this.triggered) {
  8011. if (f < threshold * this.HYST_PERCENT.getInteger(0) / 100) {
  8012. this.triggered = false;
  8013. }
  8014. } else {
  8015. if (f >= threshold) {
  8016. this.triggered = true;
  8017. this.value = 32768;
  8018. }
  8019. }
  8020. }
  8021. getInteger(led) { return this.value; }
  8022. }
  8023.  
  8024. function ThresholdPulseF(F, THRESHOLD, HYST_PERCENT) {
  8025. return new ThresholdPulseFClass(F, THRESHOLD, HYST_PERCENT);
  8026. }
  8027.  
  8028. class IncrementFClass extends MACRO {
  8029. constructor(F, V, MAX, I, HYST_PERCENT) {
  8030. super("Increase by I every time F > V.", arguments);
  8031. this.add_arg("F", "FUNCTION", "Input");
  8032. this.add_arg("V", "FUNCTION", "Compare value.", Int(32768));
  8033. this.add_arg("MAX", "FUNCTION", "Max value.", Int(32768));
  8034. this.add_arg("I", "FUNCTION", "Increment", Int(1));
  8035. this.add_arg("HYST_PERCENT", "FUNCTION", "Hysteresis percent", Int(66));
  8036. this.SetExpansion(IncrementModuloF(ThresholdPulseF(this.F, this.V, this.HYST_PERCENT), this.MAX, this.I));
  8037. }
  8038. };
  8039.  
  8040. function IncrementF(F, V, MAX, I, HYST_PERCENT) {
  8041. return new IncrementFClass(F, V, MAX, I, HYST_PERCENT);
  8042. }
  8043.  
  8044. class EffectIncrementFClass extends MACRO {
  8045. constructor(EFFECT, MAX, I) {
  8046. super("Increase by I every time F > V.", arguments);
  8047. this.add_arg("EFFECT", "EFFECT", "Effect to trigger increment.");
  8048. this.add_arg("MAX", "FUNCTION", "Max value.", Int(32768));
  8049. this.add_arg("I", "FUNCTION", "Increment", Int(1));
  8050. this.SetExpansion(IncrementModuloF(EffectPulseF(this.EFFECT), this.MAX, this.I));
  8051. }
  8052. };
  8053.  
  8054. function EffectIncrementF(EFFECT, MAX, I) {
  8055. return new EffectIncrementFClass(EFFECT, MAX, I);
  8056. }
  8057.  
  8058. class EffectRandomFClass extends FUNCTION {
  8059. constructor() {
  8060. super("Select a new random value every time an effect occurs", arguments);
  8061. this.add_arg("EFFECT", "EFFECT", "Effect to trigger new random.");
  8062. this.effect = new OneshotEffectDetector(this.EFFECT);
  8063. this.value = random(32768);
  8064. }
  8065. run(blade) {
  8066. super.run(blade);
  8067. if (this.effect.Detect(blade)) {
  8068. this.value = random(32768);
  8069. }
  8070. }
  8071.  
  8072. getInteger(led) { return this.value; }
  8073. };
  8074.  
  8075. function EffectRandomF(EFFECT) {
  8076. return new EffectRandomFClass(EFFECT);
  8077. }
  8078.  
  8079. class EffectPositionClass extends FUNCTION {
  8080. constructor() {
  8081. super("Select a new random value every time an effect occurs", arguments);
  8082. this.add_arg("EFFECT", "EFFECT", "Effect to trigger new random.", EFFECT(EFFECT_NONE));
  8083. this.value = 0;
  8084. }
  8085. run(blade) {
  8086. super.run(blade);
  8087. var effect;
  8088. if (this.EFFECT+0 == 0) {
  8089. effect = last_detected_blade_effect;
  8090. } else {
  8091. var e = new OneshotEffectDetector(this.EFFECT);
  8092. effect = e.Detect(blade);
  8093. }
  8094. if (effect) {
  8095. this.value = effect.location * 32768;
  8096. } else {
  8097. this.value = 0;
  8098. }
  8099. }
  8100. getInteger(led) {
  8101. return this.value;
  8102. }
  8103. };
  8104.  
  8105. function EffectPosition(EFFECT) {
  8106. return new EffectPositionClass(EFFECT);
  8107. }
  8108.  
  8109. class TimeSinceEffectClass extends FUNCTION {
  8110. constructor() {
  8111. super("Returns milliseconds since effect occured", arguments);
  8112. this.add_arg("EFFECT", "EFFECT", "Effect to get time since.", EFFECT(EFFECT_NONE));
  8113. this.value = 0;
  8114. }
  8115. run(blade) {
  8116. super.run(blade);
  8117. var effect;
  8118. if (this.EFFECT == 0) {
  8119. effect = last_detected_blade_effect;
  8120. } else {
  8121. var e = new OneshotEffectDetector(this.EFFECT);
  8122. effect = e.Detect(blade);
  8123. }
  8124. if (effect) {
  8125. this.value = (micros() - effect.start_micros) / 1000;
  8126. }
  8127. }
  8128. getInteger(led) {
  8129. return this.value;
  8130. }
  8131. };
  8132.  
  8133. function TimeSinceEffect(EFFECT) {
  8134. return new TimeSinceEffectClass(EFFECT);
  8135. }
  8136.  
  8137. class WavNumClass extends FUNCTION {
  8138. constructor() {
  8139. super("Returns milliseconds since effect occured", arguments);
  8140. this.add_arg("EFFECT", "EFFECT", "Effect to get time since.", EFFECT(EFFECT_NONE));
  8141. this.value = 0;
  8142. }
  8143. run(blade) {
  8144. super.run(blade);
  8145. var effect;
  8146. if (this.EFFECT == 0) {
  8147. effect = last_detected_blade_effect;
  8148. } else {
  8149. var e = new OneshotEffectDetector(this.EFFECT);
  8150. effect = e.Detect(blade);
  8151. }
  8152. if (effect) {
  8153. this.value = effect.wavnum;
  8154. }
  8155. }
  8156. getInteger(led) {
  8157. return this.value;
  8158. }
  8159. };
  8160.  
  8161. function WavNum(EFFECT) {
  8162. return new WavNumClass(EFFECT);
  8163. }
  8164.  
  8165. class CenterDistFClass extends FUNCTION {
  8166. constructor(CENTER) {
  8167. super("Distance from center.", arguments);
  8168. this.add_arg("CENTER", "FUNCTION", "Center point", Int(16384));
  8169. }
  8170. run(blade) {
  8171. super.run(blade);
  8172. this.num_leds = blade.num_leds();
  8173. }
  8174. getInteger(led) {
  8175. return Math.abs(led * 32768 / this.num_leds - this.CENTER.getInteger(led));
  8176. }
  8177. };
  8178.  
  8179. function CenterDistF(CENTER) {
  8180. return new CenterDistFClass(CENTER);
  8181. }
  8182.  
  8183. class BladeAngleXClass extends FUNCTION {
  8184. constructor() {
  8185. super("Blade Angle", arguments);
  8186. this.add_arg("MIN", "FUNCTION", "What angle returns 0.", Int(0));
  8187. this.add_arg("MAX", "FUNCTION", "What angle returns 32768.", Int(32768));
  8188. }
  8189. run(blade) {
  8190. super.run(blade);
  8191. if (IN_FRAME) {
  8192. var min = this.MIN.getInteger(0);
  8193. var max = this.MAX.getInteger(0);
  8194. var v = fract( (BLADE_ANGLE + Math.PI / 2) / Math.PI);
  8195. if (v > 1) v = 2 - v;
  8196. v *= 32768.0;
  8197. this.var_ = clamp((v - min) * 32768 / (max - min), 0, 32768);
  8198. } else {
  8199. var v = Math.sin(millis() * Math.PI / 10000.0)/2.0 + 0.5;
  8200. this.var_ = clamp(v * 32768, 0, 32768);
  8201. }
  8202. }
  8203. getInteger(led) { return this.var_; }
  8204. };
  8205.  
  8206. function BladeAngleX(MIN, MAX) {
  8207. return new BladeAngleXClass(MIN, MAX);
  8208. }
  8209.  
  8210. class TwistAngleClass extends FUNCTION {
  8211. constructor() {
  8212. super("Twist Angle", arguments);
  8213. this.add_arg("N", "INT", "Number of up/downs per rotation.", 2);
  8214. this.add_arg("OFFSET", "INT", "Angular offset", 0);
  8215. }
  8216. run(blade) {
  8217. super.run(blade);
  8218. var v = Math.sin(millis() * Math.PI / 3000.0)/2.0 + 0.5;
  8219. this.var_ = clamp(v * 32768, 0, 32768);
  8220. }
  8221. getInteger(led) { return this.var_; }
  8222. };
  8223.  
  8224. function TwistAngle(N, OFFSET) {
  8225. return new TwistAngleClass(N, OFFSET);
  8226. }
  8227.  
  8228. class TwistAccelerationClass extends FUNCTION {
  8229. constructor() {
  8230. super("Twist Acceleration", arguments);
  8231. this.add_arg("MAX", "INT", "Acceleration needed to return 32768", 90);
  8232. }
  8233. run(blade) {
  8234. super.run(blade);
  8235. var v = Math.cos(millis() * Math.PI / 3000.0)/2.0 + 0.5;
  8236. this.var_ = clamp(v * 32768, 0, 32768);
  8237. }
  8238. getInteger(led) { return this.var_; }
  8239. };
  8240.  
  8241. function TwistAcceleration(N, OFFSET) {
  8242. return new TwistAccelerationClass(N, OFFSET);
  8243. }
  8244.  
  8245. class BladeAngleClass extends MACRO {
  8246. constructor() {
  8247. super("Blade Angle", arguments);
  8248. this.add_arg("MIN", "INT", "What angle returns 0.", 0);
  8249. this.add_arg("MAX", "INT", "What angle returns 32768.", 32768);
  8250. this.SetExpansion(BladeAngleX(Int(this.MIN), Int(this.MAX)));
  8251. }
  8252. };
  8253.  
  8254. function BladeAngle(MIN, MAX) {
  8255. return new BladeAngleClass(MIN, MAX);
  8256. }
  8257.  
  8258. class LockupPulseFClass extends FUNCTION {
  8259. constructor() {
  8260. super("32768 if specified lockup type, 0 otherwise.", arguments);
  8261. this.add_arg("LOCKUP_TYPE", "LOCKUP_TYPE", "Lockup type");
  8262. this.value_ = 0;
  8263. }
  8264. run(blade) {
  8265. super.run(blade)
  8266. if (STATE_LOCKUP == this.LOCKUP_TYPE) {
  8267. this.value_ = 32768;
  8268. } else {
  8269. this.value_ = 0;
  8270. }
  8271. }
  8272. getInteger(led) { return this.value_; }
  8273. }
  8274.  
  8275. function LockupPulseF(LOCKUP_TYPE) {
  8276. return new LockupPulseFClass(LOCKUP_TYPE);
  8277. }
  8278.  
  8279. class ResponsiveLockupLClass extends MACRO {
  8280. constructor(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8281. super("Responsive localized lockup layer.", arguments);
  8282. this.add_arg("COLOR", "COLOR", "Color.");
  8283. this.add_arg("TR1", "TRANSITION", "Begin transition", TrInstant());
  8284. this.add_arg("TR2", "TRANSITION", "End transition", TrInstant());
  8285. this.add_arg("TOP", "FUNCTION", "uppermost lockup limit", Scale(BladeAngle(0, 16000), Int(4000), Int(26000)));
  8286. this.add_arg("BOTTOM", "FUNCTION", "lowermost lockup limit", Int(6000));
  8287. this.add_arg("SIZE", "FUNCTION", "lockup size", Scale(SwingSpeed(100), Int(9000), Int(14000)));
  8288. this.SetExpansion(LockupTrL(AlphaL(COLOR, Bump(Scale(BladeAngle(), this.TOP, this.BOTTOM), this.SIZE)),
  8289. this.TR1,
  8290. this.TR2,
  8291. LOCKUP_TYPE(LOCKUP_NORMAL)));;
  8292. }
  8293. argify(state) {
  8294. state.color_argument = LOCKUP_COLOR_ARG;
  8295. var ret = super.argify(state);
  8296. state.color_argument = null;
  8297. return ret;
  8298. }
  8299. };
  8300.  
  8301. function ResponsiveLockupL(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8302. return new ResponsiveLockupLClass(COLOR, TR1, TR2, TOP, BOTTOM, SIZE);
  8303. }
  8304.  
  8305. class ResponsiveDragLClass extends MACRO {
  8306. constructor(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8307. super("Responsive localized drag layer.", arguments);
  8308. this.add_arg("COLOR", "COLOR", "Color.");
  8309. this.add_arg("TR1", "TRANSITION", "Begin transition", TrInstant());
  8310. this.add_arg("TR2", "TRANSITION", "End transition", TrInstant());
  8311. this.add_arg("SIZE1", "FUNCTION", "lower twist limit", Int(2000));
  8312. this.add_arg("SIZE2", "FUNCTION", "upper twist limit", Int(10000));
  8313. this.SetExpansion(LockupTrL(AlphaL(COLOR, SmoothStep(Int(32000), Scale(TwistAngle(), this.SIZE1, this.SIZE2))),
  8314. this.TR1,
  8315. this.TR2,
  8316. LOCKUP_TYPE(LOCKUP_DRAG)));
  8317. }
  8318. argify(state) {
  8319. state.color_argument = DRAG_COLOR_ARG;
  8320. var ret = super.argify(state);
  8321. state.color_argument = null;
  8322. return ret;
  8323. }
  8324. };
  8325.  
  8326. function ResponsiveDragL(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8327. return new ResponsiveDragLClass(COLOR, TR1, TR2, TOP, BOTTOM, SIZE);
  8328. }
  8329.  
  8330. class ResponsiveMeltLClass extends MACRO {
  8331. constructor(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8332. super("Responsive localized melt layer.", arguments);
  8333. this.add_arg("COLOR", "COLOR", "Color.", Mix(TwistAngle(), OrangeRed.DOCOPY(), RED.DOCOPY()));
  8334. this.add_arg("TR1", "TRANSITION", "Begin transition", TrWipeIn(600));
  8335. this.add_arg("TR2", "TRANSITION", "End transition", TrWipe(600));
  8336. this.add_arg("SIZE1", "FUNCTION", "lower twist limit", Int(4000));
  8337. this.add_arg("SIZE2", "FUNCTION", "upper twist limit", Int(10000));
  8338. this.SetExpansion(LockupTrL(AlphaL(this.COLOR, SmoothStep(Int(30000), Scale(TwistAngle(), this.SIZE1, this.SIZE2))),
  8339. this.TR1,
  8340. this.TR2,
  8341. LOCKUP_TYPE(LOCKUP_MELT)));
  8342. }
  8343. };
  8344.  
  8345. function ResponsiveMeltL(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8346. return new ResponsiveMeltLClass(COLOR, TR1, TR2, TOP, BOTTOM, SIZE);
  8347. }
  8348.  
  8349. class ResponsiveLightningBlockLClass extends MACRO {
  8350. constructor(COLOR, TR1, TR2) {
  8351. super("Responsive lightning block layer", arguments);
  8352. this.add_arg("COLOR", "COLOR", "Color.");
  8353. this.add_arg("TR1", "TRANSITION", "Begin transition", TrInstant());
  8354. this.add_arg("TR2", "TRANSITION", "End transition", TrInstant());
  8355. this.SetExpansion(
  8356. LockupTrL(
  8357. AlphaL(COLOR,
  8358. LayerFunctions(
  8359. Bump(Scale(SlowNoise(Scale(BladeAngle(24000,32768),Int(2100),Int(1000))),Scale(BladeAngle(24000,32768),Int(3000),Int(10000)),Int(16000)),
  8360. Scale(BrownNoiseF(Int(10)),Scale(TwistAngle(),Int(4000),Int(10000)),Scale(TwistAngle(),Int(9000),Int(14000)))),
  8361. Bump(Scale(SlowNoise(Int(2200)),Scale(BladeAngle(24000,32768),Int(26000),Int(18000)),Int(8000)),
  8362. Scale(NoisySoundLevel(),Scale(TwistAngle(),Int(6000),Int(10000)),Scale(TwistAngle(),Int(10000),Int(14000)))),
  8363. Bump(Scale(SlowNoise(Int(2300)),Scale(BladeAngle(24000,32768),Int(20000),Int(16000)),Scale(BladeAngle(24000,32768),Int(30000),Int(24000))),
  8364. Scale(IsLessThan(SlowNoise(Int(2000)),Int(12000)),Scale(NoisySoundLevel(),Scale(TwistAngle(),Int(9000),Int(5000)),Int(0)),Int(0))))),
  8365. this.TR1,
  8366. this.TR2,
  8367. LOCKUP_TYPE(LOCKUP_LIGHTNING_BLOCK)));
  8368. }
  8369. argify(state) {
  8370. state.color_argument = LB_COLOR_ARG;
  8371. var ret = super.argify(state);
  8372. state.color_argument = null;
  8373. return ret;
  8374. }
  8375. };
  8376.  
  8377. function ResponsiveLightningBlockL(COLOR, TR1, TR2) {
  8378. return new ResponsiveLightningBlockLClass(COLOR, TR1, TR2);
  8379. }
  8380.  
  8381. class ResponsiveClashLClass extends MACRO {
  8382. constructor(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8383. super("Responsive localized lockup layer.", arguments);
  8384. this.add_arg("COLOR", "COLOR", "Color.");
  8385. this.add_arg("TR1", "TRANSITION", "Begin transition", TrInstant());
  8386. this.add_arg("TR2", "TRANSITION", "End transition", TrFade(200));
  8387. this.add_arg("TOP", "FUNCTION", "uppermost lockup limit", Scale(BladeAngle(0, 16000), Int(4000), Int(26000)));
  8388. this.add_arg("BOTTOM", "FUNCTION", "lowermost lockup limit", Int(6000));
  8389. this.add_arg("SIZE", "FUNCTION", "lockup size", Int(10000));
  8390. this.SetExpansion(TransitionEffectL(TrConcat(this.TR1,
  8391. AlphaL(COLOR, Bump(Scale(BladeAngle(), this.TOP, this.BOTTOM), this.SIZE)),
  8392. this.TR2),
  8393. EFFECT(EFFECT_CLASH)));
  8394. }
  8395. argify(state) {
  8396. state.color_argument = CLASH_COLOR_ARG;
  8397. var ret = super.argify(state);
  8398. state.color_argument = null;
  8399. return ret;
  8400. }
  8401. };
  8402.  
  8403. function ResponsiveClashL(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8404. return new ResponsiveClashLClass(COLOR, TR1, TR2, TOP, BOTTOM, SIZE);
  8405. }
  8406.  
  8407. class ResponsiveBlastLClass extends MACRO {
  8408. constructor(COLOR, FADE, SIZE, SPEED, TOP, BOTTOM, EFFECT_ARG) {
  8409. super("Responsive localized blast layer.", arguments);
  8410. this.add_arg("COLOR", "COLOR", "Color.");
  8411. this.add_arg("FADE", "FUNCTION", "fadeout time", Int(400));
  8412. this.add_arg("SIZE", "FUNCTION", "blast size", Int(100));
  8413. this.add_arg("SPEED", "FUNCTION", "blast speed", Int(400));
  8414. this.add_arg("TOP", "FUNCTION", "uppermost blast limit", Int(28000));
  8415. this.add_arg("BOTTOM", "FUNCTION", "lowermost blast limit", Int(8000));
  8416. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  8417. this.SetExpansion(
  8418. AlphaL(MultiTransitionEffectL(
  8419. TrWaveX(this.COLOR, this.FADE, this.SIZE, this.SPEED, Scale(BladeAngle(), this.TOP, this.BOTTOM)),
  8420. this.EFFECT),
  8421. Bump(Scale(BladeAngle(), this.TOP, this.BOTTOM), Int(24000))));
  8422. }
  8423. argify(state) {
  8424. state.color_argument = BLAST_COLOR_ARG;
  8425. var ret = super.argify(state);
  8426. state.color_argument = null;
  8427. return ret;
  8428. }
  8429. };
  8430.  
  8431. function ResponsiveBlastL(COLOR, FADE, SIZE, SPEED, TOP, BOTTOM, EFFECT) {
  8432. return new ResponsiveBlastLClass(COLOR, FADE, SIZE, SPEED, TOP, BOTTOM, EFFECT);
  8433. }
  8434.  
  8435. class ResponsiveBlastWaveLClass extends MACRO {
  8436. constructor(COLOR, FADE, SIZE, SPEED, TOP, BOTTOM, EFFECT_ARG) {
  8437. super("Responsive localized blast layer.", arguments);
  8438. this.add_arg("COLOR", "COLOR", "Color.");
  8439. this.add_arg("FADE", "FUNCTION", "fadeout time", Int(400));
  8440. this.add_arg("SIZE", "FUNCTION", "blast size", Int(100));
  8441. this.add_arg("SPEED", "FUNCTION", "blast speed", Int(400));
  8442. this.add_arg("TOP", "FUNCTION", "uppermost blast limit", Int(28000));
  8443. this.add_arg("BOTTOM", "FUNCTION", "lowermost blast limit", Int(8000));
  8444. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  8445. this.SetExpansion(
  8446. MultiTransitionEffectL(
  8447. TrWaveX(this.COLOR, this.FADE, this.SIZE, this.SPEED, Scale(BladeAngle(), this.TOP, this.BOTTOM)),
  8448. this.EFFECT));
  8449.  
  8450. }
  8451. argify(state) {
  8452. state.color_argument = BLAST_COLOR_ARG;
  8453. var ret = super.argify(state);
  8454. state.color_argument = null;
  8455. return ret;
  8456. }
  8457. };
  8458.  
  8459. function ResponsiveBlastWaveL(COLOR, FADE, SIZE, SPEED, TOP, BOTTOM, EFFECT) {
  8460. return new ResponsiveBlastWaveLClass(COLOR, FADE, SIZE, SPEED, TOP, BOTTOM, EFFECT);
  8461. }
  8462.  
  8463. class ResponsiveBlastFadeLClass extends MACRO {
  8464. constructor(COLOR, FADE, SIZE, TOP, BOTTOM, EFFECT_ARG) {
  8465. super("Responsive localized blast layer.", arguments);
  8466. this.add_arg("COLOR", "COLOR", "Color.");
  8467. this.add_arg("SIZE", "FUNCTION", "blast size", Int(8000));
  8468. this.add_arg("FADE", "FUNCTION", "fadeout time", Int(400));
  8469. this.add_arg("TOP", "FUNCTION", "uppermost blast limit", Int(28000));
  8470. this.add_arg("BOTTOM", "FUNCTION", "lowermost blast limit", Int(8000));
  8471. this.add_arg("EFFECT", "EFFECT", "effect type", EFFECT(EFFECT_BLAST));
  8472. this.SetExpansion(
  8473. MultiTransitionEffectL(
  8474. TrConcat(TrInstant(),
  8475. AlphaL(this.COLOR, Bump(Scale(BladeAngle(), this.TOP, this.BOTTOM), this.SIZE)),
  8476. TrFadeX(this.FADE)),
  8477. this.EFFECT));
  8478.  
  8479. }
  8480. argify(state) {
  8481. state.color_argument = BLAST_COLOR_ARG;
  8482. var ret = super.argify(state);
  8483. state.color_argument = null;
  8484. return ret;
  8485. }
  8486. };
  8487.  
  8488. function ResponsiveBlastFadeL(COLOR, FADE, SIZE, TOP, BOTTOM, EFFECT) {
  8489. return new ResponsiveBlastFadeLClass(COLOR, FADE, SIZE, TOP, BOTTOM, EFFECT);
  8490. }
  8491.  
  8492. class ResponsiveStabLClass extends MACRO {
  8493. constructor(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8494. super("Responsive localized stab layer.", arguments);
  8495. this.add_arg("COLOR", "COLOR", "Color.");
  8496. this.add_arg("TR1", "TRANSITION", "Begin transition", TrWipeIn(600));
  8497. this.add_arg("TR2", "TRANSITION", "End transition", TrWipe(600));
  8498. this.add_arg("SIZE1", "FUNCTION", "lower twist limit", Int(14000));
  8499. this.add_arg("SIZE2", "FUNCTION", "upper twist limit", Int(8000));
  8500. this.SetExpansion(
  8501. TransitionEffectL(TrConcat(this.TR1,
  8502. AlphaL(COLOR, SmoothStep(Int(32000), Scale(BladeAngle(), this.SIZE1, this.SIZE2))),
  8503. this.TR2),
  8504. EFFECT(EFFECT_STAB)));
  8505.  
  8506. }
  8507. argify(state) {
  8508. state.color_argument = STAB_COLOR_ARG;
  8509. var ret = super.argify(state);
  8510. state.color_argument = null;
  8511. return ret;
  8512. }
  8513. };
  8514.  
  8515. function ResponsiveStabL(COLOR, TR1, TR2, TOP, BOTTOM, SIZE) {
  8516. return new ResponsiveStabLClass(COLOR, TR1, TR2, TOP, BOTTOM, SIZE);
  8517. }
  8518.  
  8519. ///////
  8520.  
  8521. class IgnitionTimeClass extends MACRO {
  8522. constructor(DEFAULT_VALUE) {
  8523. super("arg/wavlen ignition time", arguments);
  8524. this.add_arg("DEFAULT_VALUE", "INT", "Default value.", 300);
  8525. this.SetExpansion(Scale(IsLessThan(IntArg_(ArgumentName(IGNITION_TIME_ARG),this.DEFAULT_VALUE),Int(1)),IntArg_(ArgumentName(IGNITION_TIME_ARG),this.DEFAULT_VALUE),WavLen(EFFECT(EFFECT_IGNITION))));
  8526. }
  8527. }
  8528.  
  8529. function IgnitionTime(DEFAULT_VALUE) {
  8530. return new IgnitionTimeClass(DEFAULT_VALUE);
  8531. }
  8532.  
  8533. class RetractionTimeClass extends MACRO {
  8534. constructor(DEFAULT_VALUE) {
  8535. super("arg/wavlen ignition time", arguments);
  8536. this.add_arg("DEFAULT_VALUE", "INT", "Default value.", 0);
  8537. this.SetExpansion(Scale(IsLessThan(IntArg_(ArgumentName(RETRACTION_TIME_ARG),this.DEFAULT_VALUE),Int(1)),IntArg_(ArgumentName(RETRACTION_TIME_ARG),this.DEFAULT_VALUE),WavLen(EFFECT(EFFECT_RETRACTION))));
  8538. }
  8539. }
  8540.  
  8541. function RetractionTime(DEFAULT_VALUE) {
  8542. return new RetractionTimeClass(DEFAULT_VALUE);
  8543. }
  8544.  
  8545. ////////////////
  8546.  
  8547. class SumClass extends FUNCTION {
  8548. constructor(ARGS) {
  8549. super("Add functions together", ARGS);
  8550. this.F = Array.from(ARGS);
  8551. for (var i = 1; i <= this.F.length; i++) {
  8552. this.add_arg("FUN" + i, "FUNCTION", "Function " + i);
  8553. }
  8554. }
  8555. getInteger(led) {
  8556. var ret = 0;
  8557. for (var i = 0; i < this.F.length; i++) {
  8558. ret += this.F[i].getInteger(led);
  8559. }
  8560. return ret;
  8561. }
  8562. }
  8563.  
  8564. function Sum(ARGS) {
  8565. return new SumClass(Array.from(arguments));
  8566. }
  8567.  
  8568. class DivideClass extends FUNCTION {
  8569. constructor(F, V) {
  8570. super("Returns F / V", arguments);
  8571. this.add_arg("F", "FUNCTION", "F");
  8572. this.add_arg("V", "FUNCTION", "V");
  8573. }
  8574. getInteger(led) {
  8575. var v = this.V.getInteger(led);
  8576. if (v == 0) return 0;
  8577. return this.F.getInteger(led) / v;
  8578. }
  8579. };
  8580.  
  8581. function Divide(F, V) {
  8582. return new DivideClass(F, V);
  8583. }
  8584.  
  8585. class ModFClass extends FUNCTION {
  8586. constructor(F, V) {
  8587. super("Returns F mod V (always positive)", arguments);
  8588. this.add_arg("F", "FUNCTION", "F");
  8589. this.add_arg("V", "FUNCTION", "V");
  8590. }
  8591. getInteger(led) {
  8592. var v = this.V.getInteger(led);
  8593. if (v == 0) return 0;
  8594. return MOD(this.F.getInteger(led), v);
  8595. }
  8596. };
  8597.  
  8598. function ModF(F, V) {
  8599. return new ModFClass(F, V);
  8600. }
  8601.  
  8602. class SubtractClass extends FUNCTION {
  8603. constructor(F, V) {
  8604. super("Returns F - V", arguments);
  8605. this.add_arg("F", "FUNCTION", "F");
  8606. this.add_arg("V", "FUNCTION", "V");
  8607. }
  8608. getInteger(led) {
  8609. return this.F.getInteger(led) - this.V.getInteger(led);
  8610. }
  8611. };
  8612.  
  8613. function Subtract(F, V) {
  8614. return new SubtractClass(F, V);
  8615. }
  8616. ///////
  8617.  
  8618. class HoldPeakFClass extends FUNCTION {
  8619. constructor(F, HOLD_MILLIS, SPEED) {
  8620. super("Holds peak values for the given number of millis, then falls at given speed.", arguments);
  8621. this.add_arg("F", "FUNCTION", "Function to process");
  8622. this.add_arg("HOLD_MILLIS", "FUNCTION", "Millis to hold.");
  8623. this.add_arg("SPEED", "FUNCTION", "Decay speed (per second)");
  8624. this.last_micros = micros();
  8625. this.last_peak = 0;
  8626. this.value = 0;
  8627. }
  8628. run(blade) {
  8629. super.run(blade);
  8630. var current = this.F.getInteger(0);
  8631. var hold_millis = this.HOLD_MILLIS.getInteger(0);
  8632. var now = micros();
  8633. var delta = now - this.last_micros;
  8634. this.last_micros = now;
  8635. if (millis() - this.last_peak > hold_millis) {
  8636. if (delta > 1000000) delta = 1;
  8637. delta *= this.SPEED.getInteger(0);
  8638. delta /= 1000000;
  8639. this.value -= delta;
  8640. }
  8641. if (current > this.value) {
  8642. this.value = current;
  8643. this.last_peak = millis();
  8644. }
  8645. }
  8646.  
  8647. getInteger(led) {
  8648. return this.value;
  8649. }
  8650. }
  8651.  
  8652. function HoldPeakF(F, HOLD_MILLIS, SPEED) {
  8653. return new HoldPeakFClass(F, HOLD_MILLIS, SPEED);
  8654. }
  8655.  
  8656. ///////
  8657.  
  8658. class CircularSectionFClass extends FUNCTION {
  8659. constructor(POSITION, FRACTION) {
  8660. super("Circular section", arguments);
  8661. this.add_arg("POSITION", "FUNCTION", "Position of circular secion.");
  8662. this.add_arg("FRACTION", "FUNCTION", "Fraction of circle lit up.");
  8663. }
  8664. run(blade) {
  8665. super.run(blade);
  8666. this.num_leds = blade.num_leds();
  8667. var fraction = this.FRACTION.getInteger(0);
  8668. if (fraction == 32768) {
  8669. this.start = 0;
  8670. this.end = num_leds * 32768;
  8671. } else if (fraction == 0) {
  8672. this.start = 0;
  8673. this.end = 0;
  8674. } else {
  8675. var pos = this.POSITION.getInteger(0);
  8676. this.start = ((pos + 32768 - fraction / 2) & 32767) * this.num_leds;
  8677. this.end = ((pos + fraction / 2) & 32767) * this.num_leds;
  8678. }
  8679. this.num_leds *= 32768;
  8680. // console.log("START="+this.start+" END="+this.end +" num_leds="+this.num_leds);
  8681. }
  8682. getInteger(led) {
  8683. var led_range = new Range(led * 32768, led * 32768 + 32768);
  8684. var black_mix = 0;
  8685. if (this.start <= this.end) {
  8686. black_mix = (new Range(this.start, this.end).Intersect(led_range)).Size();
  8687. } else {
  8688. black_mix = (new Range(0, this.end).Intersect(led_range)).Size() +
  8689. (new Range(this.start, this.num_leds).Intersect(led_range)).Size();
  8690. }
  8691. // console.log("BLACK MIX = " + black_mix);
  8692. return black_mix;
  8693. }
  8694. }
  8695.  
  8696. function CircularSectionF(POSITION, FRACTION) {
  8697. return new CircularSectionFClass(POSITION, FRACTION);
  8698. }
  8699.  
  8700. ///////
  8701.  
  8702. class MarbleFClass extends FUNCTION {
  8703. constructor(OFFSET, FRICTION, ACCELERATION, GRAVITY) {
  8704. super("Circular marble simulator.", arguments);
  8705. this.add_arg("OFFSET","FUNCTION", "Offset");
  8706. this.add_arg("FRICTION","FUNCTION", "Friction");
  8707. this.add_arg("ACCELERATION","FUNCTION", "Acceleration");
  8708. this.add_arg("GRAVITY","FUNCTION", "Gravity");
  8709. this.last_micros = 0;
  8710. this.pos = 0;
  8711. this.value = 0;
  8712. this.pos = 0.0;
  8713. this.speed = 0.0;
  8714. }
  8715. run(blade) {
  8716. super.run(blade);
  8717. var now = micros();
  8718. var delta = now - this.last_micros;
  8719. this.last_micros = now;
  8720. if (delta > 1000000) delta = 1;
  8721. var fraction = delta / 1000000.0;
  8722. var rad = (this.pos + this.OFFSET.getInteger(0) / 32768.0) * Math.PI * 2.0;
  8723. var down = { x:0.0, y:1.0, z:0.0 };
  8724. var gravity = this.GRAVITY.getInteger(0) / 32768.0;
  8725. var accel = (down.y * Math.sin(rad) + down.z * Math.cos(rad)) * gravity;
  8726. accel += this.ACCELERATION.getInteger(0) / 32768.0;
  8727. accel-= this.speed * this.FRICTION.getInteger(0) / 32768.0;
  8728. this.speed += accel * fraction;
  8729. this.pos = fract(this.pos + this.speed * fraction);
  8730. this.value = this.pos * 32768.0;
  8731. }
  8732. getInteger(led) { return this.value; }
  8733. };
  8734.  
  8735. function MarbleF(OFFSET, FRICTION, ACCELERATION, GRAVITY) {
  8736. return new MarbleFClass(OFFSET, FRICTION, ACCELERATION, GRAVITY);
  8737. }
  8738.  
  8739. ///////
  8740.  
  8741. class LinearSectionFClass extends FUNCTION {
  8742. constructor(POSITION, FRACTION) {
  8743. super("Linear section", arguments);
  8744. this.add_arg("POSITION", "FUNCTION", "Position of linear secion.");
  8745. this.add_arg("FRACTION", "FUNCTION", "Fraction lit up.");
  8746. }
  8747. run(blade) {
  8748. super.run(blade);
  8749. var num_leds = blade.num_leds();
  8750. var fraction = this.FRACTION.getInteger(0);
  8751. var pos = this.POSITION.getInteger(0);
  8752. this.range = new Range(clamp((pos - fraction / 2) * num_leds, 0, 32768 * num_leds), clamp((pos + fraction / 2) * num_leds, 0, 32768 * num_leds));
  8753. }
  8754. getInteger(led) {
  8755. var led_range = new Range(led * 32768, led * 32768 + 32768);
  8756. return this.range.Intersect(led_range).Size();
  8757. }
  8758. }
  8759.  
  8760. function LinearSectionF(POSITION, FRACTION) {
  8761. return new LinearSectionFClass(POSITION, FRACTION);
  8762. }
  8763.  
  8764. ///////
  8765.  
  8766. var start = new Date().getTime();
  8767.  
  8768. var current_focus;
  8769. var current_focus_url;
  8770. var style_tree;
  8771.  
  8772. function newCall(Cls) {
  8773. return new (Function.prototype.bind.apply(Cls, arguments));
  8774. }
  8775.  
  8776.  
  8777. var classes = {
  8778. AlphaL : AlphaL,
  8779. AlphaMixL : AlphaMixL,
  8780. AudioFlicker : AudioFlicker,
  8781. AudioFlickerL : AudioFlickerL,
  8782. Blast : Blast,
  8783. BlastL : BlastL,
  8784. BlastF : BlastF,
  8785. BlastFadeout : BlastFadeout,
  8786. BlastFadeoutL : BlastFadeoutL,
  8787. BlastFadeoutF : BlastFadeoutF,
  8788. Blinking : Blinking,
  8789. BlinkingL : BlinkingL,
  8790. BlinkingX : BlinkingX,
  8791. BlinkingF : BlinkingF,
  8792. BrownNoiseFlicker : BrownNoiseFlicker,
  8793. BrownNoiseFlickerL : BrownNoiseFlickerL,
  8794. BrownNoiseF : BrownNoiseF,
  8795. ColorCycle : ColorCycle,
  8796. ColorChange : ColorChange,
  8797. ColorSelect : ColorSelect,
  8798. IntSelect : IntSelect,
  8799. ColorSequence : ColorSequence,
  8800. EffectSequence : EffectSequence,
  8801. Cylon : Cylon,
  8802. EasyBlade : EasyBlade,
  8803. FOCUS : Focus,
  8804. FireConfig : FireConfig,
  8805. Gradient : Gradient,
  8806. HumpFlicker : HumpFlicker,
  8807. HumpFlickerL : HumpFlickerL,
  8808. HumpFlickerF : HumpFlickerF,
  8809. HumpFlickerFX : HumpFlickerFX,
  8810. IgnitionDelay : IgnitionDelay,
  8811. IgnitionDelayX : IgnitionDelayX,
  8812. RetractionDelay : RetractionDelay,
  8813. RetractionDelayX : RetractionDelayX,
  8814. InOutHelper : InOutHelper,
  8815. InOutHelperX : InOutHelperX,
  8816. InOutHelperL : InOutHelperL,
  8817. InOutHelperF : InOutHelperF,
  8818. InOutSparkTip : InOutSparkTip,
  8819. Layers : Layers,
  8820. LocalizedClash : LocalizedClash,
  8821. LocalizedClashL : LocalizedClashL,
  8822. Lockup : Lockup,
  8823. LockupL : LockupL,
  8824. LockupTr : LockupTr,
  8825. LockupTrL : LockupTrL,
  8826. Mix : Mix,
  8827. OnSpark : OnSpark,
  8828. OnSparkX : OnSparkX,
  8829. OnSparkL : OnSparkL,
  8830. OnSparkF : OnSparkF,
  8831. OriginalBlast : OriginalBlast,
  8832. OriginalBlastL : OriginalBlastL,
  8833. OriginalBlastF : OriginalBlastF,
  8834. Pulsing : Pulsing,
  8835. PulsingX : PulsingX,
  8836. PulsingL : PulsingL,
  8837. PulsingF : PulsingF,
  8838. RandomFlicker : RandomFlicker,
  8839. RandomL : RandomL,
  8840. RandomF : RandomF,
  8841. RandomPerLEDFlicker : RandomPerLEDFlicker,
  8842. RandomPerLEDFlickerL : RandomPerLEDFlickerL,
  8843. RandomPerLEDF : RandomPerLEDF,
  8844. RandomBlink : RandomBlink,
  8845. RandomBlinkX : RandomBlinkX,
  8846. RandomBlinkL : RandomBlinkL,
  8847. RandomBlinkF : RandomBlinkF,
  8848. Remap : Remap,
  8849. Sequence : Sequence,
  8850. SequenceL : SequenceL,
  8851. SequenceF : SequenceF,
  8852. Rgb : Rgb,
  8853. Rgb16 : Rgb16,
  8854. SimpleClash : SimpleClash,
  8855. SimpleClashL : SimpleClashL,
  8856. Sparkle : Sparkle,
  8857. SparkleL : SparkleL,
  8858. SparkleF : SparkleF,
  8859. Strobe : Strobe,
  8860. StrobeX : StrobeX,
  8861. StrobeL : StrobeL,
  8862. StrobeF : StrobeF,
  8863. Stripes : Stripes,
  8864. StripesX : StripesX,
  8865. StyleFire : StyleFire,
  8866. StylePtr : StylePtr,
  8867. StyleFirePtr : StyleFirePtr,
  8868. StyleNormalPtr : StyleNormalPtr,
  8869. StyleRainbowPtr : StyleRainbowPtr,
  8870. StyleStrobePtr : StyleStrobePtr,
  8871. StaticFire : StaticFire,
  8872. TransitionLoop : TransitionLoop,
  8873. TransitionLoopL : TransitionLoopL,
  8874. TransitionEffect : TransitionEffect,
  8875. TransitionEffectL : TransitionEffectL,
  8876. MultiTransitionEffect : MultiTransitionEffect,
  8877. MultiTransitionEffectL : MultiTransitionEffectL,
  8878. TransitionPulseL : TransitionPulseL,
  8879. InOutTr : InOutTr,
  8880. InOutTrL : InOutTrL,
  8881.  
  8882. RotateColorsX : RotateColorsX,
  8883. RotateColors : RotateColors,
  8884. HueX : HueX,
  8885. Hue : Hue,
  8886.  
  8887. TrInstant : TrInstant,
  8888. TrFade : TrFade,
  8889. TrFadeX : TrFadeX,
  8890. TrSmoothFade : TrSmoothFade,
  8891. TrSmoothFadeX : TrSmoothFadeX,
  8892. TrDelay : TrDelay,
  8893. TrDelayX : TrDelayX,
  8894. TrBoing : TrBoing,
  8895. TrBoingX : TrBoingX,
  8896. TrBlink : TrBlink,
  8897. TrBlinkX : TrBlinkX,
  8898. TrDoEffect : TrDoEffect,
  8899. TrDoEffectX : TrDoEffectX,
  8900. TrDoEffectAlways : TrDoEffectAlways,
  8901. TrDoEffectAlwaysX : TrDoEffectAlwaysX,
  8902. TrWipe : TrWipe,
  8903. TrWipeX : TrWipeX,
  8904. TrWipeIn : TrWipeIn,
  8905. TrWipeInX : TrWipeInX,
  8906. TrCenterWipe : TrCenterWipe,
  8907. TrCenterWipeX : TrCenterWipeX,
  8908. TrCenterWipeSpark : TrCenterWipeSpark,
  8909. TrCenterWipeSparkX : TrCenterWipeSparkX,
  8910. TrCenterWipeIn : TrCenterWipeIn,
  8911. TrCenterWipeInX : TrCenterWipeInX,
  8912. TrCenterWipeInSpark : TrCenterWipeInSpark,
  8913. TrCenterWipeInSparkX : TrCenterWipeInSparkX,
  8914. TrColorCycle : TrColorCycle,
  8915. TrColorCycleX : TrColorCycleX,
  8916. TrConcat : TrConcat,
  8917. TrJoin : TrJoin,
  8918. TrJoinR : TrJoinR,
  8919. TrRandom : TrRandom,
  8920. TrSelect : TrSelect,
  8921. TrWaveX : TrWaveX,
  8922. TrSparkX : TrSparkX,
  8923. TrWipeSparkTip : TrWipeSparkTip,
  8924. TrWipeSparkTipX : TrWipeSparkTipX,
  8925. TrWipeInSparkTip : TrWipeInSparkTip,
  8926. TrWipeInSparkTipX : TrWipeInSparkTipX,
  8927. TrExtendX : TrExtendX,
  8928. TrExtend : TrExtend,
  8929. TrLoop : TrLoop,
  8930. TrLoopN : TrLoopN,
  8931. TrLoopNX : TrLoopNX,
  8932. TrLoopUntil : TrLoopUntil,
  8933.  
  8934. ReverseTime : ReverseTime,
  8935. ReverseTimeX : ReverseTimeX,
  8936. BendTimePow : BendTimePow,
  8937. BendTimePowX : BendTimePowX,
  8938. BendTimePowInv : BendTimePowInv,
  8939. BendTimePowInvX : BendTimePowInvX,
  8940.  
  8941. BatteryLevel : BatteryLevel,
  8942. VolumeLevel : VolumeLevel,
  8943. Bump : Bump,
  8944. Ifon : Ifon,
  8945. ChangeSlowly : ChangeSlowly,
  8946. InOutFunc : InOutFunc,
  8947. InOutFuncX : InOutFuncX,
  8948. Int : Int,
  8949. IntArg : IntArg_,
  8950. RgbArg : RgbArg_,
  8951. Scale : Scale,
  8952. InvertF : InvertF,
  8953. Sin : Sin,
  8954. Saw : Saw,
  8955. Trigger : Trigger,
  8956. SmoothStep : SmoothStep,
  8957. RampF : RampF,
  8958. Mult : Mult,
  8959. Percentage : Percentage,
  8960. NoisySoundLevel : NoisySoundLevel,
  8961. NoisySoundLevelCompat : NoisySoundLevelCompat,
  8962. SmoothSoundLevel : SmoothSoundLevel,
  8963. SwingSpeedX : SwingSpeedX,
  8964. SwingSpeed : SwingSpeed,
  8965. SwingAccelerationX : SwingAccelerationX,
  8966. SwingAcceleration : SwingAcceleration,
  8967. ClashImpactFX : ClashImpactFX,
  8968. ClashImpactF : ClashImpactF,
  8969. LayerFunctions : LayerFunctions,
  8970. SlowNoise : SlowNoise,
  8971. IsLessThan : IsLessThan,
  8972. IsGreaterThan : IsGreaterThan,
  8973. IsBetween : IsBetween,
  8974. ClampFX : ClampFX,
  8975. ClampF : ClampF,
  8976. Variation : Variation,
  8977. AltF : AltF,
  8978. SyncAltToVarianceF : SyncAltToVarianceF,
  8979. SyncAltToVarianceL : SyncAltToVarianceL,
  8980. BladeAngleX : BladeAngleX,
  8981. BladeAngle : BladeAngle,
  8982. TwistAngle : TwistAngle,
  8983. TwistAcceleration : TwistAcceleration,
  8984. Sum : Sum,
  8985. Divide : Divide,
  8986. ModF : ModF,
  8987. Subtract : Subtract,
  8988. HoldPeakF : HoldPeakF,
  8989. ThresholdPulseF : ThresholdPulseF,
  8990. EffectRandomF : EffectRandomF,
  8991. EffectPulseF : EffectPulseF,
  8992. EffectPosition : EffectPosition,
  8993. TimeSinceEffect : TimeSinceEffect,
  8994. WavNum : WavNum,
  8995. CenterDistF : CenterDistF,
  8996. CircularSectionF : CircularSectionF,
  8997. LinearSectionF : LinearSectionF,
  8998. IncrementWithReset : IncrementWithReset,
  8999. IncrementModuloF : IncrementModuloF,
  9000. IncrementF : IncrementF,
  9001. EffectIncrementF : EffectIncrementF,
  9002. MarbleF : MarbleF,
  9003.  
  9004. ResponsiveLockupL : ResponsiveLockupL,
  9005. ResponsiveDragL : ResponsiveDragL,
  9006. ResponsiveMeltL : ResponsiveMeltL,
  9007. ResponsiveLightningBlockL : ResponsiveLightningBlockL,
  9008. ResponsiveClashL : ResponsiveClashL,
  9009. ResponsiveBlastL : ResponsiveBlastL,
  9010. ResponsiveBlastWaveL : ResponsiveBlastWaveL,
  9011. ResponsiveBlastFadeL : ResponsiveBlastFadeL,
  9012. ResponsiveStabL : ResponsiveStabL,
  9013.  
  9014. IgnitionTime : IgnitionTime,
  9015. RetractionTime : RetractionTime,
  9016. WavLen : WavLen,
  9017. LockupPulseF : LockupPulseF,
  9018. };
  9019.  
  9020.  
  9021. AddIdentifier("RgbCycle", RgbCycle);
  9022. AddIdentifier("Rainbow", Rainbow);
  9023. AddIdentifier("WHITE", Rgb.bind(null, 255,255,255));
  9024. AddIdentifier("BLACK", Rgb.bind(null, 0,0,0));
  9025.  
  9026. AddIdentifier("RED", Rgb.bind(null, 255,0,0));
  9027. AddIdentifier("GREEN", Rgb.bind(null, 0,255,0));
  9028. AddIdentifier("BLUE", Rgb.bind(null, 0,0,255));
  9029. AddIdentifier("YELLOW", Rgb.bind(null, 255,255,0));
  9030. AddIdentifier("CYAN", Rgb.bind(null, 0,255,255));
  9031. AddIdentifier("MAGENTA", Rgb.bind(null, 255,0,255));
  9032. AddIdentifier("WHITE", Rgb.bind(null, 255,255,255));
  9033. AddIdentifier("BLACK", Rgb.bind(null, 0,0,0));
  9034.  
  9035. AddIdentifier("AliceBlue", Rgb.bind(null, 223, 239, 255));
  9036. AddIdentifier("Aqua", Rgb.bind(null, 0, 255, 255));
  9037. AddIdentifier("Aquamarine", Rgb.bind(null, 55, 255, 169));
  9038. AddIdentifier("Azure", Rgb.bind(null, 223, 255, 255));
  9039. AddIdentifier("Bisque", Rgb.bind(null, 255, 199, 142));
  9040. AddIdentifier("Black", Rgb.bind(null, 0, 0, 0));
  9041. AddIdentifier("BlanchedAlmond", Rgb.bind(null, 255, 213, 157));
  9042. AddIdentifier("Blue", Rgb.bind(null, 0, 0, 255));
  9043. AddIdentifier("Chartreuse", Rgb.bind(null, 55, 255, 0));
  9044. AddIdentifier("Coral", Rgb.bind(null, 255, 55, 19));
  9045. AddIdentifier("Cornsilk", Rgb.bind(null, 255, 239, 184));
  9046. AddIdentifier("Cyan", Rgb.bind(null, 0, 255, 255));
  9047. AddIdentifier("DarkOrange", Rgb.bind(null, 255, 68, 0));
  9048. AddIdentifier("DeepPink", Rgb.bind(null, 255, 0, 75));
  9049. AddIdentifier("DeepSkyBlue", Rgb.bind(null, 0, 135, 255));
  9050. AddIdentifier("DodgerBlue", Rgb.bind(null, 2, 72, 255));
  9051. AddIdentifier("FloralWhite", Rgb.bind(null, 255, 244, 223));
  9052. AddIdentifier("Fuchsia", Rgb.bind(null, 255, 0, 255));
  9053. AddIdentifier("GhostWhite", Rgb.bind(null, 239, 239, 255));
  9054. AddIdentifier("Green", Rgb.bind(null, 0, 255, 0));
  9055. AddIdentifier("GreenYellow", Rgb.bind(null, 108, 255, 6));
  9056. AddIdentifier("HoneyDew", Rgb.bind(null, 223, 255, 223));
  9057. AddIdentifier("HotPink", Rgb.bind(null, 255, 36, 118));
  9058. AddIdentifier("Ivory", Rgb.bind(null, 255, 255, 223));
  9059. AddIdentifier("LavenderBlush", Rgb.bind(null, 255, 223, 233));
  9060. AddIdentifier("LemonChiffon", Rgb.bind(null, 255, 244, 157));
  9061. AddIdentifier("LightCyan", Rgb.bind(null, 191, 255, 255));
  9062. AddIdentifier("LightPink", Rgb.bind(null, 255, 121, 138));
  9063. AddIdentifier("LightSalmon", Rgb.bind(null, 255, 91, 50));
  9064. AddIdentifier("LightYellow", Rgb.bind(null, 255, 255, 191));
  9065. AddIdentifier("Lime", Rgb.bind(null, 0, 255, 0));
  9066. AddIdentifier("Magenta", Rgb.bind(null, 255, 0, 255));
  9067. AddIdentifier("MintCream", Rgb.bind(null, 233, 255, 244));
  9068. AddIdentifier("MistyRose", Rgb.bind(null, 255, 199, 193));
  9069. AddIdentifier("Moccasin", Rgb.bind(null, 255, 199, 119));
  9070. AddIdentifier("NavajoWhite", Rgb.bind(null, 255, 187, 108));
  9071. AddIdentifier("Orange", Rgb.bind(null, 255, 97, 0));
  9072. AddIdentifier("OrangeRed", Rgb.bind(null, 255, 14, 0));
  9073. AddIdentifier("PapayaWhip", Rgb.bind(null, 255, 221, 171));
  9074. AddIdentifier("PeachPuff", Rgb.bind(null, 255, 180, 125));
  9075. AddIdentifier("Pink", Rgb.bind(null, 255, 136, 154));
  9076. AddIdentifier("Red", Rgb.bind(null, 255, 0, 0));
  9077. AddIdentifier("SeaShell", Rgb.bind(null, 255, 233, 219));
  9078. AddIdentifier("Snow", Rgb.bind(null, 255, 244, 244));
  9079. AddIdentifier("SpringGreen", Rgb.bind(null, 0, 255, 55));
  9080. AddIdentifier("SteelBlue", Rgb.bind(null, 14, 57, 118));
  9081. AddIdentifier("Tomato", Rgb.bind(null, 255, 31, 15));
  9082. AddIdentifier("White", Rgb.bind(null, 255, 255, 255));
  9083. AddIdentifier("Yellow", Rgb.bind(null, 255, 255, 0));
  9084.  
  9085. // New in ProffieOS 8
  9086. AddIdentifier("ElectricPurple", Rgb.bind(null, 127, 0, 255));
  9087. AddIdentifier("ElectricViolet", Rgb.bind(null, 71, 0, 255));
  9088. AddIdentifier("ElectricLime", Rgb.bind(null, 156, 255, 0));
  9089. AddIdentifier("Amber", Rgb.bind(null, 255, 135, 0));
  9090. AddIdentifier("CyberYellow", Rgb.bind(null, 255, 168, 0));
  9091. AddIdentifier("CanaryYellow", Rgb.bind(null, 255, 221, 0));
  9092. AddIdentifier("PaleGreen", Rgb.bind(null, 28, 255, 28));
  9093. AddIdentifier("Flamingo", Rgb.bind(null, 255, 80, 254));
  9094. AddIdentifier("VividViolet", Rgb.bind(null, 90, 0, 255));
  9095. AddIdentifier("PsychedelicPurple", Rgb.bind(null, 186, 0, 255));
  9096. AddIdentifier("HotMagenta", Rgb.bind(null, 255, 0, 156));
  9097. AddIdentifier("BrutalPink", Rgb.bind(null, 255, 0, 128));
  9098. AddIdentifier("NeonRose", Rgb.bind(null, 255, 0, 55));
  9099. AddIdentifier("VividRaspberry", Rgb.bind(null, 255, 0, 38));
  9100. AddIdentifier("HaltRed", Rgb.bind(null, 255, 0, 19));
  9101. AddIdentifier("MoltenCore", Rgb.bind(null, 255, 24, 0));
  9102. AddIdentifier("SafetyOrange", Rgb.bind(null, 255, 33, 0));
  9103. AddIdentifier("OrangeJuice", Rgb.bind(null, 255, 55, 0));
  9104. AddIdentifier("Orange", Rgb.bind(null, 255, 97, 0));
  9105. AddIdentifier("ImperialYellow", Rgb.bind(null, 255, 115, 0));
  9106. AddIdentifier("SchoolBus", Rgb.bind(null, 255, 176, 0));
  9107. AddIdentifier("SuperSaiyan", Rgb.bind(null, 255, 186, 0));
  9108. AddIdentifier("Star", Rgb.bind(null, 255, 201, 0));
  9109. AddIdentifier("Lemon", Rgb.bind(null, 255, 237, 0));
  9110. AddIdentifier("ElectricBanana", Rgb.bind(null, 246, 255, 0));
  9111. AddIdentifier("BusyBee", Rgb.bind(null, 231, 255, 0));
  9112. AddIdentifier("ZeusBolt", Rgb.bind(null, 219, 255, 0));
  9113. AddIdentifier("LimeZest", Rgb.bind(null, 186, 255, 0));
  9114. AddIdentifier("Limoncello", Rgb.bind(null, 135, 255, 0));
  9115. AddIdentifier("CathodeGreen", Rgb.bind(null, 0, 255, 22));
  9116. AddIdentifier("MintyParadise", Rgb.bind(null, 0, 255, 128));
  9117. AddIdentifier("PlungePool", Rgb.bind(null, 0, 255, 156));
  9118. AddIdentifier("VibrantMint", Rgb.bind(null, 0, 255, 201));
  9119. AddIdentifier("MasterSwordBlue", Rgb.bind(null, 0, 255, 219));
  9120. AddIdentifier("BrainFreeze", Rgb.bind(null, 0, 219, 255));
  9121. AddIdentifier("BlueRibbon", Rgb.bind(null, 0, 33, 255));
  9122. AddIdentifier("RareBlue", Rgb.bind(null, 0, 13, 255));
  9123. AddIdentifier("OverdueBlue", Rgb.bind(null, 13, 0, 255));
  9124. AddIdentifier("ViolentViolet", Rgb.bind(null, 55, 0, 255));
  9125.  
  9126.  
  9127. class Parser {
  9128. constructor(str, classes, identifiers) {
  9129. console.log("PARSING: " + str);
  9130. this.str = str;
  9131. this.pos = 0;
  9132. this.classes = classes;
  9133. this.identifiers = identifiers;
  9134. }
  9135. peek() {
  9136. if (this.pos >= this.str.length) return ""
  9137. return this.str[this.pos]
  9138. }
  9139. peek2() {
  9140. if (this.pos + 1 >= this.str.length) return ""
  9141. return this.str[this.pos + 1]
  9142. }
  9143. gobble(c) {
  9144. this.skipspace();
  9145. if (this.peek() == c) {
  9146. this.pos++;
  9147. return true;
  9148. }
  9149. return false;
  9150. }
  9151. expect(c) {
  9152. if (!this.gobble(c)) throw "Missing " + c;
  9153. }
  9154. skipspace2() {
  9155. var start_pos = this.pos;
  9156. while (true) {
  9157. if (this.peek() == ' ' || this.peek() == '\t' || this.peek() == '\n' || this.peek() == '\r') { this.pos++; continue; }
  9158. if (this.peek() == '/') {
  9159. if (this.peek2() == '*') {
  9160. this.pos += 2;
  9161. while (this.pos < this.str.length && !(this.peek() == '*' && this.peek2() == '/')) this.pos++;
  9162. this.pos += 2;
  9163. continue;
  9164. }
  9165. if (this.peek2() == '/') {
  9166. this.pos += 2;
  9167. while (this.pos < this.str.length && this.peek() != '\n') this.pos++;
  9168. this.pos ++;
  9169. continue;
  9170. }
  9171. }
  9172. return this.str.substr(start_pos, this.pos - start_pos); // trim?
  9173. }
  9174. }
  9175.  
  9176. skipspace() {
  9177. var ret = this.skipspace2();
  9178. if (ret.trim()) {
  9179. console.log("COMMENT DROPPED ON GROUND: "+ret);
  9180. console.trace();
  9181. }
  9182. }
  9183.  
  9184. startswithnewline(str) {
  9185. var tmp = str.trimStart();
  9186. var start = str.substr(0, str.length - tmp.length);
  9187. return start.split('\n').length > 1;
  9188. }
  9189.  
  9190. stripcomment(str) {
  9191. var ret = "";
  9192. while (true) {
  9193. str = str.trim();
  9194. if (str) console.log("STRIPCOMMENT: "+str);
  9195. if (str == "") {
  9196. if (ret) console.log("STRIPCOMMENT -> "+ret);
  9197. return ret;
  9198. }
  9199. if (ret != "") ret += "\n";
  9200. if (str[0] == '/' && str[1] == '/') {
  9201. var tmp = str.split('\n');
  9202. ret += tmp[0].substr(2);
  9203. str = tmp.slice(1).join('\n');
  9204. } else if (str[0] == '/' && str[1] == '*') {
  9205. var tmp = str.split('*/');
  9206. ret += tmp[0].substr(2);
  9207. str = tmp.slice(1).join('*/');
  9208. } else {
  9209. if (ret) console.log("STRIPCOMMENT -> "+ret);
  9210. return ret;
  9211. }
  9212. }
  9213. }
  9214.  
  9215. identifier() {
  9216. var ret = "";
  9217. while (true) {
  9218. var c = this.peek();
  9219. if ((c >= 'a' && c <= 'z') ||
  9220. (c >= 'A' && c <= 'Z') ||
  9221. (c >= '0' && c <= '9') || c == '_' || c == ':') {
  9222. ret += c;
  9223. this.pos++;
  9224. } else {
  9225. return ret;
  9226. }
  9227. }
  9228. }
  9229.  
  9230. parse_optional_string() {
  9231. this.skipspace();
  9232. if (this.peek() != '"') return null;
  9233. var ret = "";
  9234. while (this.gobble('"')) {
  9235. while (this.peek() != '"') {
  9236. ret += this.peek();
  9237. this.pos++;
  9238. }
  9239. this.expect('"')
  9240. this.skipspace();
  9241. }
  9242. return ret;
  9243. }
  9244.  
  9245. // recursive descent parser
  9246. parse_atom() {
  9247. this.skipspace();
  9248. var start_of_atom = this.pos;
  9249. var id = this.identifier();
  9250. if (id == "") {
  9251. throw "Expected identifier or number";
  9252. }
  9253. if ((id[0] >= '0' && id[0] <= '9')) {
  9254. if (id.slice(0,2) == "0b") {
  9255. return new BINARY(parseInt(id.slice(2), 2));
  9256. }
  9257. return new INTEGER(parseInt(id));
  9258. }
  9259. var midcomment = this.skipspace2();
  9260. var args = 0;
  9261. var argstring = 0;
  9262. if (this.peek() == "<") {
  9263. this.pos++;
  9264. var POS = this.pos;
  9265. this.skipspace2();
  9266. args = [null];
  9267. if (this.peek() != '>') {
  9268. this.pos = POS; // backtrack for comments
  9269. while (true) {
  9270. var v = this.parse_internal();
  9271. args.push(v);
  9272. this.skipspace2();
  9273. if (this.peek() != ',') break;
  9274. this.pos++;
  9275. POS = this.pos
  9276. var comment = this.skipspace2();
  9277. if (this.startswithnewline(comment)) {
  9278. // Comment belongs to next value.
  9279. // rewind and let parse_unary do it.
  9280. this.pos = POS;
  9281. } else {
  9282. v.addcomment(this.stripcomment(comment));
  9283. if (v.COMMENT) console.log("SETCOMMENT:" + v.COMMENT);
  9284. }
  9285. }
  9286. }
  9287. if (this.peek() != '>') {
  9288. throw "Missing > or ,";
  9289. }
  9290. this.pos++;
  9291. if (this.peek() == '(') {
  9292. this.pos++;
  9293. argstring = this.parse_optional_string();
  9294. if (this.peek() != ')') throw "Missing )";
  9295. this.pos++;
  9296. }
  9297. }
  9298. var ret;
  9299. if (this.identifiers[id]) {
  9300. if (args != 0) {
  9301. throw "Unexpected arguments";
  9302. }
  9303. ret = this.identifiers[id]();
  9304. } else if (this.classes[id]) {
  9305. //console.log(id);
  9306. //console.log(this.classes[id]);
  9307. //console.log(args);
  9308. if (args == 0) args = [null];
  9309. // var ret = new (Function.prototype.bind.apply(this.classes[id], args));
  9310. try {
  9311. ret = classes[id].apply(args[0], args.slice(1));
  9312. } catch(e) {
  9313. if (typeof(e) == "string")
  9314. e = new MyError(id +": " + e);
  9315. if (typeof(e) == "object" && e.constructor == MyError)
  9316. e.desc = id + ": " + e.desc;
  9317. if (typeof(e) == "object" && e.constructor == MyError && e.end_pos == -1) {
  9318. e.setBegin(start_of_atom);
  9319. e.setEnd(this.pos);
  9320. }
  9321. throw e;
  9322. }
  9323. // console.log(ret);
  9324. if (argstring) {
  9325. console.log("ARGSTRING : " + argstring);
  9326. ret.argstring = argstring;
  9327. }
  9328. }
  9329. if (!ret) {
  9330. throw "Unknown identifier: " + id;
  9331. }
  9332. ret.addcomment(this.stripcomment(midcomment));
  9333. return ret;
  9334. }
  9335.  
  9336. parse_unary() {
  9337. var pre_comment = this.skipspace2();
  9338. var ret = 0;
  9339. if (this.peek() == '-') {
  9340. this.pos++;
  9341. ret = this.parse_atom();
  9342. if (ret.getType() != "INT")
  9343. throw "Expected integer, got " + ret.getType();
  9344. ret.value = - ret.value;
  9345. return ret;
  9346. } else {
  9347. ret = this.parse_atom();
  9348. }
  9349. if (pre_comment.trim()) {
  9350. ret.prependcomment(this.stripcomment(pre_comment));
  9351. }
  9352. return ret;
  9353. }
  9354.  
  9355. parse_internal() {
  9356. var ret = this.parse_unary();
  9357. this.skipspace();
  9358. while (this.peek() == '|') {
  9359. this.pos++;
  9360. ret.value |= this.parse_unary();
  9361. this.skipspace();
  9362. }
  9363. //console.log("PARSE, returns ID " + ret.get_id());
  9364. // console.log(ret);
  9365. // console.trace();
  9366.  
  9367. return ret;
  9368. }
  9369.  
  9370. parse() {
  9371. var OLD = PushHandledTypes();
  9372. var begin_pos = this.pos;
  9373. var ret = this.parse_internal();
  9374.  
  9375. // secret handshake
  9376. ret.__begin_pos = begin_pos;
  9377. ret.__end_pos = this.pos;
  9378. ret.__handled_types = handled_types;
  9379. ret.__handled_lockups = handled_lockups;
  9380. PopHandledTypes(OLD);
  9381.  
  9382. return ret;
  9383. }
  9384. };
  9385.  
  9386. var current_style = InOutHelper(SimpleClash(Lockup(new BlastClass(BLUE, WHITE), new AudioFlickerClass(BLUE, WHITE)), WHITE, 40), 300, 800);
  9387. //var current_style = InOutHelper(SimpleClash(Lockup(new BlastClass(new RainbowClass(), WHITE), new AudioFlickerClass(BLUE, WHITE)), WHITE, 40), 300, 800);
  9388. var blade = new Blade();
  9389. var rotate_start;
  9390.  
  9391. var last_actual_millis= actual_millis() - 10;
  9392. var time_factor = 1000; // transforms ms to us
  9393.  
  9394. var last_style;
  9395. var show_style;
  9396.  
  9397. var numTick = 0;
  9398. var framesPerUpdate = 0;
  9399. var timeFactor = 1.0;
  9400. var bad_fps = 0;
  9401.  
  9402. </script>
  9403. <div id="popup_window" class="popup-window">
  9404. <p id="popup_message"></p>
  9405. <button title="OK" onclick="dismissPopupMessage()">OK</button>
  9406. <br>
  9407. <label title="Don't show this again. Click 'Restore Defaults' in Settings to re-enable.">
  9408. <input type="checkbox" id="dont_show_again" name="dontshow" onclick="DontShowAgain(this.checked)">
  9409. <span>Don't show this again</span>
  9410. </label>
  9411. </div>
  9412. <div id="overlay" class="overlay"></div>
  9413.  
  9414. <script>
  9415. var popupIdentifier;
  9416. var popupWindow = FIND("popup_window");
  9417. var overlay = FIND("overlay");
  9418.  
  9419. function showPopupMessage(message, currentPopup) {
  9420. popupIdentifier = currentPopup;
  9421. var checkbox = FIND("dont_show_again");
  9422. checkbox.checked = localStorage.getItem(popupIdentifier) === "false";
  9423.  
  9424. if (localStorage.getItem(popupIdentifier) === "false") {
  9425. console.log(popupIdentifier + " is disabled.");
  9426. } else {
  9427. FIND("popup_message").innerHTML = message;
  9428. popupWindow.classList.add("show");
  9429. overlay.classList.add("show");
  9430. }
  9431. }
  9432.  
  9433. function dismissPopupMessage() {
  9434. popupWindow.classList.remove("show");
  9435. overlay.classList.remove("show");
  9436. }
  9437.  
  9438. function DontShowAgain(checkboxState) {
  9439. checkboxState = !checkboxState;
  9440. localStorage.setItem(popupIdentifier, checkboxState);
  9441. console.log("Saving " + popupIdentifier + " " + checkboxState);
  9442. }
  9443.  
  9444. var pixels;
  9445.  
  9446. function drawScene() {
  9447. var now_actual_millis = actual_millis();
  9448. var delta_actual_millis = now_actual_millis - last_actual_millis;
  9449. last_actual_millis = now_actual_millis;
  9450.  
  9451. var delta_us = delta_actual_millis * time_factor
  9452. last_micros = current_micros;
  9453. current_micros_internal += delta_us;
  9454. current_micros = current_micros_internal
  9455. if (current_micros - last_micros > 1000000/45) {
  9456. bad_fps ++;
  9457. } else {
  9458. if (bad_fps) bad_fps --;
  9459. }
  9460. if (bad_fps > 10 && graflexState.get()) {
  9461. showPopupMessage("Struggling to render hilt model.<br>Switching to simpler design.<br>To re-enable Graflex model, go to Settings.", "graflexPopup");
  9462. graflexState.set(false);
  9463. }
  9464. num_leds = blade.num_leds()
  9465. if (!pixels || pixels.length < num_leds * 4 * 2) {
  9466. pixels = new Uint8Array(num_leds * 4 * 2);
  9467. }
  9468. var S = current_style;
  9469. if (S != last_style) {
  9470. last_style = S;
  9471. if (S.getType) {
  9472. S.set_right_side(current_focus || style_tree)
  9473. if (S.getType() == "TRANSITION") {
  9474. S = TransitionLoop(Rgb(0,0,0), TrConcat(TrDelay(500), Rgb(255,0,0), S, Rgb(0,0,255), TrInstant()));
  9475. }
  9476. if (S.getType() == "FUNCTION") {
  9477. S = Mix(S, Rgb(0,0,0), Rgb(255,255,255));
  9478. }
  9479. }
  9480. show_style = S;
  9481. } else {
  9482. S = show_style;
  9483. }
  9484. numTick++;
  9485. if (S.getColor && S.getType && S.getType() == "COLOR" && numTick > framesPerUpdate) {
  9486. numTick = 0;
  9487. S.run(blade);
  9488. for (var i = 0; i < num_leds; i++) {
  9489. c = S.getColor(i);
  9490. pixels[i*4 + 0] = Math.round(c.r * 255);
  9491. pixels[i*4 + 1] = Math.round(c.g * 255);
  9492. pixels[i*4 + 2] = Math.round(c.b * 255);
  9493. pixels[i*4 + 3] = 255;
  9494. }
  9495. if (last_micros != 0) {
  9496. current_micros += delta_us / 2;
  9497. }
  9498. if (framesPerUpdate == 0) {
  9499. S.run(blade);
  9500. }
  9501. for (var i = 0; i < num_leds; i++) {
  9502. c = S.getColor(i);
  9503. pixels[i*4 + 0 + num_leds * 4] = Math.round(c.r * 255);
  9504. pixels[i*4 + 1 + num_leds * 4] = Math.round(c.g * 255);
  9505. pixels[i*4 + 2 + num_leds * 4] = Math.round(c.b * 255);
  9506. pixels[i*4 + 3 + num_leds * 4] = 255;
  9507. }
  9508. S.update_displays();
  9509. }
  9510. // TODO: Generate mipmaps, then adjust level based on distance from blade
  9511. gl.texImage2D(
  9512. gl.TEXTURE_2D,
  9513. 0, // level
  9514. gl.RGBA, // internalFormat
  9515. num_leds, 2, // width, height
  9516. 0, // border
  9517. gl.RGBA, // source format
  9518. gl.UNSIGNED_BYTE, // source type
  9519. pixels);
  9520.  
  9521. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  9522. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  9523. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  9524. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  9525.  
  9526. // Draw these textures to the screen, offset by 1 pixel increments
  9527. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  9528. gl.viewport(0, 0, width * dpr, height * dpr);
  9529. gl.clearColor(0.0, 1.0, 0.0, 1.0);
  9530. gl.clear(gl.COLOR_BUFFER_BIT);
  9531.  
  9532. gl.viewport(0, 0, width * dpr, height * dpr);
  9533. var rotation = MOVE_MATRIX;
  9534. if (STATE_ROTATE) {
  9535. var u_value = (new Date().getTime() - rotate_start) / 3000.0;
  9536. var rotation = default_move_matrix();
  9537. rotation = rotation.mult(Matrix.mkyrot(u_value));
  9538. rotation = rotation.mult(Matrix.mkzrot(u_value / 7.777));
  9539.  
  9540. } else {
  9541. if (0) {
  9542. OLD_MOVE_MATRIX = default_move_matrix();
  9543. rotation = default_move_matrix();
  9544. rotation = rotation.mult(Matrix.mkzrot(0.2));
  9545. }
  9546. rotate_start = new Date().getTime();
  9547. }
  9548. gl.uniform1f(gl.getUniformLocation(shaderProgram, "u_time"),
  9549. (new Date().getTime() - start) / 1000.0);
  9550. gl.uniform1f(gl.getUniformLocation(shaderProgram, "u_width"), width);
  9551. gl.uniform1f(gl.getUniformLocation(shaderProgram, "u_height"), height);
  9552.  
  9553. gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "u_move_matrix"), false, rotation.values);
  9554. gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "u_old_move_matrix"), false, OLD_MOVE_MATRIX.values);
  9555. OLD_MOVE_MATRIX = rotation;
  9556. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  9557.  
  9558. t += 1;
  9559. }
  9560.  
  9561. function tick() {
  9562. window.requestAnimationFrame(tick);
  9563. drawScene();
  9564. }
  9565. var overall_string;
  9566.  
  9567. function ReplaceCurrentFocus(str) {
  9568. current_focus_url = str;
  9569.  
  9570. if (current_focus) {
  9571. current_focus.super_short_desc = true;
  9572. pp_is_url++;
  9573. pp_is_verbose++;
  9574. var url = style_tree.pp();
  9575. console.log("FOCUS URL: " + url);
  9576. pp_is_url--;
  9577. pp_is_verbose--;
  9578. current_focus.super_short_desc = false;
  9579. str = url.replace("$", "FOCUS<" + str + ">");
  9580. }
  9581.  
  9582. var old_focus = current_focus;
  9583. current_focus = 0;
  9584. focus_catcher = 0;
  9585.  
  9586. var parser = new Parser(str, classes, identifiers);
  9587. try {
  9588. style_tree = parser.parse();
  9589. }
  9590. catch(e) {
  9591. var err = FIND("error_message");
  9592. current_focus = old_focus;
  9593. console.log(e);
  9594. console.log(e.stack);
  9595. console.log(typeof(e));
  9596. if (typeof(e) == "string") {
  9597. err.innerHTML = e;
  9598. return;
  9599. } else if (typeof(e) == "object" && e.constructor == MyError) {
  9600. err.innerHTML = e.desc;
  9601. return;
  9602. } else {
  9603. throw e;
  9604. }
  9605. }
  9606. var tmp = "";
  9607. tmp += style_tree.pp();
  9608. FIND("pp").innerHTML = tmp;
  9609. if (focus_catcher) {
  9610. current_focus = focus_catcher;
  9611. var id = current_focus.get_id();
  9612. var container = FIND("X"+id);
  9613. if (!container) {
  9614. console.log("Lost focus when parsing " + str);
  9615. console.log(focus_trace);
  9616. } else {
  9617. container.classList.add("selected-area-container");
  9618. }
  9619. } else {
  9620. console.log("No focus catcher found!!");
  9621. }
  9622. var type = "COLOR";
  9623. var classname = "Style";
  9624. // if (current_focus && current_style && current_focus != current_style) {
  9625. // type = current_style.getType();
  9626. // classname = current_focus.constructor.name;
  9627. // }
  9628.  
  9629. if (current_style) {
  9630. type = current_style.getType();
  9631. classname = current_style.constructor.name;
  9632. }
  9633.  
  9634. AddHistory(current_focus_url, current_style.getType());
  9635. console.log("TYPE = " + type);
  9636. // FIND("color_links").className = type == "COLOR" ? "normal" : "grayout";
  9637. // FIND("effect_links").className = type == "COLOR" ? "normal" : "grayout";
  9638. // FIND("effect_type_links").className = type == "EFFECT" ? "normal" : "grayout";
  9639. // FIND("template_links").className = type == "COLOR" ? "normal" : "grayout";
  9640. // FIND("function_links").className = type == "FUNCTION" ? "normal" : "grayout";
  9641. // FIND("transition_links").className = type == "TRANSITION" ? "normal" : "grayout";
  9642. FIND("expand_button").className = current_style && current_style.isMacro ? "button-on" : "button-off";
  9643. FIND("layerize_button").className = CanLayerize(current_style) ? "button-on" : "button-off";
  9644.  
  9645. if (type == "COLOR" && classname.endsWith("LClass")) {
  9646. ActivateTab("layer");
  9647. } else if (type == "COLOR" && (classname == "Rgb16Class" || classname == "RgbClass")) {
  9648. ActivateTab("rgb");
  9649. } else if (type === "ArgumentName") {
  9650. ActivateTab("arguments");
  9651. } else {
  9652. ActivateTab(type.toLowerCase());
  9653. }
  9654.  
  9655. for (var i = 0; i < document.styleSheets.length; i++) {
  9656. var sheet = document.styleSheets[i];
  9657. for (var r = 0; r < sheet.cssRules.length; r++) {
  9658. var rule = sheet.cssRules[r];
  9659. if (rule.cssText.toLowerCase().includes("magic_class_") ) {
  9660. if (rule.cssText.toLowerCase().includes("magic_class_" + type.toLowerCase())) {
  9661. rule.style.background = "lightblue";
  9662. rule.style.color = "black";
  9663. } else {
  9664. rule.style.background = "lightgray";
  9665. rule.style.color = "darkgray";
  9666. }
  9667. }
  9668. if (rule.cssText.toLowerCase().includes("magic_invisible_class_") ) {
  9669. if (rule.cssText.toLowerCase().includes("magic_invisible_class_" + type.toLowerCase())) {
  9670. rule.style.display = 'inline';
  9671. } else {
  9672. rule.style.display = 'none';
  9673. }
  9674. }
  9675. }
  9676. }
  9677. }
  9678.  
  9679.  
  9680. function Run() {
  9681. var sty = FIND("style");
  9682. var err = FIND("error_message");
  9683. var str = sty.value;
  9684. var parser = new Parser(str,
  9685. classes,
  9686. identifiers);
  9687. err.innerHTML = "";
  9688. try {
  9689. current_style = parser.parse();
  9690. }
  9691. catch(e) {
  9692. console.log(e);
  9693. console.log(e.stack);
  9694. console.log(typeof(e));
  9695. if (typeof(e) == "string") {
  9696.  
  9697. err.innerHTML = e;
  9698. sty.focus();
  9699. sty.setSelectionRange(parser.pos, parser.pos);
  9700.  
  9701. parser = new Parser("BLACK",
  9702. classes,
  9703. identifiers);
  9704. current_style = parser.parse();
  9705. compile();
  9706. return;
  9707. } else if (typeof(e) == "object" && e.constructor == MyError) {
  9708. err.innerHTML = e.desc;
  9709. sty.focus();
  9710. if (e.begin_pos > -1) {
  9711. sty.setSelectionRange(e.begin_pos, e.end_pos);
  9712. } else {
  9713. sty.setSelectionRange(parser.pos, parser.pos);
  9714. }
  9715.  
  9716. parser = new Parser("BLACK",
  9717. classes,
  9718. identifiers);
  9719. current_style = parser.parse();
  9720. compile();
  9721. return;
  9722. } else {
  9723. throw e;
  9724. }
  9725. }
  9726. ReplaceCurrentFocus(str);
  9727. compile();
  9728. if (current_style.argstring) {
  9729. FIND("ARGSTR").value = "builtin 0 1 " + current_style.argstring
  9730. ArgStringChanged();
  9731. }
  9732. }
  9733.  
  9734. var ARGUMENTS = ["builtin", "0", "1"];
  9735. var default_arguments = [];
  9736.  
  9737. function updateArgTag(ARG, VALUE) {
  9738. var N = ArgumentName_ENUM_BUILDER.value_to_name[ARG];
  9739. var tag = FIND("ARGSTR_"+N);
  9740. if (VALUE.search(",") >= 0) {
  9741. console.log("FIXING COLOR VALUE: "+VALUE);
  9742. var values = VALUE.split(",")
  9743. VALUE = '#' + UnFixColor(values[0])+UnFixColor(values[1])+UnFixColor(values[2]);
  9744. }
  9745. console.log("Setting tag from: " + tag.value + " to " + VALUE);
  9746. tag.value = VALUE;
  9747. }
  9748.  
  9749. function getARG(ARG, DEFAULT) {
  9750. ARG = ARG + 2;
  9751. if (!default_arguments[ARG]) {
  9752. updateArgTag(ARG - 2, DEFAULT);
  9753. }
  9754. default_arguments[ARG] = DEFAULT;
  9755. if (ARGUMENTS[ARG] && ARGUMENTS[ARG] != "~")
  9756. return ARGUMENTS[ARG];
  9757. return DEFAULT;
  9758. }
  9759.  
  9760. function ArgStringChanged() {
  9761. var tag = FIND("ARGSTR");
  9762. ARGUMENTS = tag.value.split(" ");
  9763. for (var i = 3; i < ARGUMENTS.length; i++) {
  9764. if (ARGUMENTS[i] == "~") continue;
  9765. if (!ARGUMENTS[i]) continue;
  9766. var ARG = i - 2;
  9767. updateArgTag(ARG, ARGUMENTS[i]);
  9768. }
  9769. }
  9770.  
  9771. function setARG(ARG, TO) {
  9772. ARG += 2;
  9773. ARGUMENTS[ARG] = TO;
  9774. for (var i = 0; i < ARGUMENTS.length; i++) {
  9775. if (!ARGUMENTS[i]) {
  9776. ARGUMENTS[i] = '~';
  9777. }
  9778. }
  9779. FIND("ARGSTR").value = ARGUMENTS.join(" ");
  9780. }
  9781.  
  9782. function ArgChanged(ARG) {
  9783. var N = ArgumentName_ENUM_BUILDER.value_to_name[ARG];
  9784. var tag = FIND("ARGSTR_"+N);
  9785. setARG(ARG, tag.value);
  9786. console.log("Updated " + N + " : " + tag.value)
  9787. }
  9788.  
  9789. function IncreaseArg(ARG, I) {
  9790. var N = ArgumentName_ENUM_BUILDER.value_to_name[ARG];
  9791. var tag = FIND("ARGSTR_"+N);
  9792. tag.value = parseInt(tag.value) + I;
  9793. setARG(ARG, tag.value);
  9794. console.log("Updated " + N + " : " + tag.value)
  9795. }
  9796.  
  9797. function ClickArgColor(ARG) {
  9798. var N = ArgumentName_ENUM_BUILDER.value_to_name[ARG];
  9799. var tag = FIND("ARGSTR_"+N);
  9800. var R = FixColor(tag.value.substr(1,2));
  9801. var G = FixColor(tag.value.substr(3,2));
  9802. var B = FixColor(tag.value.substr(5,2));
  9803. setARG(ARG, R+","+G+","+B);
  9804. }
  9805.  
  9806. function PopState(event) {
  9807. if (event.state) {
  9808. FIND("style").value = event.state;
  9809. Run();
  9810. }
  9811. }
  9812.  
  9813. function SetTo(str) {
  9814. console.log(str);
  9815. var old = FIND("style").value;
  9816. var url = new URL(window.location.href);
  9817. url.searchParams.set("S", str);
  9818.  
  9819. // FIXME: Use pushState and fix back arrow
  9820. window.history.replaceState(old, "Style Editor", window.location.href);
  9821. window.history.pushState(str, "Style Editor", url);
  9822. window.onpopstate = PopState;
  9823.  
  9824. FIND("style").value = str;
  9825. Run();
  9826. }
  9827.  
  9828. function SetToAndFormat(str, event) {
  9829. var parser = new Parser(str, classes, identifiers);
  9830. var style = parser.parse();
  9831. pp_is_url++;
  9832. var url = style.pp();
  9833. pp_is_url--;
  9834. SetTo(url);
  9835.  
  9836. // If the clicked element is a button in either the "Examples" or "History" tab, enable all tabs
  9837. if (event && (event.target.closest('.example-tabcontent') || event.target.closest('.history-tabcontent'))) {
  9838. enableTabs();
  9839. }
  9840. }
  9841.  
  9842.  
  9843. function FocusOnLow(id) {
  9844. console.log("FOCUSON: " + id);
  9845. var style = style_ids[id];
  9846. console.log(id);
  9847. console.log(style);
  9848. current_focus = style;
  9849. var container = FIND("X"+id);
  9850. console.log(container);
  9851. pp_is_url++;
  9852. var url = style.pp();
  9853. pp_is_url--;
  9854. console.log(url);
  9855. current_focus_url = url;
  9856. SetTo(url);
  9857. return true;
  9858. }
  9859.  
  9860. function FocusOn(id, event) {
  9861. event.stopPropagation();
  9862. FocusOnLow(id);
  9863. }
  9864.  
  9865. function ClickRotate() {
  9866. STATE_ROTATE = !STATE_ROTATE;
  9867. var rotate_button = FIND("ROTATE_BUTTON");
  9868. rotate_button.classList.toggle("button-latched", STATE_ROTATE ? true : false);
  9869. console.log("ROTATE");
  9870. }
  9871.  
  9872. function ClickPower() {
  9873. STATE_ON = !STATE_ON; STATE_LOCKUP=0;
  9874. var power_button = FIND("POWER_BUTTON");
  9875. power_button.classList.toggle("button-latched", STATE_ON ? true : false);
  9876. console.log("POWER");
  9877. blade.addEffect(STATE_ON ? EFFECT_IGNITION : EFFECT_RETRACTION, Math.random() * 0.7 + 0.2);
  9878. }
  9879.  
  9880. var lockups_to_event = {};
  9881. lockups_to_event[LOCKUP_NORMAL] = [ EFFECT_LOCKUP_BEGIN, EFFECT_LOCKUP_END ];
  9882. lockups_to_event[LOCKUP_DRAG] = [ EFFECT_DRAG_BEGIN, EFFECT_DRAG_END ];
  9883.  
  9884. function OnLockupChange() {
  9885. console.log("OnLockupChange");
  9886. var select = FIND("LOCKUP");
  9887. var old = STATE_LOCKUP;
  9888. STATE_LOCKUP = window[select.value];
  9889. if (STATE_LOCKUP && lockups_to_event[STATE_LOCKUP]) {
  9890. blade.addEffect(lockups_to_event[STATE_LOCKUP][0], Math.random() * 0.7 + 0.2);
  9891. } else if (old && lockups_to_event[old]) {
  9892. blade.addEffect(lockups_to_event[old][1], Math.random() * 0.7 + 0.2);
  9893. }
  9894. }
  9895.  
  9896. function ClickLockup() {
  9897. STATE_LOCKUP = STATE_LOCKUP == LOCKUP_NORMAL ? 0 : LOCKUP_NORMAL;
  9898. blade.addEffect(STATE_LOCKUP ? EFFECT_LOCKUP_BEGIN : EFFECT_LOCKUP_END, Math.random() * 0.7 + 0.2);
  9899. }
  9900.  
  9901. function ClickDrag() {
  9902. STATE_LOCKUP = STATE_LOCKUP == LOCKUP_DRAG ? 0 : LOCKUP_DRAG;
  9903. blade.addEffect(STATE_LOCKUP ? EFFECT_DRAG_BEGIN : EFFECT_DRAG_END, 1.0);
  9904. }
  9905.  
  9906. function ClickLB() {
  9907. STATE_LOCKUP = STATE_LOCKUP == LOCKUP_LIGHTNING_BLOCK ? 0 : LOCKUP_LIGHTNING_BLOCK;
  9908. }
  9909.  
  9910. function ClickMelt() {
  9911. STATE_LOCKUP = STATE_LOCKUP == LOCKUP_MELT ? 0 : LOCKUP_MELT;
  9912. }
  9913.  
  9914. /* Save blade style to local storage via user OS dialog */
  9915. function ClickSave() {
  9916. Copy();
  9917. let textArea = FIND("style");
  9918. let content = "/*\nSaved from ProffieOS Style Editor:\nhttps://fredrik.hubbe.net/lightsaber/style_editor.html\n*/" + "\n\n" + textArea.value;
  9919. var a = document.createElement("a");
  9920. var file = new Blob([content], {type: "text/plain"});
  9921. a.href = URL.createObjectURL(file);
  9922. a.download = "blade-style.txt";
  9923. a.click();
  9924. }
  9925.  
  9926. function Copy() {
  9927. if (current_style.getType() != "COLOR") {
  9928. FIND("error_message").innerHTML = "Not a complete style.";
  9929. return;
  9930. }
  9931. var color = current_style.getColor(0);
  9932. if (color.a != 1.0) {
  9933. FIND("error_message").innerHTML = "Style is transparent.";
  9934. return;
  9935. }
  9936. var copyText = FIND("style");
  9937. var argStr = '"' + ARGUMENTS.slice(3).join(" ") + '"';
  9938. if (argStr == '""') argStr = "";
  9939. if(copyText.value.includes("StylePtr") ||
  9940. copyText.value.includes("StyleNormalPtr") ||
  9941. copyText.value.includes("StyleFirePtr") ||
  9942. copyText.value.includes("StyleRainbowPtr"))
  9943. {
  9944. if(!copyText.value.endsWith(")"))
  9945. copyText.value = copyText.value + "("+ argStr +")";
  9946. } else {
  9947. copyText.value = "StylePtr<" + copyText.value + ">" + "("+ argStr +")";
  9948. }
  9949. copyText.select();
  9950. document.execCommand("copy");
  9951. // alert("Copy to Clipboard" + copyText.value);
  9952. // myAlertTop("Copy to Clipboard");
  9953. }
  9954.  
  9955. function DoExpand() {
  9956. if (current_style && current_style.expansion) {
  9957. pp_is_url++;
  9958. var url = current_style.expansion.pp();
  9959. pp_is_url--;
  9960. SetTo(url);
  9961. }
  9962. }
  9963.  
  9964. function ShouldJoin(layer1, layer2) {
  9965. if (layer1.LAYERS.length == 0) return true;
  9966. if (layer2.LAYERS.length == 0) return true;
  9967. return layer1.LAYERS[0].isEffect() == layer2.LAYERS[0].isEffect();
  9968. }
  9969.  
  9970. function RecursiveLayerize(node) {
  9971. while (node.isMacro) {
  9972. node = node.expansion;
  9973. }
  9974. if (node.constructor == LayersClass) {
  9975. node.BASE = RecursiveLayerize(node.BASE);
  9976. while (node.BASE.constructor == LayersClass && ShouldJoin(node, node.BASE)) {
  9977. node = new LayersClass([node.BASE.BASE].concat(node.BASE.LAYERS,node.LAYERS));
  9978. }
  9979. }
  9980. return node;
  9981. }
  9982.  
  9983. function CanLayerize(node) {
  9984. if (!node) return false;
  9985. if (node.constructor == LayersClass) return false;
  9986. while (node.isMacro) {
  9987. node = node.expansion;
  9988. }
  9989. return node.constructor == LayersClass;
  9990. }
  9991.  
  9992. function DoLayerize() {
  9993. var tmp = RecursiveLayerize(current_style);
  9994. pp_is_url++;
  9995. tmp = tmp.pp();
  9996. pp_is_url--;
  9997. SetTo(tmp);
  9998. }
  9999.  
  10000. function DoArgify() {
  10001. state = {}
  10002. // Only do this if at top level...
  10003. state.color_argument = BASE_COLOR_ARG;
  10004. var tmp = current_style.argify(state);
  10005. pp_is_url++;
  10006. tmp = tmp.pp();
  10007. pp_is_url--;
  10008. SetTo(tmp);
  10009. }
  10010.  
  10011.  
  10012. // Tab mania.
  10013. const allTabs = ["color", "rgb", "layer", "function", "transition", "effect", "lockup_type", "arguments", "example", "history", "arg_string"];
  10014. // The "color group" are the 3 tabs that contain valid replacements for a COLOR.
  10015. const colorGroup = ["color", "rgb", "layer"];
  10016. var wasTabClicked = false;
  10017. var allTabsEnabled = true;
  10018. var currentTab;
  10019.  
  10020. function AddTab(tab, name, contents) {
  10021. FIND("TABLINKS").innerHTML += "<button id=" + tab + "_tab class=tablinks onclick=\"TabClicked('"+tab+"');\">" + name + "</button>";
  10022. FIND("TABBODIES").innerHTML += "<div id=" + tab + "_tabcontent class='tabcontent " + tab + "-tabcontent'></div>";
  10023. if (contents) {
  10024. AddTabContent(tab, contents);
  10025. }
  10026. }
  10027.  
  10028. function TabClicked(tab) {
  10029. wasTabClicked = true;
  10030. ActivateTab(tab);
  10031. }
  10032.  
  10033. function AddTabContent(tab, data) {
  10034. FIND(tab + "_tabcontent").innerHTML += data;
  10035. }
  10036.  
  10037. function SetTabContent(tab, data) {
  10038. FIND(tab + "_tabcontent").innerHTML = data;
  10039. }
  10040.  
  10041. function sortByName() {
  10042. // Extract name from HTML string
  10043. colorData.sort((a, b) => {
  10044. const nameA = a.match(/value='(.*?)'/)[1];
  10045. const nameB = b.match(/value='(.*?)'/)[1];
  10046. return nameA.localeCompare(nameB);
  10047. });
  10048. return colorData;
  10049. }
  10050.  
  10051. function updateRgbTabContent() {
  10052. if (colorsortState.get()) {
  10053. console.log("Sort colors by Name");
  10054. sortedRgbLinks = sortByName();
  10055. } else {
  10056. console.log("Sort colors by Hue");
  10057. sortedRgbLinks = colorData.sort();
  10058. }
  10059.  
  10060. SetTabContent("rgb", sortedRgbLinks.join("") +
  10061. "<div class='custom-color'>" +
  10062. "<label for='COLOR'>Custom color </label>" +
  10063. "<input type='color' id='COLOR' value='#ff0000' class='color-picker' onclick='ClickColor()' /></div>");
  10064. }
  10065.  
  10066.  
  10067.  
  10068.  
  10069. var tablinks = document.getElementsByClassName("tablinks");
  10070.  
  10071. function enableTabs() {
  10072. for (var i = 0; i < tablinks.length; i++) {
  10073. tablinks[i].classList.remove("disabled");
  10074. tablinks[i].disabled = false;
  10075. }
  10076. allTabsEnabled = true;
  10077. }
  10078.  
  10079. // Enable all tabs on page load - needs time to let tabs load
  10080. window.addEventListener("load", function () {
  10081. setTimeout(function () {
  10082. enableTabs();
  10083. }, 0);
  10084. });
  10085.  
  10086. function ActivateTab(tab) {
  10087. if (!FIND(tab + "_tab")) {
  10088. console.log("No such tab");
  10089. return;
  10090. }
  10091. /* The priority is to have non-applicable tabs be disabled
  10092. if the tab is active due to a clicked type in the Structured View
  10093. (therefore showing only valid replacement choices).
  10094. If the tab is already active, clicking it again
  10095. allows "unlocking" of the other tabs.
  10096. This is useful when you want to change the top level
  10097. of the Structured View to something completely different.
  10098. */
  10099. var activeTab = FIND(tab + "_tab");
  10100. if (activeTab.classList.contains("active") && wasTabClicked) {
  10101. // If the clicked tab is already active, enable all tabs
  10102. enableTabs();
  10103. // Reset the wasTabClicked flag as all tabs are now enabled
  10104. wasTabClicked = false;
  10105. return;
  10106. }
  10107.  
  10108. // Get all elements with class="tabcontent" and hide them to clear last selected tabcontents.
  10109. var tabcontent = document.getElementsByClassName("tabcontent");
  10110. for (var i = 0; i < tabcontent.length; i++) {
  10111. tabcontent[i].style.display = "none";
  10112. }
  10113.  
  10114. // First, remove the "active" and "disabled" class from all tabs
  10115. for (var i = 0; i < tablinks.length; i++) {
  10116. tablinks[i].classList.remove("active");
  10117. tablinks[i].classList.remove("disabled");
  10118. tablinks[i].disabled = false;
  10119. }
  10120.  
  10121. // Show the current tab, and add an "active" class to the button that opened the tab
  10122. FIND(tab + "_tabcontent").style.display = "block";
  10123. FIND(tab + "_tab").classList.add("active");
  10124.  
  10125. // Find the outer-most bracket element using simplified XPath
  10126. var outerMostBracket = document.evaluate("/html/body/table/tbody/tr/td[2]/div/div/div", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  10127.  
  10128. outerMostBracket.addEventListener("click", function (event) {
  10129. enableTabs();
  10130. });
  10131.  
  10132. // Deactivate tabs that are not compatible with the current tab
  10133. var incompatibleTabs = getIncompatibleTabs(tab);
  10134. if (!wasTabClicked || (colorGroup.includes(tab) && !allTabsEnabled)) {
  10135. for (var i = 0; i < incompatibleTabs.length; i++) {
  10136. currentTab = FIND(incompatibleTabs[i] + "_tab");
  10137. if (currentTab) {
  10138. currentTab.classList.add("disabled");
  10139. currentTab.disabled = true;
  10140. allTabsEnabled = false;
  10141. }
  10142. }
  10143. }
  10144. // Reset the flag
  10145. wasTabClicked = false;
  10146. }
  10147.  
  10148. function getIncompatibleTabs(tab) {
  10149. let incompatibleTabs;
  10150.  
  10151. if (colorGroup.includes(tab)) {
  10152. // If the current tab is in the color group, filter out all other tabs outside this group
  10153. incompatibleTabs = allTabs.filter(t => !colorGroup.includes(t));
  10154. } else {
  10155. // If the current tab is NOT in the color group, filter out all tabs including the color group
  10156. incompatibleTabs = allTabs.filter(t => t !== tab);
  10157. }
  10158.  
  10159. return incompatibleTabs;
  10160. }
  10161.  
  10162.  
  10163. </script>
  10164. </head>
  10165.  
  10166. <!-- <body onload="initGL()"> -->
  10167. <body>
  10168.  
  10169. <table>
  10170. <tr>
  10171. <td class="page-left">
  10172. <div class="canvas-container">
  10173. <canvas
  10174. id="canvas_id"
  10175. title="Blade Preview. Move mouse to swing. Click to Clash or Do Selected Effect (and to dismiss this Tooltip.) Goto settings to change hilt model or toggle Mouse Swings mode (no swing on mouse move.)"
  10176. onmousemove='mouse_move(event)' onmouseleave='mouse_leave(event)'
  10177. onclick='AddClickedEffect()'>
  10178. </canvas>
  10179. <button
  10180. id="ENLARGE"
  10181. title="Click to toggle blade preview window size."
  10182. style="position:absolute; bottom:10px; right:10px;">Enlarge</button>
  10183. </div>
  10184. <br>
  10185. <button
  10186. id="POWER_BUTTON"
  10187. title="Turn the blade On and Off. Simulates ignition and retraction events on the blade."
  10188. onclick="ClickPower()">Power</button>
  10189. <button
  10190. title="Simulates an impact hit on the blade. Triggers style code using EFFECT_CLASH."
  10191. onclick="AddClash()">Clash</button>
  10192. <button
  10193. title="Simulates an impact hit on the tip of the blade. Triggers style code using EFFECT_STAB."
  10194. onclick="AddStab()">Stab</button>
  10195. <button
  10196. title="Simulates a blaster bolt deflection impact hit on the blade. Triggers style code using EFFECT_BLAST."
  10197. onclick="AddBlast()">Blast</button>
  10198. <button
  10199. title="Triggers style code using EFFECT_FORCE."
  10200. onclick="AddForce()">Force</button>
  10201. <button
  10202. title="Simulates powering on the Proffieboard. Triggers style code Int<using EFFECT_BOOT."
  10203. onclick="AddBoot()">Boot</button>
  10204. <button
  10205. title="Simulates changing presets. Triggers style code using EFFECT_NEWFONT."
  10206. onclick="AddNewfont()">NewFont</button>
  10207. <button
  10208. title="Simulates &quot;pre-ignition&quot;; pressing the power button when there are preon.wav files available in the current font. Triggers style code using EFFECT_PREON."
  10209. onclick="AddPreon()">Preon</button>
  10210.  
  10211. <select
  10212. id="LOCKUP" name="lockup"
  10213. title="Choose a Lockup Type to begin lockup effect. Choose &quot;No Lockup&quot; to end lockup."
  10214. onchange="OnLockupChange()" >
  10215. <option value=LOCKUP_NONE>No lockup</option>
  10216. <option value=LOCKUP_NORMAL>Lockup</option>
  10217. <option value=LOCKUP_DRAG>Drag</option>
  10218. <option value=LOCKUP_MELT>Melt</option>
  10219. <option value=LOCKUP_LIGHTNING_BLOCK>LB</option>
  10220. <option value=LOCKUP_AUTOFIRE>Autofire</option>
  10221.  
  10222. </select>
  10223.  
  10224. <span class="more-menu-container">
  10225. <select
  10226. id="more_effects_menu"
  10227. title="This menu contains all of the additional EFFECTS. Select one, then use the &quot;Do Selected Effect&quot; button to trigger the effect." >
  10228. <option value="">Select More Effects</option>
  10229. </select>
  10230. <input
  10231. type=button id="do_selected" value="Do Selected Effect"
  10232. title="Press this to trigger the EFFECT selected in the More Effects menu.">
  10233. </span>
  10234.  
  10235. <script>
  10236. // Get the menu and button elements
  10237. const menu = FIND('more_effects_menu');
  10238. const do_selected_button = FIND('do_selected');
  10239.  
  10240. /* Add values from the enum builder to an array and sort alphabetically,
  10241. excluding effects with dedicated buttons.*/
  10242. const values = Object.entries(EFFECT_ENUM_BUILDER.value_to_name)
  10243. .sort((a, b) => a[1].localeCompare(b[1]))
  10244. .filter(([value]) => ![
  10245. EFFECT_CLASH,
  10246. EFFECT_STAB,
  10247. EFFECT_BLAST,
  10248. EFFECT_FORCE,
  10249. EFFECT_BOOT,
  10250. EFFECT_NEWFONT,
  10251. EFFECT_PREON,
  10252. ].includes(Number(value)));
  10253.  
  10254. // Add sub-groups for the different categories of effects
  10255. const generalEffectsMenu = document.createElement('optgroup');
  10256. generalEffectsMenu.label = 'General Effects';
  10257. const userEffectsMenu = document.createElement('optgroup');
  10258. userEffectsMenu.label = 'User Effects';
  10259. const blasterEffectsMenu = document.createElement('optgroup');
  10260. blasterEffectsMenu.label = 'Blaster Effects';
  10261. const gameEffectsMenu = document.createElement('optgroup');
  10262. gameEffectsMenu.label = 'Game Effects';
  10263. const errorMessagesMenu = document.createElement('optgroup');
  10264. errorMessagesMenu.label = 'Error Messages';
  10265.  
  10266. // Add sorted values to the menu and actions dictionary,
  10267. for (const [value, name] of values) {
  10268. const nameWithoutEffect = name.replace(/^EFFECT_/, '');
  10269. const option = document.createElement('option');
  10270. option.value = value;
  10271. option.text = nameWithoutEffect;
  10272.  
  10273. // Check if the effect belongs to a certain category and add it to the corresponding sub-group
  10274. if (name.startsWith('EFFECT_GAME')) {
  10275. gameEffectsMenu.appendChild(option);
  10276. } else if (name.startsWith('EFFECT_USER')) {
  10277. userEffectsMenu.appendChild(option);
  10278. } else {
  10279. switch (Number(value)) {
  10280. case EFFECT_STUN:
  10281. case EFFECT_FIRE:
  10282. case EFFECT_CLIP_IN:
  10283. case EFFECT_CLIP_OUT:
  10284. case EFFECT_RELOAD:
  10285. case EFFECT_MODE:
  10286. case EFFECT_RANGE:
  10287. case EFFECT_EMPTY:
  10288. case EFFECT_FULL:
  10289. case EFFECT_JAM:
  10290. case EFFECT_UNJAM:
  10291. case EFFECT_PLI_ON:
  10292. case EFFECT_PLI_OFF:
  10293. blasterEffectsMenu.appendChild(option);
  10294. break;
  10295. case EFFECT_ERROR_IN_BLADE_ARRAY:
  10296. case EFFECT_ERROR_IN_FONT_DIRECTORY:
  10297. case EFFECT_FONT_DIRECTORY_NOT_FOUND:
  10298. case EFFECT_SD_CARD_NOT_FOUND:
  10299. case EFFECT_LOW_BATTERY:
  10300. errorMessagesMenu.appendChild(option);
  10301. break;
  10302. default:
  10303. generalEffectsMenu.appendChild(option);
  10304. break;
  10305. }
  10306. }
  10307. }
  10308.  
  10309. // Add the sub-groups to the main menu
  10310. menu.appendChild(generalEffectsMenu);
  10311. menu.appendChild(userEffectsMenu);
  10312. menu.appendChild(blasterEffectsMenu);
  10313. menu.appendChild(gameEffectsMenu);
  10314. menu.appendChild(errorMessagesMenu);
  10315.  
  10316. // Set up the event listener for the menu
  10317. menu.addEventListener('change', function() {
  10318. // If the selected value is not the default, enable the button and set its action
  10319. if (menu.value !== '') {
  10320. do_selected_button.disabled = false;
  10321. do_selected_button.className = "button-on"
  10322. do_selected_button.onclick = function() {
  10323. AddClickedEffect();
  10324. };
  10325. } else {
  10326. // If the selected value is the default, disable the button
  10327. do_selected_button.disabled = true;
  10328. do_selected_button.onclick = null;
  10329. do_selected_button.className = "button-off"
  10330. }
  10331. });
  10332. // Disable the button initially
  10333. do_selected_button.disabled = true;
  10334. do_selected_button.className = "button-off"
  10335.  
  10336. // What to do when preview saber area is clicked
  10337. function AddClickedEffect() {
  10338. if (do_selected_button.disabled) {
  10339. AddClash();
  10340. } else {
  10341. blade.addEffect(menu.value, 0.0)
  10342. }
  10343. };
  10344. </script>
  10345.  
  10346. <span class="variant-alt-container">
  10347. <span class="variant-alt-controls">
  10348. <span class="variant-label">Variant:</span>
  10349. <input
  10350. id="VARIANT_SLIDER" name=varslider type=range class="variant-slider"
  10351. title="Slide to adjust the Variant value."
  10352. min="0" max="32768" step="1" value="0"
  10353. oninput="updateVariantValue(this.value)" >
  10354. <input
  10355. id="VARIANT_MINUS" name=varminus type=button value="<"
  10356. title="Decreases the Variant value. Hold to increase speed of the changing value."
  10357. onmousedown="startAdjustingValue(-1, 'VARIANT_SLIDER')"
  10358. onmouseup="stopAdjustingValue()"
  10359. onmouseleave="stopAdjustingValue()" >
  10360. <input
  10361. id="VARIANT_VALUE" name=varvalue type='number' value="0"
  10362. title="The current Variant value. You can also type in this field for direct entry of a value."
  10363. oninput="updateVariantValue(this.value)"
  10364. onfocusout="SafeguardInputs(event)" >
  10365. <input
  10366. id="VARIANT_PLUS" name=varplus type=button value=">"
  10367. title="Increases the Variant value. Hold to increase speed of the changing value."
  10368. onmousedown="startAdjustingValue(1, 'VARIANT_SLIDER')"
  10369. onmouseup="stopAdjustingValue()"
  10370. onmouseleave="stopAdjustingValue()" >
  10371. <span class="alt-label">Alt:</span>
  10372. <input
  10373. id="ALT_MINUS" name=altminus type=button value="<"
  10374. title="Decreases the Alt value."
  10375. onclick="IncreaseAlt(-1)" >
  10376. <input
  10377. id="ALT" name=alt type='number' value="0"
  10378. title="The current Alt value. You can also type in this field for direct entry of a value."
  10379. oninput="updateAltValue(this.value)"
  10380. onfocusout="SafeguardInputs(event)" >
  10381. <input
  10382. id="ALT_PLUS" name=altplus type=button value=">"
  10383. title="Increases the Alt value."
  10384. onclick="IncreaseAlt(1)" >
  10385. </span>
  10386. </span>
  10387. <br>
  10388. <script>
  10389.  
  10390. var num_alternatives = 1000;
  10391.  
  10392. function Alt() {
  10393. return parseInt(FIND("ALT").value);
  10394. }
  10395.  
  10396. function updateAltValue(newValue) {
  10397. if (newValue > num_alternatives) {
  10398. newValue = num_alternatives;
  10399. }
  10400. FIND("ALT").value = newValue;
  10401. console.log("Updated Alt: " + newValue);
  10402. }
  10403.  
  10404. function IncreaseAlt(n) {
  10405. var v = Alt() + n;
  10406. if (v < 0) v += num_alternatives;
  10407. if (v > num_alternatives) v -= num_alternatives;
  10408. FIND("ALT").value = v;
  10409. console.log("Updated Alt: " + v)
  10410. }
  10411.  
  10412. function Variant() {
  10413. return parseInt(FIND("VARIANT_VALUE").value);
  10414. }
  10415.  
  10416. /* Variant Slider functions */
  10417.  
  10418. function updateVariantValue(newValue) {
  10419. // Ensure values are in range, and auto-filled zeros get registered as 0.
  10420. if (newValue < 0) {
  10421. newValue = 0;
  10422. } else if (newValue > 32768) {
  10423. newValue = 32768;
  10424. }
  10425. FIND("VARIANT_VALUE").value = newValue;
  10426. FIND("VARIANT_SLIDER").value = newValue;
  10427. console.log("Updated Variant: " + newValue);
  10428. }
  10429.  
  10430. var timeoutId, intervalId;
  10431.  
  10432. // Single click arrow to adjust by 1, hold to accelerate.
  10433. function startAdjustingValue(adjustment, inputId) {
  10434. adjustmentValue(adjustment, inputId);
  10435. var speed = 100;
  10436. clearTimeout(timeoutId);
  10437. timeoutId = setTimeout(function() {
  10438. var startTime = new Date().getTime();
  10439. intervalId = setInterval(function() {
  10440. var elapsedTime = new Date().getTime() - startTime;
  10441. var progress = elapsedTime / speed;
  10442. var ease = Math.pow(progress, 2);
  10443. var value = Math.round(adjustment * ease);
  10444. adjustmentValue(value, inputId);
  10445. }, 1000 / 60); // 60 FPS for more responsive input.
  10446. }, 500); // delay until hold down button acceleration starts.
  10447. }
  10448.  
  10449. function adjustmentValue(adjustment, inputId) {
  10450. var variantInput = FIND(inputId);
  10451. var newValue = parseInt(variantInput.value) + adjustment;
  10452. variantInput.value = newValue;
  10453. updateVariantValue(newValue);
  10454. }
  10455.  
  10456. // Release or mouse leave arrow button
  10457. function stopAdjustingValue() {
  10458. clearInterval(intervalId);
  10459. clearTimeout(timeoutId);
  10460. }
  10461. /* End Variant Slider functions */
  10462.  
  10463. </script>
  10464.  
  10465. <span id="error_message" class="error-message"></span>
  10466.  
  10467. <pre><textarea
  10468. rows=10 cols=80 id=style wrap="off" class="textbox"
  10469. title="Style and value editing area. Edits in here are committed by clicking the Submit button below." >
  10470. </textarea></pre>
  10471.  
  10472. <span>
  10473. <button
  10474. class="submit-button" title="Submit what is currently in the text box above."
  10475. onclick="Run()">
  10476. <i class="fa-solid fa-right-from-bracket"></i> Submit</button>
  10477. <button
  10478. title="Adds StylePtr&lt;&gt;() wrapper and copies the style to the clipboard."
  10479. onclick="Copy()">
  10480. <i class="fa-solid fa-copy"></i> Copy</button>
  10481. <button
  10482. id="expand_button"
  10483. title="Reveals the &quot;under the hood&quot; code that the shorthand macro uses. This allows access to otherwise omitted arguments, and is only an active button when applicable."
  10484. onclick="DoExpand()">
  10485. <i class="fa-solid fa-up-right-and-down-left-from-center"></i> Expand</button>
  10486. <button
  10487. id="layerize_button"
  10488. title="Transforms nested version style code and breaks it out into more contemporary Layers format."
  10489. onclick="DoLayerize()">
  10490. <i class="fa-solid fa-layer-group"></i> Layerize</button>
  10491. <button
  10492. title="Converts arguments to their ARG version counterparts for use with ProffieOS Workbench or Edit Mode. See the &quot;ArgString&quot; tab below."
  10493. onclick="DoArgify()">
  10494. <i class="fa-solid fa-list"></i> Argify</button>
  10495. <button
  10496. id="ROTATE_BUTTON" title="Toggles animation of the preview blade above."
  10497. onclick="ClickRotate()">
  10498. <i class="fa-solid fa-arrows-rotate"></i> Rotate</button>
  10499. <button
  10500. title="Save the blade style to your computer."
  10501. onclick="ClickSave()">
  10502. <i class="fa-solid fa-save"></i> Save</button>
  10503. <button
  10504. id="SETTINGS_BUTTON" title="Settings"
  10505. onclick="toggleSettingsPanel()">
  10506. <i class="fa-solid fa-cog"></i> Settings</button>
  10507. </span>
  10508.  
  10509. <div id="settings_panel" class="settings-panel"
  10510. title="Turning on checkboxes saves your settings for future visits to the Style Editor page.">
  10511.  
  10512. <button
  10513. title="Close Settings Panel."
  10514. class="close-settings" onclick="toggleSettingsPanel()">X</button>
  10515.  
  10516. <h2 class="settings-header-label" title="Settings Panel">Settings</h2>
  10517.  
  10518. <div class="settings-section">
  10519. <h4 class="settings-section-label" title="These settings apply to the whole page.">General:</h4>
  10520.  
  10521. <label title="Toggles a Dark Mode look to this page.">
  10522. <input type="checkbox" id="DARK_BUTTON" name="darkmode" onclick="handleSettings(this)">
  10523. <span>Dark Mode</span>
  10524. </label>
  10525.  
  10526. <label title="Toggles pop-up tooltips On or Off.">
  10527. <input type="checkbox" id="TIPS_BUTTON" name="tooltips" onclick="handleSettings(this)">
  10528. <span >Tool Tips</span>
  10529. </label>
  10530.  
  10531. <label title="Toggle between sorting the Colors tab hue or by name.">
  10532. <input type="checkbox" id="COLORSORT_BUTTON" name="colorsort" onclick="handleSettings(this)">
  10533. <span>Sort Colors by name</span>
  10534. </label>
  10535.  
  10536. </div>
  10537.  
  10538. <div class="settings-section">
  10539. <h4 class="settings-section-label" title="These settings apply to the blade preview above.">Blade Preview:</h4>
  10540.  
  10541. <label title="Toggles the Graflex Hilt model in the preview.">
  10542. <input type="checkbox" id="GRAFLEX_BUTTON" name="graflex" onclick="handleSettings(this)">
  10543. <span>Graflex Hilt</span>
  10544. </label>
  10545.  
  10546. <label title="Toggles the option for swinging around the preview blade above using mouse control.">
  10547. <input type="checkbox" id="MOUSESWINGS_BUTTON" name="mouseswings" onclick="handleSettings(this)">
  10548. <span>Disable mouse swings</span>
  10549. </label>
  10550.  
  10551. <label title="Toggles automated swing emulation on the preview blade while at rest (ie: shown when using SwingSpeed<>). Swinging while hovering over the preview area always shows swing blade effects according to movements.">
  10552. <input type="checkbox" id="AUTOSWING_BUTTON" name="autoswing" onclick="handleSettings(this)">
  10553. <span>Swing Emulation</span>
  10554. </label>
  10555.  
  10556. <label title="Simulates In-Hilt LED blades, (no addressable pixel effects).">
  10557. <input type="checkbox" id="INHILT_BUTTON" name="inhilt" onclick="handleSettings(this)">
  10558. <span>Inhilt LED</span>
  10559. </label>
  10560.  
  10561. <label title="Provides &quot;snapshots&quot; of rapidly moving things to see what they look like without fast movement. *Note* Graflex hilt model probably won't be able to render with this on.">
  10562. <input type="checkbox" id="SLOW_BUTTON" name="slow" onclick="handleSettings(this)">
  10563. <span>Slow</span>
  10564. </label>
  10565. </div>
  10566.  
  10567. <div class="settings-section">
  10568. <label title="Enter a custom WavLen value in milliseconds." class="wavlen-label">
  10569. WavLen time:
  10570. <input type='number' id="WAVLEN_VALUE" class="wavlen-value" value="500">
  10571. </label>
  10572. </div>
  10573. <br>
  10574. <button
  10575. title="Restore all settings to default, including restoring popups."
  10576. id="restore_defaults" onclick="ClickRestore()">Restore Defaults</button>
  10577. </div>
  10578. <script>
  10579.  
  10580. function toggleSettingsPanel() {
  10581. var settingsButton = FIND("SETTINGS_BUTTON");
  10582. var settingsPanel = FIND("settings_panel");
  10583.  
  10584. settingsPanel.classList.toggle("show");
  10585.  
  10586. // Mouseleave event listener
  10587. if (settingsPanel.classList.contains("show")) {
  10588. var timeoutId = null;
  10589. settingsPanel.addEventListener("mouseleave", function(e) {
  10590. timeoutId = setTimeout(function() {
  10591. settingsPanel.classList.remove("show");
  10592. }, 1000);
  10593. });
  10594. settingsPanel.addEventListener("mouseenter", function(e) {
  10595. if (timeoutId) {
  10596. clearTimeout(timeoutId);
  10597. timeoutId = null;
  10598. }
  10599. });
  10600. }
  10601. }
  10602.  
  10603. // Call the onPageLoad function when the page is loaded
  10604. window.addEventListener('DOMContentLoaded', onPageLoad);
  10605.  
  10606. var all_saved_states = [];
  10607. var state_by_checkbox = new Map();
  10608. var body = document.querySelector("body");
  10609. var structuredView;
  10610. var wavlenInput = FIND("WAVLEN_VALUE");
  10611. var myWavLen = new WavLenClass();
  10612.  
  10613. /* Settings buttons saved as local storage */
  10614. function getSavedState(buttonState, defaultValue) {
  10615. var value = localStorage.getItem(buttonState);
  10616. console.log("Retrieved SavedState for " + buttonState + ": " + value);
  10617.  
  10618. if (value === null) {
  10619. return defaultValue;
  10620. }
  10621. if (buttonState === "wavlenSave"){
  10622. return value;
  10623. }
  10624. return value !== "false";
  10625. }
  10626.  
  10627. function saveState(buttonState, settingIsOn) {
  10628. localStorage.setItem(buttonState, settingIsOn);
  10629. }
  10630.  
  10631. class SavedState {
  10632. constructor(name, def, update_function, type = "checkbox") {
  10633. this.name = name;
  10634. this.def = def;
  10635. this.update_function = update_function;
  10636. this.type = type;
  10637. all_saved_states.push(this);
  10638. const checkbox = FIND(this.name.toUpperCase()+"_BUTTON");
  10639. state_by_checkbox.set(checkbox, this);
  10640. }
  10641. onload() {
  10642. this.set(getSavedState(this.name + "Save", this.def));
  10643. }
  10644. set(value) {
  10645. this.value = value;
  10646. if (this.type === "checkbox") {
  10647. FIND(this.name.toUpperCase() + "_BUTTON").checked = value ? true : false;
  10648. } else if (this.type === "number") {
  10649. FIND(this.name.toUpperCase() + "_VALUE").value = value;
  10650. }
  10651. saveState(this.name + "Save", value);
  10652. this.update_function(value);
  10653. }
  10654. get() { return this.value; }
  10655. }
  10656.  
  10657. var darkState = new SavedState("dark", false, (on) => {
  10658. body.classList.toggle("dark-mode", on);
  10659. structuredView.classList.toggle("dark-mode", on);
  10660. });
  10661.  
  10662. var tipsState = new SavedState("tips", true, (on) => {
  10663. if (on) {
  10664. const elementsWithDataTitles = document.querySelectorAll("[data-title]");
  10665. elementsWithDataTitles.forEach((element) => {
  10666. element.setAttribute("title", element.dataset.title);
  10667. element.removeAttribute("data-title");
  10668. });
  10669. } else {
  10670. const elementsWithTitles = document.querySelectorAll("[title]");
  10671. elementsWithTitles.forEach((element) => {
  10672. element.dataset.title = element.getAttribute("title");
  10673. element.removeAttribute("title");
  10674. });
  10675. }
  10676. });
  10677.  
  10678. var colorsortState = new SavedState("colorsort", false, (on) => {
  10679. updateRgbTabContent();
  10680. });
  10681.  
  10682. var graflexState = new SavedState("graflex", true, (on) => { compile(); });
  10683. var mouseswingsState = new SavedState("mouseswings", false, (on) => {});
  10684. var autoswingState = new SavedState("autoswing", true, (on) => {});
  10685. var inhiltState = new SavedState("inhilt", false, (on) => { STATE_NUM_LEDS = on ? 1 : 144; });
  10686. var slowState = new SavedState("slow", false, (on) => { framesPerUpdate = on ? 10 : 0; time_factor = framesPerUpdate == 0 ? 1000 : (500/framesPerUpdate)});
  10687. var wavlenState = new SavedState("wavlen", 500, (value) => {
  10688. myWavLen.setLength(value);
  10689. }, "number");
  10690. wavlenInput.addEventListener("input", function(e) {
  10691. var value = parseInt(this.value);
  10692. var newValue = (isNaN(value) || value < 1) ? 1 : value;
  10693. this.value = newValue;
  10694. wavlenState.set(newValue);
  10695. });
  10696.  
  10697. function onPageLoad() {
  10698. initGL();
  10699. structuredView = FIND("structured_view");
  10700. all_saved_states.forEach(state => {
  10701. state.onload();
  10702. });
  10703. }
  10704.  
  10705. function handleSettings(checkbox) {
  10706. var state = state_by_checkbox.get(checkbox);
  10707. state.set(!state.get());
  10708. }
  10709.  
  10710. function ClickRestore() {
  10711. localStorage.clear();
  10712. onPageLoad();
  10713. }
  10714.  
  10715. </script>
  10716.  
  10717. <br>
  10718. <!-- <div id=TABS class="tabs-container"> -->
  10719. <div id=TABS>
  10720. <div id=TABLINKS class=tab></div>
  10721. <div id=TABBODIES></div>
  10722. </div>
  10723. <div class="footer-container">
  10724. <span class="footer-title">ProffieOS Style Editor</span>
  10725. <span class="footer-links">
  10726. <span class="other-sites">Other sites: </span>
  10727. <a href="https://crucible.hubbe.net/" target="_blank"><img src="https://crucible.hubbe.net/uploads/default/optimized/1X/2237f551ca8f4f69ac478df5c64aee1c951c33f5_2_180x180.png" alt="Crucible logo"></a>
  10728. <a href="https://pod.hubbe.net/" target="_blank"><img src="https://pod.hubbe.net/images/favicon.png" alt="Pod logo"></a>
  10729. <a href="https://fredrik.hubbe.net/lightsaber/" target="_blank"><img src="https://fredrik.hubbe.net/favicon.ico" alt="Lightsaber logo"></a>
  10730. <a href="https://www.fett263.com/" target="_blank"><img src="https://www.fett263.com//favicon.ico" alt="Fett263 logo"></a>
  10731. <a href="https://www.facebook.com/groups/opensourcesabers/" target="_blank"><img src="https://www.facebook.com/favicon.ico" alt="Facebook logo"></a>
  10732. </span>
  10733. </div>
  10734. </td>
  10735. <td class="page-right">
  10736. <div id="structured_view" class="structured-view">
  10737. <span class="structured-view-label">Structured view, click to edit.</span>
  10738. <div id="pp" class="pp"></div>
  10739. </div>
  10740. </td>
  10741. </tr>
  10742. </table>
  10743. </body>
  10744. </html>
  10745.  
Add Comment
Please, Sign In to add comment