UNPKG

171 kBJavaScriptView Raw
1import {
2 TraversalUp,
3 directedGenerator
4} from "./chunk-CGV2FNCG.js";
5import {
6 experimentalAppendMove
7} from "./chunk-FCMGMWMB.js";
8import {
9 KPuzzleSVGWrapper,
10 areStatesEquivalent,
11 areTransformationsEquivalent,
12 transformationForQuantumMove
13} from "./chunk-PM3Y2HC7.js";
14import {
15 getFaceletAppearance
16} from "./chunk-CQ4FB3JL.js";
17import {
18 KPuzzle,
19 combineTransformations,
20 identityTransformation,
21 invertTransformation,
22 puzzles,
23 transformationForMove
24} from "./chunk-QACYPZ22.js";
25import {
26 __async
27} from "./chunk-BYEGYEXS.js";
28import {
29 Alg,
30 AlgBuilder,
31 Conjugate,
32 Grouping,
33 IterationDirection,
34 Move,
35 TraversalDownUp,
36 __accessCheck,
37 __privateGet,
38 __privateSet,
39 direct
40} from "./chunk-UJ4KPAG6.js";
41var __defProp = Object.defineProperty;
42var __hasOwnProp = Object.prototype.hasOwnProperty;
43var __getOwnPropSymbols = Object.getOwnPropertySymbols;
44var __propIsEnum = Object.prototype.propertyIsEnumerable;
45var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {enumerable: true, configurable: true, writable: true, value}) : obj[key] = value;
46var __assign = (a, b) => {
47 for (var prop in b || (b = {}))
48 if (__hasOwnProp.call(b, prop))
49 __defNormalProp(a, prop, b[prop]);
50 if (__getOwnPropSymbols)
51 for (var prop of __getOwnPropSymbols(b)) {
52 if (__propIsEnum.call(b, prop))
53 __defNormalProp(a, prop, b[prop]);
54 }
55 return a;
56};
57var __privateMethod = (obj, member, method) => {
58 __accessCheck(obj, member, "access private method");
59 return method;
60};
61
62// src/cubing/twisty/dom/viewers/Twisty3DCanvas.ts
63import {PerspectiveCamera, Vector3 as Vector32, WebGLRenderer} from "three";
64
65// src/cubing/twisty/animation/RenderScheduler.ts
66var RenderScheduler = class {
67 constructor(callback) {
68 this.callback = callback;
69 this.animFrameID = null;
70 this.animFrame = this.animFrameWrapper.bind(this);
71 }
72 requestAnimFrame() {
73 if (!this.animFrameID) {
74 this.animFrameID = requestAnimationFrame(this.animFrame);
75 }
76 }
77 cancelAnimFrame() {
78 if (this.animFrameID) {
79 cancelAnimationFrame(this.animFrameID);
80 this.animFrameID = 0;
81 }
82 }
83 animFrameWrapper(timestamp) {
84 this.animFrameID = 0;
85 this.callback(timestamp);
86 }
87};
88
89// src/cubing/twisty/dom/element/node-custom-element-shims.ts
90var HTMLElementStub = class {
91};
92var HTMLElementShim;
93if (typeof HTMLElement !== "undefined") {
94 HTMLElementShim = HTMLElement;
95} else {
96 HTMLElementShim = HTMLElementStub;
97}
98var CustomElementsStub = class {
99 define() {
100 }
101};
102var customElementsShim;
103if (typeof customElements !== "undefined") {
104 customElementsShim = customElements;
105} else {
106 customElementsShim = new CustomElementsStub();
107}
108
109// src/cubing/twisty/dom/element/ManagedCustomElement.ts
110var CSSSource = class {
111 constructor(sourceText) {
112 this.sourceText = sourceText;
113 }
114 getAsString() {
115 return this.sourceText;
116 }
117};
118var _cssSourceMap;
119var ManagedCustomElement = class extends HTMLElementShim {
120 constructor(options) {
121 super();
122 _cssSourceMap.set(this, new Map());
123 var _a;
124 this.shadow = this.attachShadow({mode: (_a = options == null ? void 0 : options.mode) != null ? _a : "closed"});
125 this.contentWrapper = document.createElement("div");
126 this.contentWrapper.classList.add("wrapper");
127 this.shadow.appendChild(this.contentWrapper);
128 }
129 addCSS(cssSource) {
130 if (__privateGet(this, _cssSourceMap).get(cssSource)) {
131 return;
132 }
133 const cssElem = document.createElement("style");
134 cssElem.textContent = cssSource.getAsString();
135 __privateGet(this, _cssSourceMap).set(cssSource, cssElem);
136 this.shadow.appendChild(cssElem);
137 }
138 removeCSS(cssSource) {
139 const cssElem = __privateGet(this, _cssSourceMap).get(cssSource);
140 if (!cssElem) {
141 return;
142 }
143 this.shadow.removeChild(cssElem);
144 __privateGet(this, _cssSourceMap).delete(cssSource);
145 }
146 addElement(element) {
147 return this.contentWrapper.appendChild(element);
148 }
149 prependElement(element) {
150 this.contentWrapper.prepend(element);
151 }
152 removeElement(element) {
153 return this.contentWrapper.removeChild(element);
154 }
155};
156_cssSourceMap = new WeakMap();
157customElementsShim.define("twisty-managed-custom-element", ManagedCustomElement);
158
159// src/cubing/twisty/dom/viewers/canvas.ts
160function pixelRatio() {
161 return devicePixelRatio || 1;
162}
163
164// src/cubing/twisty/dom/viewers/Twisty3DCanvas.css_.ts
165var twisty3DCanvasCSS = new CSSSource(`
166:host {
167 width: 384px;
168 height: 256px;
169 display: grid;
170}
171
172.wrapper {
173 width: 100%;
174 height: 100%;
175 display: grid;
176 overflow: hidden;
177}
178
179/* TODO: This is due to stats hack. Replace with \`canvas\`. */
180.wrapper > canvas {
181 max-width: 100%;
182 max-height: 100%;
183}
184
185.wrapper.invisible {
186 opacity: 0;
187}
188`);
189
190// src/cubing/twisty/dom/viewers/TwistyOrbitControls.ts
191import {Spherical, Vector3} from "three";
192var EPSILON = 1e-8;
193var INERTIA_DEFAULT = true;
194var LATITUDE_LIMITS_DEFAULT = true;
195var INERTIA_DURATION_MS = 500;
196var INERTIA_TIMEOUT_MS = 50;
197var VERTICAL_MOVEMENT_BASE_SCALE = 0.75;
198function momentumScale(progress) {
199 return (Math.exp(1 - progress) - (1 - progress)) / (1 - Math.E) + 1;
200}
201var Inertia = class {
202 constructor(startTimestamp, momentumX, momentumY, callback) {
203 this.startTimestamp = startTimestamp;
204 this.momentumX = momentumX;
205 this.momentumY = momentumY;
206 this.callback = callback;
207 this.scheduler = new RenderScheduler(this.render.bind(this));
208 this.scheduler.requestAnimFrame();
209 this.lastTimestamp = startTimestamp;
210 }
211 render(now) {
212 const progressBefore = (this.lastTimestamp - this.startTimestamp) / INERTIA_DURATION_MS;
213 const progressAfter = Math.min(1, (now - this.startTimestamp) / INERTIA_DURATION_MS);
214 if (progressBefore === 0 && progressAfter > INERTIA_TIMEOUT_MS / INERTIA_DURATION_MS) {
215 return;
216 }
217 const delta = momentumScale(progressAfter) - momentumScale(progressBefore);
218 this.callback(this.momentumX * delta * 1e3, this.momentumY * delta * 1e3);
219 if (progressAfter < 1) {
220 this.scheduler.requestAnimFrame();
221 }
222 this.lastTimestamp = now;
223 }
224};
225var TwistyOrbitControls = class {
226 constructor(camera, canvas, scheduleRender) {
227 this.camera = camera;
228 this.canvas = canvas;
229 this.scheduleRender = scheduleRender;
230 this.experimentalInertia = INERTIA_DEFAULT;
231 this.experimentalLatitudeLimits = LATITUDE_LIMITS_DEFAULT;
232 this.lastTouchClientX = 0;
233 this.lastTouchClientY = 0;
234 this.currentTouchID = null;
235 this.onMoveBound = this.onMove.bind(this);
236 this.onMouseMoveBound = this.onMouseMove.bind(this);
237 this.onMouseEndBound = this.onMouseEnd.bind(this);
238 this.onTouchMoveBound = this.onTouchMove.bind(this);
239 this.onTouchEndBound = this.onTouchEnd.bind(this);
240 this.tempSpherical = new Spherical();
241 this.lastTouchTimestamp = 0;
242 this.lastTouchMoveMomentumX = 0;
243 this.lastTouchMoveMomentumY = 0;
244 this.lastMouseTimestamp = 0;
245 this.lastMouseMoveMomentumX = 0;
246 this.lastMouseMoveMomentumY = 0;
247 this.experimentalHasBeenMoved = false;
248 canvas.addEventListener("mousedown", this.onMouseStart.bind(this));
249 canvas.addEventListener("touchstart", this.onTouchStart.bind(this));
250 }
251 temperMovement(f) {
252 return Math.sign(f) * Math.log(Math.abs(f * 10) + 1) / 6;
253 }
254 onMouseStart(e) {
255 window.addEventListener("mousemove", this.onMouseMoveBound);
256 window.addEventListener("mouseup", this.onMouseEndBound);
257 this.onStart(e);
258 this.lastMouseTimestamp = e.timeStamp;
259 }
260 onMouseMove(e) {
261 if (e.buttons === 0) {
262 this.onMouseEnd(e);
263 return;
264 }
265 const minDim = Math.min(this.canvas.offsetWidth, this.canvas.offsetHeight);
266 const movementX = this.temperMovement(e.movementX / minDim);
267 const movementY = this.temperMovement(e.movementY / minDim * VERTICAL_MOVEMENT_BASE_SCALE);
268 this.onMove(movementX, movementY);
269 this.lastMouseMoveMomentumX = movementX / (e.timeStamp - this.lastMouseTimestamp);
270 this.lastMouseMoveMomentumY = movementY / (e.timeStamp - this.lastMouseTimestamp);
271 this.lastMouseTimestamp = e.timeStamp;
272 }
273 onMouseEnd(e) {
274 window.removeEventListener("mousemove", this.onMouseMoveBound);
275 window.removeEventListener("mouseup", this.onMouseEndBound);
276 this.onEnd(e);
277 if (this.experimentalInertia) {
278 new Inertia(this.lastMouseTimestamp, this.lastMouseMoveMomentumX, this.lastMouseMoveMomentumY, this.onMoveBound);
279 }
280 }
281 onTouchStart(e) {
282 if (this.currentTouchID === null) {
283 this.currentTouchID = e.changedTouches[0].identifier;
284 this.lastTouchClientX = e.touches[0].clientX;
285 this.lastTouchClientY = e.touches[0].clientY;
286 window.addEventListener("touchmove", this.onTouchMoveBound);
287 window.addEventListener("touchend", this.onTouchEndBound);
288 window.addEventListener("touchcanel", this.onTouchEndBound);
289 this.onStart(e);
290 this.lastTouchTimestamp = e.timeStamp;
291 }
292 }
293 onTouchMove(e) {
294 for (let i = 0; i < e.changedTouches.length; i++) {
295 const touch = e.changedTouches[i];
296 if (touch.identifier === this.currentTouchID) {
297 const minDim = Math.min(this.canvas.offsetWidth, this.canvas.offsetHeight);
298 const movementX = this.temperMovement((touch.clientX - this.lastTouchClientX) / minDim);
299 const movementY = this.temperMovement((touch.clientY - this.lastTouchClientY) / minDim * VERTICAL_MOVEMENT_BASE_SCALE);
300 this.onMove(movementX, movementY);
301 this.lastTouchClientX = touch.clientX;
302 this.lastTouchClientY = touch.clientY;
303 this.lastTouchMoveMomentumX = movementX / (e.timeStamp - this.lastTouchTimestamp);
304 this.lastTouchMoveMomentumY = movementY / (e.timeStamp - this.lastTouchTimestamp);
305 this.lastTouchTimestamp = e.timeStamp;
306 }
307 }
308 }
309 onTouchEnd(e) {
310 for (let i = 0; i < e.changedTouches.length; i++) {
311 const touch = e.changedTouches[i];
312 if (touch.identifier === this.currentTouchID) {
313 this.currentTouchID = null;
314 window.removeEventListener("touchmove", this.onTouchMoveBound);
315 window.removeEventListener("touchend", this.onTouchEndBound);
316 window.removeEventListener("touchcancel", this.onTouchEndBound);
317 this.onEnd(e);
318 }
319 }
320 if (this.experimentalInertia) {
321 new Inertia(this.lastTouchTimestamp, this.lastTouchMoveMomentumX, this.lastTouchMoveMomentumY, this.onMoveBound);
322 }
323 }
324 onStart(e) {
325 e.preventDefault();
326 }
327 onMove(movementX, movementY) {
328 var _a;
329 this.tempSpherical.setFromVector3(this.camera.position);
330 this.tempSpherical.theta += -2 * movementX;
331 this.tempSpherical.phi += -2 * movementY;
332 if (this.experimentalLatitudeLimits) {
333 this.tempSpherical.phi = Math.max(this.tempSpherical.phi, Math.PI * 0.3);
334 this.tempSpherical.phi = Math.min(this.tempSpherical.phi, Math.PI * 0.7);
335 } else {
336 this.tempSpherical.phi = Math.max(this.tempSpherical.phi, EPSILON);
337 this.tempSpherical.phi = Math.min(this.tempSpherical.phi, Math.PI - EPSILON);
338 }
339 this.camera.position.setFromSpherical(this.tempSpherical);
340 this.camera.lookAt(new Vector3(0, 0, 0));
341 this.experimentalHasBeenMoved = true;
342 this.scheduleRender();
343 (_a = this.mirrorControls) == null ? void 0 : _a.updateMirroredCamera(this.camera);
344 }
345 onEnd(e) {
346 e.preventDefault();
347 }
348 setMirror(m) {
349 this.mirrorControls = m;
350 }
351 updateMirroredCamera(c) {
352 this.camera.position.copy(c.position);
353 this.camera.position.multiplyScalar(-1);
354 this.camera.lookAt(new Vector3(0, 0, 0));
355 this.scheduleRender();
356 }
357};
358
359// src/cubing/vendor/three/examples/jsm/libs/stats.module.wrapped.js
360var Stats = function() {
361 var mode = 0;
362 var container = document.createElement("div");
363 container.style.cssText = "position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";
364 container.addEventListener("click", function(event) {
365 event.preventDefault();
366 showPanel(++mode % container.children.length);
367 }, false);
368 function addPanel(panel) {
369 container.appendChild(panel.dom);
370 return panel;
371 }
372 function showPanel(id) {
373 for (var i = 0; i < container.children.length; i++) {
374 container.children[i].style.display = i === id ? "block" : "none";
375 }
376 mode = id;
377 }
378 var beginTime = (performance || Date).now(), prevTime = beginTime, frames = 0;
379 var fpsPanel = addPanel(new Stats.Panel("FPS", "#0ff", "#002"));
380 var msPanel = addPanel(new Stats.Panel("MS", "#0f0", "#020"));
381 if (self.performance && self.performance.memory) {
382 var memPanel = addPanel(new Stats.Panel("MB", "#f08", "#201"));
383 }
384 showPanel(0);
385 return {
386 REVISION: 16,
387 dom: container,
388 addPanel,
389 showPanel,
390 begin: function() {
391 beginTime = (performance || Date).now();
392 },
393 end: function() {
394 frames++;
395 var time = (performance || Date).now();
396 msPanel.update(time - beginTime, 200);
397 if (time >= prevTime + 1e3) {
398 fpsPanel.update(frames * 1e3 / (time - prevTime), 100);
399 prevTime = time;
400 frames = 0;
401 if (memPanel) {
402 var memory = performance.memory;
403 memPanel.update(memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576);
404 }
405 }
406 return time;
407 },
408 update: function() {
409 beginTime = this.end();
410 },
411 domElement: container,
412 setMode: showPanel
413 };
414};
415Stats.Panel = function(name, fg, bg) {
416 var min = Infinity, max = 0, round = Math.round;
417 var PR = round(window.devicePixelRatio || 1);
418 var WIDTH = 80 * PR, HEIGHT = 48 * PR, TEXT_X = 3 * PR, TEXT_Y = 2 * PR, GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR, GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR;
419 var canvas = document.createElement("canvas");
420 canvas.width = WIDTH;
421 canvas.height = HEIGHT;
422 canvas.style.cssText = "width:80px;height:48px";
423 var context = canvas.getContext("2d");
424 context.font = "bold " + 9 * PR + "px Helvetica,Arial,sans-serif";
425 context.textBaseline = "top";
426 context.fillStyle = bg;
427 context.fillRect(0, 0, WIDTH, HEIGHT);
428 context.fillStyle = fg;
429 context.fillText(name, TEXT_X, TEXT_Y);
430 context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT);
431 context.fillStyle = bg;
432 context.globalAlpha = 0.9;
433 context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT);
434 return {
435 dom: canvas,
436 update: function(value, maxValue) {
437 min = Math.min(min, value);
438 max = Math.max(max, value);
439 context.fillStyle = bg;
440 context.globalAlpha = 1;
441 context.fillRect(0, 0, WIDTH, GRAPH_Y);
442 context.fillStyle = fg;
443 context.fillText(round(value) + " " + name + " (" + round(min) + "-" + round(max) + ")", TEXT_X, TEXT_Y);
444 context.drawImage(canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT);
445 context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT);
446 context.fillStyle = bg;
447 context.globalAlpha = 0.9;
448 context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round((1 - value / maxValue) * GRAPH_HEIGHT));
449 }
450 };
451};
452
453// src/cubing/twisty/dom/viewers/Twisty3DCanvas.ts
454var SHOW_STATS = false;
455function experimentalShowRenderStats(show) {
456 SHOW_STATS = show;
457}
458var shareAllNewRenderers = false;
459function experimentalSetShareAllNewRenderers(share) {
460 shareAllNewRenderers = share;
461}
462var sharedRenderer = null;
463function newRenderer() {
464 return new WebGLRenderer({
465 antialias: true,
466 alpha: true
467 });
468}
469function newSharedRenderer() {
470 return sharedRenderer != null ? sharedRenderer : sharedRenderer = newRenderer();
471}
472var _invisible, _onRenderFinish, _resize, resize_fn;
473var Twisty3DCanvas = class extends ManagedCustomElement {
474 constructor(scene, options = {}) {
475 super();
476 _resize.add(this);
477 this.legacyExperimentalShift = 0;
478 this.scheduler = new RenderScheduler(this.render.bind(this));
479 this.resizePending = false;
480 this.stats = null;
481 _invisible.set(this, false);
482 _onRenderFinish.set(this, null);
483 var _a, _b;
484 this.addCSS(twisty3DCanvasCSS);
485 this.scene = scene;
486 (_a = this.scene) == null ? void 0 : _a.addRenderTarget(this);
487 if (SHOW_STATS) {
488 this.stats = Stats();
489 this.stats.dom.style.position = "absolute";
490 this.addElement(this.stats.dom);
491 }
492 this.rendererIsShared = shareAllNewRenderers;
493 this.renderer = this.rendererIsShared ? newSharedRenderer() : newRenderer();
494 this.canvas = this.rendererIsShared ? document.createElement("canvas") : this.renderer.domElement;
495 this.canvas2DContext = this.canvas.getContext("2d");
496 this.addElement(this.canvas);
497 this.camera = new PerspectiveCamera(20, 1, 0.1, 20);
498 this.camera.position.copy((_b = options.experimentalCameraPosition) != null ? _b : new Vector32(2, 4, 4));
499 if (options.negateCameraPosition) {
500 this.camera.position.multiplyScalar(-1);
501 }
502 this.camera.lookAt(new Vector32(0, 0, 0));
503 this.orbitControls = new TwistyOrbitControls(this.camera, this.canvas, this.scheduleRender.bind(this));
504 const observer = new ResizeObserver(this.onResize.bind(this));
505 observer.observe(this.contentWrapper);
506 }
507 setMirror(partner) {
508 this.orbitControls.setMirror(partner.orbitControls);
509 partner.orbitControls.setMirror(this.orbitControls);
510 }
511 experimentalSetLatitudeLimits(limits) {
512 this.orbitControls.experimentalLatitudeLimits = limits;
513 }
514 connectedCallback() {
515 __privateMethod(this, _resize, resize_fn).call(this);
516 this.render();
517 }
518 scheduleRender() {
519 this.scheduler.requestAnimFrame();
520 }
521 makeInvisibleUntilRender() {
522 this.contentWrapper.classList.add("invisible");
523 __privateSet(this, _invisible, true);
524 }
525 experimentalSetOnRenderFinish(f) {
526 __privateSet(this, _onRenderFinish, f);
527 }
528 render() {
529 var _a, _b;
530 (_a = this.stats) == null ? void 0 : _a.begin();
531 this.scheduler.cancelAnimFrame();
532 if (this.resizePending) {
533 __privateMethod(this, _resize, resize_fn).call(this);
534 }
535 if (this.rendererIsShared) {
536 this.renderer.setSize(this.canvas.width, this.canvas.height, false);
537 this.canvas2DContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
538 }
539 if (this.scene) {
540 this.renderer.render(this.scene, this.camera);
541 }
542 if (this.rendererIsShared) {
543 this.canvas2DContext.drawImage(this.renderer.domElement, 0, 0);
544 }
545 if (__privateGet(this, _invisible)) {
546 this.contentWrapper.classList.remove("invisible");
547 }
548 (_b = this.stats) == null ? void 0 : _b.end();
549 if (__privateGet(this, _onRenderFinish)) {
550 __privateGet(this, _onRenderFinish).call(this);
551 }
552 }
553 onResize() {
554 this.resizePending = true;
555 this.scheduleRender();
556 }
557 renderToDataURL(options = {}) {
558 __privateMethod(this, _resize, resize_fn).call(this, options.minWidth, options.minHeight);
559 this.render();
560 let url;
561 if (!options.squareCrop || this.canvas.width === this.canvas.height) {
562 url = this.canvas.toDataURL();
563 } else {
564 const tempCanvas = document.createElement("canvas");
565 const squareSize = Math.min(this.canvas.width, this.canvas.height);
566 tempCanvas.width = squareSize;
567 tempCanvas.height = squareSize;
568 const tempCtx = tempCanvas.getContext("2d");
569 tempCtx.drawImage(this.canvas, -(this.canvas.width - squareSize) / 2, -(this.canvas.height - squareSize) / 2);
570 url = tempCanvas.toDataURL();
571 }
572 __privateMethod(this, _resize, resize_fn).call(this);
573 return url;
574 }
575};
576_invisible = new WeakMap();
577_onRenderFinish = new WeakMap();
578_resize = new WeakSet();
579resize_fn = function(minWidth = 0, minHeight = 0) {
580 this.resizePending = false;
581 const w = Math.max(this.contentWrapper.clientWidth, minWidth);
582 const h = Math.max(this.contentWrapper.clientHeight, minHeight);
583 let off = 0;
584 if (this.legacyExperimentalShift > 0) {
585 off = Math.max(0, Math.floor((w - h) * 0.5));
586 } else if (this.legacyExperimentalShift < 0) {
587 off = -Math.max(0, Math.floor((w - h) * 0.5));
588 }
589 let yoff = 0;
590 let excess = 0;
591 if (h > w) {
592 excess = h - w;
593 yoff = -Math.floor(0.5 * excess);
594 }
595 this.camera.aspect = w / h;
596 this.camera.setViewOffset(w, h - excess, off, yoff, w, h);
597 this.camera.updateProjectionMatrix();
598 if (this.rendererIsShared) {
599 this.canvas.width = w * pixelRatio();
600 this.canvas.height = h * pixelRatio();
601 this.canvas.style.width = w.toString();
602 this.canvas.style.height = w.toString();
603 } else {
604 this.renderer.setPixelRatio(pixelRatio());
605 this.renderer.setSize(w, h, true);
606 }
607 this.scheduleRender();
608};
609customElementsShim.define("twisty-3d-canvas", Twisty3DCanvas);
610
611// src/cubing/twisty/dom/TwistyPlayer.ts
612import {Vector3 as Vector37} from "three";
613
614// src/cubing/notation/CountMoves.ts
615var CountMoves = class extends TraversalUp {
616 constructor(metric) {
617 super();
618 this.metric = metric;
619 }
620 traverseAlg(alg) {
621 let r2 = 0;
622 for (const unit of alg.units()) {
623 r2 += this.traverseUnit(unit);
624 }
625 return r2;
626 }
627 traverseGrouping(grouping) {
628 const alg = grouping.alg;
629 return this.traverseAlg(alg) * Math.abs(grouping.amount);
630 }
631 traverseMove(move) {
632 return this.metric(move);
633 }
634 traverseCommutator(commutator) {
635 return 2 * (this.traverseAlg(commutator.A) + this.traverseAlg(commutator.B));
636 }
637 traverseConjugate(conjugate) {
638 return 2 * this.traverseAlg(conjugate.A) + this.traverseAlg(conjugate.B);
639 }
640 traversePause(_pause) {
641 return 0;
642 }
643 traverseNewline(_newLine) {
644 return 0;
645 }
646 traverseLineComment(_comment) {
647 return 0;
648 }
649};
650function isCharUppercase(c) {
651 return "A" <= c && c <= "Z";
652}
653function baseMetric(move) {
654 const fam = move.family;
655 if (isCharUppercase(fam[0]) && fam[fam.length - 1] === "v" || fam === "x" || fam === "y" || fam === "z" || fam === "T") {
656 return 0;
657 } else {
658 return 1;
659 }
660}
661var countMovesInstance = new CountMoves(baseMetric);
662var countMoves = countMovesInstance.traverseAlg.bind(countMovesInstance);
663
664// src/cubing/notation/CountAnimatedMoves.ts
665var CountAnimatedMoves = class extends TraversalUp {
666 traverseAlg(alg) {
667 let total = 0;
668 for (const part of alg.units()) {
669 total += this.traverseUnit(part);
670 }
671 return total;
672 }
673 traverseGrouping(grouping) {
674 return this.traverseAlg(grouping.alg) * Math.abs(grouping.amount);
675 }
676 traverseMove(_move) {
677 return 1;
678 }
679 traverseCommutator(commutator) {
680 return 2 * (this.traverseAlg(commutator.A) + this.traverseAlg(commutator.B));
681 }
682 traverseConjugate(conjugate) {
683 return 2 * this.traverseAlg(conjugate.A) + this.traverseAlg(conjugate.B);
684 }
685 traversePause(_pause) {
686 return 1;
687 }
688 traverseNewline(_newline) {
689 return 0;
690 }
691 traverseLineComment(_comment) {
692 return 0;
693 }
694};
695var countAnimatedMovesInstance = new CountAnimatedMoves();
696var countAnimatedMoves = countAnimatedMovesInstance.traverseAlg.bind(countAnimatedMovesInstance);
697
698// src/cubing/twisty/3D/puzzles/Cube3D.ts
699import {
700 BackSide,
701 BoxGeometry,
702 BufferAttribute,
703 BufferGeometry,
704 DoubleSide,
705 Euler,
706 Group,
707 Matrix4,
708 Mesh,
709 MeshBasicMaterial,
710 Object3D,
711 Quaternion,
712 TextureLoader,
713 Vector2,
714 Vector3 as Vector35
715} from "three";
716
717// src/cubing/twisty/animation/easing.ts
718function smootherStep(x) {
719 return x * x * x * (10 - x * (15 - 6 * x));
720}
721
722// src/cubing/twisty/dom/TwistyPlayerConfig.ts
723import {Vector3 as Vector34} from "three";
724
725// src/cubing/twisty/dom/element/ElementConfig.ts
726import {Vector3 as Vector33} from "three";
727var AlgAttribute = class {
728 constructor(initialValue) {
729 if (initialValue) {
730 if (typeof initialValue === "string") {
731 this.setString(initialValue);
732 } else {
733 this.setValue(initialValue);
734 }
735 } else {
736 this.setValue(this.defaultValue());
737 }
738 }
739 setString(str) {
740 if (this.string === str) {
741 return false;
742 }
743 this.string = str;
744 this.value = this.toValue(str);
745 return true;
746 }
747 setValue(val) {
748 const str = this.toString(val);
749 if (this.string === str) {
750 return false;
751 }
752 this.string = str;
753 this.value = val;
754 return true;
755 }
756 defaultValue() {
757 return new Alg([]);
758 }
759 toValue(s) {
760 return Alg.fromString(s);
761 }
762 toString(val) {
763 return val.toString();
764 }
765};
766var StringEnumAttribute = class {
767 constructor(enumVal, initialValue) {
768 this.enumVal = enumVal;
769 this.setString(initialValue != null ? initialValue : this.defaultValue());
770 }
771 setString(str) {
772 if (this.string === str) {
773 return false;
774 }
775 if (!(str in this.enumVal)) {
776 throw new Error(`Invalid string for attribute!: ${str}`);
777 }
778 this.string = str;
779 this.value = this.toValue(str);
780 return true;
781 }
782 setValue(val) {
783 return this.setString(val);
784 }
785 defaultValue() {
786 return Object.keys(this.enumVal)[0];
787 }
788 toValue(s) {
789 return s;
790 }
791};
792var _defaultValue;
793var Vector3Attribute = class {
794 constructor(defaultValue, initialValue) {
795 _defaultValue.set(this, void 0);
796 __privateSet(this, _defaultValue, defaultValue);
797 this.setValue(initialValue != null ? initialValue : this.defaultValue());
798 }
799 setString(str) {
800 return this.setValue(str === "" ? null : this.toValue(str));
801 }
802 setValue(val) {
803 const str = this.toString(val);
804 if (this.string === str) {
805 return false;
806 }
807 this.string = str;
808 this.value = val;
809 return true;
810 }
811 defaultValue() {
812 return __privateGet(this, _defaultValue);
813 }
814 toValue(s) {
815 if (!s.startsWith("[")) {
816 throw new Error("TODO");
817 }
818 if (!s.endsWith("]")) {
819 throw new Error("TODO");
820 }
821 const coords = s.slice(1, s.length - 1).split(",");
822 if (coords.length !== 3) {
823 throw new Error("TODO");
824 }
825 const [x, y, z] = coords.map((c) => parseInt(c, 10));
826 return new Vector33(x, y, z);
827 }
828 toString(v) {
829 return v ? `[${v.x}, ${v.y}, ${v.z}]` : "";
830 }
831};
832_defaultValue = new WeakMap();
833
834// src/cubing/twisty/dom/element/ClassListManager.ts
835var _currentClassName;
836var ClassListManager = class {
837 constructor(elem, prefix, validSuffixes) {
838 this.elem = elem;
839 this.prefix = prefix;
840 this.validSuffixes = validSuffixes;
841 _currentClassName.set(this, null);
842 }
843 clearValue() {
844 if (__privateGet(this, _currentClassName)) {
845 this.elem.contentWrapper.classList.remove(__privateGet(this, _currentClassName));
846 }
847 __privateSet(this, _currentClassName, null);
848 }
849 setValue(suffix) {
850 if (!this.validSuffixes.includes(suffix)) {
851 throw new Error(`Invalid suffix: ${suffix}`);
852 }
853 const newClassName = `${this.prefix}${suffix}`;
854 const changed = __privateGet(this, _currentClassName) !== newClassName;
855 if (changed) {
856 this.clearValue();
857 this.elem.contentWrapper.classList.add(newClassName);
858 __privateSet(this, _currentClassName, newClassName);
859 }
860 return changed;
861 }
862};
863_currentClassName = new WeakMap();
864
865// src/cubing/twisty/dom/viewers/TwistyViewerWrapper.css_.ts
866var twistyViewerWrapperCSS = new CSSSource(`
867:host {
868 width: 384px;
869 height: 256px;
870 display: grid;
871}
872
873.wrapper {
874 width: 100%;
875 height: 100%;
876 display: grid;
877 overflow: hidden;
878}
879
880.wrapper > * {
881 width: 100%;
882 height: 100%;
883 overflow: hidden;
884}
885
886.wrapper.back-view-side-by-side {
887 grid-template-columns: 1fr 1fr;
888}
889
890.wrapper.back-view-top-right {
891 grid-template-columns: 3fr 1fr;
892 grid-template-rows: 1fr 3fr;
893}
894
895.wrapper.back-view-top-right > :nth-child(1) {
896 grid-row: 1 / 3;
897 grid-column: 1 / 3;
898}
899
900.wrapper.back-view-top-right > :nth-child(2) {
901 grid-row: 1 / 2;
902 grid-column: 2 / 3;
903}
904`);
905
906// src/cubing/twisty/dom/viewers/TwistyViewerWrapper.ts
907var backViewLayouts = {
908 none: true,
909 "side-by-side": true,
910 "top-right": true
911};
912var _backViewClassListManager;
913var TwistyViewerWrapper = class extends ManagedCustomElement {
914 constructor(config = {}) {
915 super();
916 _backViewClassListManager.set(this, new ClassListManager(this, "back-view-", ["none", "side-by-side", "top-right"]));
917 this.addCSS(twistyViewerWrapperCSS);
918 if (config.backView && config.backView in backViewLayouts) {
919 __privateGet(this, _backViewClassListManager).setValue(config.backView);
920 }
921 }
922 setBackView(backView) {
923 return __privateGet(this, _backViewClassListManager).setValue(backView);
924 }
925 clear() {
926 this.contentWrapper.innerHTML = "";
927 }
928};
929_backViewClassListManager = new WeakMap();
930customElementsShim.define("twisty-viewer-wrapper", TwistyViewerWrapper);
931
932// src/cubing/twisty/dom/TwistyPlayerConfig.ts
933var DEFAULT_CAMERA_Z = 5;
934var DEFAULT_CAMERA_Y = DEFAULT_CAMERA_Z * (2 / (1 + Math.sqrt(5)));
935var centeredCameraPosition = new Vector34(0, DEFAULT_CAMERA_Y, DEFAULT_CAMERA_Z);
936var cubeCameraPosition = new Vector34(3, 4, 5);
937var setupToLocations = {
938 start: true,
939 end: true
940};
941var visualizationFormats = {
942 "3D": true,
943 "2D": true,
944 "experimental-2D-LL": true,
945 PG3D: true
946};
947var backgroundThemes = {
948 checkered: true,
949 none: true
950};
951var hintFaceletStyles = {
952 floating: true,
953 none: true
954};
955var experimentalStickerings = {
956 full: true,
957 "centers-only": true,
958 PLL: true,
959 CLS: true,
960 OLL: true,
961 COLL: true,
962 OCLL: true,
963 CLL: true,
964 ELL: true,
965 ELS: true,
966 LL: true,
967 F2L: true,
968 ZBLL: true,
969 ZBLS: true,
970 WVLS: true,
971 VLS: true,
972 LS: true,
973 EO: true,
974 CMLL: true,
975 L6E: true,
976 L6EO: true,
977 Daisy: true,
978 Cross: true,
979 "2x2x2": true,
980 "2x2x3": true,
981 "Void Cube": true,
982 invisible: true,
983 picture: true,
984 "experimental-centers-U": true,
985 "experimental-centers-U-D": true,
986 "experimental-centers-U-L-D": true,
987 "experimental-centers-U-L-B-D": true,
988 "experimental-centers": true,
989 "experimental-fto-fc": true,
990 "experimental-fto-f2t": true,
991 "experimental-fto-sc": true,
992 "experimental-fto-l2c": true,
993 "experimental-fto-lbt": true
994};
995var controlsLocations = {
996 "bottom-row": true,
997 none: true
998};
999var puzzleIDs = {
1000 "3x3x3": true,
1001 custom: true,
1002 "2x2x2": true,
1003 "4x4x4": true,
1004 "5x5x5": true,
1005 "6x6x6": true,
1006 "7x7x7": true,
1007 "40x40x40": true,
1008 megaminx: true,
1009 pyraminx: true,
1010 square1: true,
1011 clock: true,
1012 skewb: true,
1013 fto: true,
1014 gigaminx: true
1015};
1016var viewerLinkPages = {
1017 twizzle: true,
1018 none: true
1019};
1020var twistyPlayerAttributeMap = {
1021 alg: "alg",
1022 "experimental-setup-alg": "experimentalSetupAlg",
1023 "experimental-setup-anchor": "experimentalSetupAnchor",
1024 puzzle: "puzzle",
1025 visualization: "visualization",
1026 "hint-facelets": "hintFacelets",
1027 "experimental-stickering": "experimentalStickering",
1028 background: "background",
1029 "control-panel": "controlPanel",
1030 "back-view": "backView",
1031 "experimental-camera-position": "experimentalCameraPosition",
1032 "viewer-link": "viewerLink"
1033};
1034var TwistyPlayerConfig = class {
1035 constructor(twistyPlayer, initialValues) {
1036 this.twistyPlayer = twistyPlayer;
1037 this.attributes = {
1038 alg: new AlgAttribute(initialValues.alg),
1039 "experimental-setup-alg": new AlgAttribute(initialValues.experimentalSetupAlg),
1040 "experimental-setup-anchor": new StringEnumAttribute(setupToLocations, initialValues.experimentalSetupAnchor),
1041 puzzle: new StringEnumAttribute(puzzleIDs, initialValues.puzzle),
1042 visualization: new StringEnumAttribute(visualizationFormats, initialValues.visualization),
1043 "hint-facelets": new StringEnumAttribute(hintFaceletStyles, initialValues.hintFacelets),
1044 "experimental-stickering": new StringEnumAttribute(experimentalStickerings, initialValues.experimentalStickering),
1045 background: new StringEnumAttribute(backgroundThemes, initialValues.background),
1046 "control-panel": new StringEnumAttribute(controlsLocations, initialValues.controlPanel),
1047 "back-view": new StringEnumAttribute(backViewLayouts, initialValues["backView"]),
1048 "experimental-camera-position": new Vector3Attribute(null, initialValues["experimentalCameraPosition"]),
1049 "viewer-link": new StringEnumAttribute(viewerLinkPages, initialValues.viewerLink)
1050 };
1051 }
1052 static get observedAttributes() {
1053 return Object.keys(twistyPlayerAttributeMap);
1054 }
1055 attributeChangedCallback(attributeName, oldValue, newValue) {
1056 const managedAttribute = this.attributes[attributeName];
1057 if (managedAttribute) {
1058 if (oldValue !== null && managedAttribute.string !== oldValue) {
1059 console.warn("Attribute out of sync!", attributeName, managedAttribute.string, oldValue);
1060 }
1061 managedAttribute.setString(newValue);
1062 const propertyName = twistyPlayerAttributeMap[attributeName];
1063 this.twistyPlayer[propertyName] = managedAttribute.value;
1064 }
1065 }
1066};
1067
1068// src/cubing/twisty/3D/TAU.ts
1069var TAU = Math.PI * 2;
1070
1071// src/cubing/twisty/3D/puzzles/Cube3D.ts
1072var svgLoader = new TextureLoader();
1073var ignoredMaterial = new MeshBasicMaterial({
1074 color: 4473924,
1075 side: DoubleSide
1076});
1077var ignoredMaterialHint = new MeshBasicMaterial({
1078 color: 13421772,
1079 side: BackSide
1080});
1081var invisibleMaterial = new MeshBasicMaterial({
1082 visible: false
1083});
1084var orientedMaterial = new MeshBasicMaterial({
1085 color: 16746751
1086});
1087var orientedMaterialHint = new MeshBasicMaterial({
1088 color: 16746751,
1089 side: BackSide
1090});
1091var AxisInfo = class {
1092 constructor(vector, fromZ, color, dimColor) {
1093 this.vector = vector;
1094 this.fromZ = fromZ;
1095 this.color = color;
1096 this.dimColor = dimColor;
1097 this.stickerMaterial = {
1098 regular: new MeshBasicMaterial({
1099 color,
1100 side: DoubleSide
1101 }),
1102 dim: new MeshBasicMaterial({
1103 color: dimColor,
1104 side: DoubleSide
1105 }),
1106 oriented: orientedMaterial,
1107 ignored: ignoredMaterial,
1108 invisible: invisibleMaterial
1109 };
1110 this.hintStickerMaterial = {
1111 regular: new MeshBasicMaterial({
1112 color,
1113 side: BackSide
1114 }),
1115 dim: new MeshBasicMaterial({
1116 color: dimColor,
1117 side: BackSide,
1118 transparent: true,
1119 opacity: 0.75
1120 }),
1121 oriented: orientedMaterialHint,
1122 ignored: ignoredMaterialHint,
1123 invisible: invisibleMaterial
1124 };
1125 }
1126};
1127var axesInfo = [
1128 new AxisInfo(new Vector35(0, 1, 0), new Euler(-TAU / 4, 0, 0), 16777215, 14540253),
1129 new AxisInfo(new Vector35(-1, 0, 0), new Euler(0, -TAU / 4, 0), 16746496, 8930304),
1130 new AxisInfo(new Vector35(0, 0, 1), new Euler(0, 0, 0), 65280, 34816),
1131 new AxisInfo(new Vector35(1, 0, 0), new Euler(0, TAU / 4, 0), 16711680, 6684672),
1132 new AxisInfo(new Vector35(0, 0, -1), new Euler(0, TAU / 2, 0), 255, 136),
1133 new AxisInfo(new Vector35(0, -1, 0), new Euler(TAU / 4, 0, 0), 16776960, 8947712)
1134];
1135var face = {
1136 U: 0,
1137 L: 1,
1138 F: 2,
1139 R: 3,
1140 B: 4,
1141 D: 5
1142};
1143var familyToAxis = {
1144 U: face.U,
1145 u: face.U,
1146 Uw: face.U,
1147 y: face.U,
1148 L: face.L,
1149 l: face.L,
1150 Lw: face.L,
1151 M: face.L,
1152 F: face.F,
1153 f: face.F,
1154 Fw: face.F,
1155 S: face.F,
1156 z: face.F,
1157 R: face.R,
1158 r: face.R,
1159 Rw: face.R,
1160 x: face.R,
1161 B: face.B,
1162 b: face.B,
1163 Bw: face.B,
1164 D: face.D,
1165 d: face.D,
1166 Dw: face.D,
1167 E: face.D
1168};
1169var cubieDimensions = {
1170 stickerWidth: 0.85,
1171 stickerElevation: 0.503,
1172 foundationWidth: 1,
1173 hintStickerElevation: 1.45
1174};
1175var EXPERIMENTAL_PICTURE_CUBE_HINT_ELEVATION = 2;
1176var cube3DOptionsDefaults = {
1177 showMainStickers: true,
1178 hintFacelets: "floating",
1179 showFoundation: true,
1180 experimentalStickering: "full"
1181};
1182var blackMesh = new MeshBasicMaterial({
1183 color: 0,
1184 opacity: 1,
1185 transparent: true
1186});
1187var blackTranslucentMesh = new MeshBasicMaterial({
1188 color: 0,
1189 opacity: 0.3,
1190 transparent: true
1191});
1192var CubieDef = class {
1193 constructor(orbit, stickerFaceNames, q) {
1194 this.orbit = orbit;
1195 const individualStickerFaceNames = typeof stickerFaceNames === "string" ? stickerFaceNames.split("") : stickerFaceNames;
1196 this.stickerFaces = individualStickerFaceNames.map((s) => face[s]);
1197 this.matrix = new Matrix4();
1198 this.matrix.setPosition(firstPiecePosition[orbit]);
1199 this.matrix.premultiply(new Matrix4().makeRotationFromQuaternion(q));
1200 }
1201};
1202function t(v, t4) {
1203 return new Quaternion().setFromAxisAngle(v, TAU * t4 / 4);
1204}
1205var r = {
1206 O: new Vector35(0, 0, 0),
1207 U: new Vector35(0, -1, 0),
1208 L: new Vector35(1, 0, 0),
1209 F: new Vector35(0, 0, -1),
1210 R: new Vector35(-1, 0, 0),
1211 B: new Vector35(0, 0, 1),
1212 D: new Vector35(0, 1, 0)
1213};
1214var firstPiecePosition = {
1215 EDGES: new Vector35(0, 1, 1),
1216 CORNERS: new Vector35(1, 1, 1),
1217 CENTERS: new Vector35(0, 1, 0)
1218};
1219var orientationRotation = {
1220 EDGES: [0, 1].map((i) => new Matrix4().makeRotationAxis(firstPiecePosition.EDGES.clone().normalize(), -i * TAU / 2)),
1221 CORNERS: [0, 1, 2].map((i) => new Matrix4().makeRotationAxis(firstPiecePosition.CORNERS.clone().normalize(), -i * TAU / 3)),
1222 CENTERS: [0, 1, 2, 3].map((i) => new Matrix4().makeRotationAxis(firstPiecePosition.CENTERS.clone().normalize(), -i * TAU / 4))
1223};
1224var cubieStickerOrder = [face.U, face.F, face.R];
1225var pieceDefs = {
1226 EDGES: [
1227 new CubieDef("EDGES", "UF", t(r.O, 0)),
1228 new CubieDef("EDGES", "UR", t(r.U, 3)),
1229 new CubieDef("EDGES", "UB", t(r.U, 2)),
1230 new CubieDef("EDGES", "UL", t(r.U, 1)),
1231 new CubieDef("EDGES", "DF", t(r.F, 2)),
1232 new CubieDef("EDGES", "DR", t(r.F, 2).premultiply(t(r.D, 1))),
1233 new CubieDef("EDGES", "DB", t(r.F, 2).premultiply(t(r.D, 2))),
1234 new CubieDef("EDGES", "DL", t(r.F, 2).premultiply(t(r.D, 3))),
1235 new CubieDef("EDGES", "FR", t(r.U, 3).premultiply(t(r.R, 3))),
1236 new CubieDef("EDGES", "FL", t(r.U, 1).premultiply(t(r.R, 3))),
1237 new CubieDef("EDGES", "BR", t(r.U, 3).premultiply(t(r.R, 1))),
1238 new CubieDef("EDGES", "BL", t(r.U, 1).premultiply(t(r.R, 1)))
1239 ],
1240 CORNERS: [
1241 new CubieDef("CORNERS", "UFR", t(r.O, 0)),
1242 new CubieDef("CORNERS", "URB", t(r.U, 3)),
1243 new CubieDef("CORNERS", "UBL", t(r.U, 2)),
1244 new CubieDef("CORNERS", "ULF", t(r.U, 1)),
1245 new CubieDef("CORNERS", "DRF", t(r.F, 2).premultiply(t(r.D, 1))),
1246 new CubieDef("CORNERS", "DFL", t(r.F, 2).premultiply(t(r.D, 0))),
1247 new CubieDef("CORNERS", "DLB", t(r.F, 2).premultiply(t(r.D, 3))),
1248 new CubieDef("CORNERS", "DBR", t(r.F, 2).premultiply(t(r.D, 2)))
1249 ],
1250 CENTERS: [
1251 new CubieDef("CENTERS", "U", t(r.O, 0)),
1252 new CubieDef("CENTERS", "L", t(r.R, 3).premultiply(t(r.U, 1))),
1253 new CubieDef("CENTERS", "F", t(r.R, 3)),
1254 new CubieDef("CENTERS", "R", t(r.R, 3).premultiply(t(r.D, 1))),
1255 new CubieDef("CENTERS", "B", t(r.R, 3).premultiply(t(r.D, 2))),
1256 new CubieDef("CENTERS", "D", t(r.R, 2))
1257 ]
1258};
1259var CUBE_SCALE = 1 / 3;
1260var pictureStickerCoords = {
1261 EDGES: [
1262 [
1263 [0, 4, 6],
1264 [0, 4, 5]
1265 ],
1266 [
1267 [3, 5, 7],
1268 [0, 7, 5]
1269 ],
1270 [
1271 [2, 4, 8],
1272 [0, 10, 5]
1273 ],
1274 [
1275 [1, 3, 7],
1276 [0, 1, 5]
1277 ],
1278 [
1279 [2, 4, 2],
1280 [2, 4, 3]
1281 ],
1282 [
1283 [3, 5, 1],
1284 [2, 7, 3]
1285 ],
1286 [
1287 [2, 4, 2],
1288 [2, 10, 3]
1289 ],
1290 [
1291 [1, 3, 1],
1292 [2, 1, 3]
1293 ],
1294 [
1295 [3, 5, 4],
1296 [3, 6, 4]
1297 ],
1298 [
1299 [1, 3, 4],
1300 [1, 2, 4]
1301 ],
1302 [
1303 [1, 9, 4],
1304 [1, 8, 4]
1305 ],
1306 [
1307 [3, 11, 4],
1308 [3, 0, 4]
1309 ]
1310 ],
1311 CORNERS: [
1312 [
1313 [0, 5, 6],
1314 [0, 5, 5],
1315 [0, 6, 5]
1316 ],
1317 [
1318 [3, 5, 8],
1319 [0, 8, 5],
1320 [0, 9, 5]
1321 ],
1322 [
1323 [2, 3, 8],
1324 [0, 11, 5],
1325 [0, 0, 5]
1326 ],
1327 [
1328 [1, 3, 6],
1329 [0, 2, 5],
1330 [0, 3, 5]
1331 ],
1332 [
1333 [3, 5, 2],
1334 [2, 6, 3],
1335 [2, 5, 3]
1336 ],
1337 [
1338 [2, 3, 2],
1339 [2, 3, 3],
1340 [2, 2, 3]
1341 ],
1342 [
1343 [1, 3, 0],
1344 [2, 0, 3],
1345 [2, 11, 3]
1346 ],
1347 [
1348 [0, 5, 0],
1349 [2, 9, 3],
1350 [2, 8, 3]
1351 ]
1352 ],
1353 CENTERS: [
1354 [[0, 4, 7]],
1355 [[0, 1, 4]],
1356 [[0, 4, 4]],
1357 [[0, 7, 4]],
1358 [[0, 10, 4]],
1359 [[0, 4, 1]]
1360 ]
1361};
1362var sharedCubieFoundationGeometryCache = null;
1363function sharedCubieFoundationGeometry() {
1364 return sharedCubieFoundationGeometryCache != null ? sharedCubieFoundationGeometryCache : sharedCubieFoundationGeometryCache = new BoxGeometry(cubieDimensions.foundationWidth, cubieDimensions.foundationWidth, cubieDimensions.foundationWidth);
1365}
1366function newStickerGeometry() {
1367 const r2 = new BufferGeometry();
1368 const half = 0.5 * cubieDimensions.stickerWidth;
1369 r2.setAttribute("position", new BufferAttribute(new Float32Array([
1370 half,
1371 half,
1372 0,
1373 -half,
1374 half,
1375 0,
1376 half,
1377 -half,
1378 0,
1379 -half,
1380 half,
1381 0,
1382 -half,
1383 -half,
1384 0,
1385 half,
1386 -half,
1387 0
1388 ]), 3));
1389 r2.setAttribute("uv", new BufferAttribute(new Float32Array([
1390 1,
1391 1,
1392 0,
1393 1,
1394 1,
1395 0,
1396 0,
1397 1,
1398 0,
1399 0,
1400 1,
1401 0,
1402 0,
1403 1,
1404 0,
1405 0,
1406 1,
1407 1,
1408 0,
1409 0,
1410 1,
1411 0,
1412 1,
1413 1
1414 ]), 2));
1415 return r2;
1416}
1417var sharedStickerGeometryCache = null;
1418function sharedStickerGeometry() {
1419 return sharedStickerGeometryCache != null ? sharedStickerGeometryCache : sharedStickerGeometryCache = newStickerGeometry();
1420}
1421var Cube3D = class extends Object3D {
1422 constructor(def, cursor, scheduleRenderCallback, options = {}) {
1423 super();
1424 this.def = def;
1425 this.scheduleRenderCallback = scheduleRenderCallback;
1426 this.pieces = {};
1427 this.experimentalHintStickerMeshes = [];
1428 this.experimentalFoundationMeshes = [];
1429 this.sprite = new Promise((resolve) => {
1430 this.setSpriteURL = (url) => {
1431 svgLoader.load(url, resolve);
1432 };
1433 });
1434 this.hintSprite = new Promise((resolve) => {
1435 this.setHintSpriteURL = (url) => {
1436 svgLoader.load(url, resolve);
1437 };
1438 });
1439 this.options = __assign({}, cube3DOptionsDefaults);
1440 Object.assign(this.options, options);
1441 if (this.def.name !== "3x3x3") {
1442 throw new Error(`Invalid puzzle for this Cube3D implementation: ${this.def.name}`);
1443 }
1444 this.kpuzzleFaceletInfo = {};
1445 for (const orbit in pieceDefs) {
1446 const orbitFaceletInfo = [];
1447 this.kpuzzleFaceletInfo[orbit] = orbitFaceletInfo;
1448 this.pieces[orbit] = pieceDefs[orbit].map(this.createCubie.bind(this, orbit, orbitFaceletInfo));
1449 }
1450 this.scale.set(CUBE_SCALE, CUBE_SCALE, CUBE_SCALE);
1451 if (this.options.experimentalStickering) {
1452 this.setStickering(this.options.experimentalStickering);
1453 }
1454 cursor == null ? void 0 : cursor.addPositionListener(this);
1455 }
1456 experimentalSetStickerSpriteURL(stickerSpriteURL) {
1457 this.setSpriteURL(stickerSpriteURL);
1458 }
1459 experimentalSetHintStickerSpriteURL(hintStickerSpriteURL) {
1460 this.setHintSpriteURL(hintStickerSpriteURL);
1461 }
1462 setStickering(stickering) {
1463 (() => __async(this, null, function* () {
1464 const appearance = yield puzzles["3x3x3"].appearance(stickering != null ? stickering : "full");
1465 this.setAppearance(appearance != null ? appearance : yield puzzles["3x3x3"].appearance("full"));
1466 }))();
1467 }
1468 setAppearance(appearance) {
1469 var _a;
1470 for (const [orbitName, orbitAppearance] of Object.entries(appearance.orbits)) {
1471 for (let pieceIdx = 0; pieceIdx < orbitAppearance.pieces.length; pieceIdx++) {
1472 const pieceAppearance = orbitAppearance.pieces[pieceIdx];
1473 if (pieceAppearance) {
1474 const pieceInfo = this.kpuzzleFaceletInfo[orbitName][pieceIdx];
1475 for (let faceletIdx = 0; faceletIdx < pieceInfo.length; faceletIdx++) {
1476 const faceletAppearance = pieceAppearance.facelets[faceletIdx];
1477 if (faceletAppearance) {
1478 const faceletInfo = pieceInfo[faceletIdx];
1479 const appearance2 = typeof faceletAppearance === "string" ? faceletAppearance : faceletAppearance == null ? void 0 : faceletAppearance.appearance;
1480 faceletInfo.facelet.material = axesInfo[faceletInfo.faceIdx].stickerMaterial[appearance2];
1481 const hintAppearance = typeof faceletAppearance === "string" ? appearance2 : (_a = faceletAppearance.hintAppearance) != null ? _a : appearance2;
1482 if (faceletInfo.hintFacelet) {
1483 faceletInfo.hintFacelet.material = axesInfo[faceletInfo.faceIdx].hintStickerMaterial[hintAppearance];
1484 }
1485 }
1486 }
1487 }
1488 }
1489 }
1490 if (this.scheduleRenderCallback) {
1491 this.scheduleRenderCallback();
1492 }
1493 }
1494 experimentalUpdateOptions(options) {
1495 if ("showMainStickers" in options) {
1496 throw new Error("Unimplemented");
1497 }
1498 const showFoundation = options.showFoundation;
1499 if (typeof showFoundation !== "undefined" && this.options.showFoundation !== showFoundation) {
1500 this.options.showFoundation = showFoundation;
1501 for (const foundation of this.experimentalFoundationMeshes) {
1502 foundation.visible = showFoundation;
1503 }
1504 }
1505 const hintFacelets = options.hintFacelets;
1506 if (typeof hintFacelets !== "undefined" && this.options.hintFacelets !== hintFacelets && hintFaceletStyles[hintFacelets]) {
1507 this.options.hintFacelets = hintFacelets;
1508 for (const hintSticker of this.experimentalHintStickerMeshes) {
1509 hintSticker.visible = hintFacelets === "floating";
1510 }
1511 this.scheduleRenderCallback();
1512 }
1513 const experimentalStickering = options.experimentalStickering;
1514 if (typeof experimentalStickering !== "undefined" && this.options.experimentalStickering !== experimentalStickering && experimentalStickerings[experimentalStickering]) {
1515 this.options.experimentalStickering = experimentalStickering;
1516 this.setStickering(experimentalStickering);
1517 this.scheduleRenderCallback();
1518 }
1519 }
1520 onPositionChange(p) {
1521 const reid333 = p.state;
1522 for (const orbit in pieceDefs) {
1523 const pieces = pieceDefs[orbit];
1524 for (let i = 0; i < pieces.length; i++) {
1525 const j = reid333[orbit].permutation[i];
1526 this.pieces[orbit][j].matrix.copy(pieceDefs[orbit][i].matrix);
1527 this.pieces[orbit][j].matrix.multiply(orientationRotation[orbit][reid333[orbit].orientation[i]]);
1528 }
1529 for (const moveProgress of p.movesInProgress) {
1530 const move = moveProgress.move;
1531 const turnNormal = axesInfo[familyToAxis[move.family]].vector;
1532 const moveMatrix = new Matrix4().makeRotationAxis(turnNormal, -this.ease(moveProgress.fraction) * moveProgress.direction * move.amount * TAU / 4);
1533 for (let i = 0; i < pieces.length; i++) {
1534 const k = this.def.moves[move.family][orbit].permutation[i];
1535 if (i !== k || this.def.moves[move.family][orbit].orientation[i] !== 0) {
1536 const j = reid333[orbit].permutation[i];
1537 this.pieces[orbit][j].matrix.premultiply(moveMatrix);
1538 }
1539 }
1540 }
1541 }
1542 this.scheduleRenderCallback();
1543 }
1544 createCubie(orbit, orbitFacelets, piece, orbitPieceIdx) {
1545 const cubieFaceletInfo = [];
1546 orbitFacelets.push(cubieFaceletInfo);
1547 const cubie = new Group();
1548 if (this.options.showFoundation) {
1549 const foundation = this.createCubieFoundation();
1550 cubie.add(foundation);
1551 this.experimentalFoundationMeshes.push(foundation);
1552 }
1553 for (let i = 0; i < piece.stickerFaces.length; i++) {
1554 const sticker = this.createSticker(axesInfo[cubieStickerOrder[i]], axesInfo[piece.stickerFaces[i]], false);
1555 const faceletInfo = {
1556 faceIdx: piece.stickerFaces[i],
1557 facelet: sticker
1558 };
1559 cubie.add(sticker);
1560 if (this.options.hintFacelets === "floating") {
1561 const hintSticker = this.createSticker(axesInfo[cubieStickerOrder[i]], axesInfo[piece.stickerFaces[i]], true);
1562 cubie.add(hintSticker);
1563 faceletInfo.hintFacelet = hintSticker;
1564 this.experimentalHintStickerMeshes.push(hintSticker);
1565 }
1566 if (this.options.experimentalStickering === "picture" && pictureStickerCoords[orbit] && pictureStickerCoords[orbit][orbitPieceIdx] && pictureStickerCoords[orbit][orbitPieceIdx][i]) {
1567 const [rotate, offsetX, offsetY] = pictureStickerCoords[orbit][orbitPieceIdx][i];
1568 (() => __async(this, null, function* () {
1569 const addImageSticker = (hint) => __async(this, null, function* () {
1570 const texture = yield hint ? this.hintSprite : this.sprite;
1571 const mesh = this.createSticker(axesInfo[cubieStickerOrder[i]], axesInfo[piece.stickerFaces[i]], hint);
1572 mesh.material = new MeshBasicMaterial({
1573 map: texture,
1574 side: hint ? BackSide : DoubleSide,
1575 transparent: true
1576 });
1577 const x1 = offsetX / 12;
1578 const x2 = (offsetX + 1) / 12;
1579 const y1 = offsetY / 9;
1580 const y2 = (offsetY + 1) / 9;
1581 let v1 = new Vector2(x1, y1);
1582 let v2 = new Vector2(x1, y2);
1583 let v3 = new Vector2(x2, y2);
1584 let v4 = new Vector2(x2, y1);
1585 switch (rotate) {
1586 case 1:
1587 [v1, v2, v3, v4] = [v2, v3, v4, v1];
1588 break;
1589 case 2:
1590 [v1, v2, v3, v4] = [v3, v4, v1, v2];
1591 break;
1592 case 3:
1593 [v1, v2, v3, v4] = [v4, v1, v2, v3];
1594 break;
1595 }
1596 mesh.geometry.setAttribute("uv", new BufferAttribute(new Float32Array([
1597 v3.x,
1598 v3.y,
1599 v2.x,
1600 v2.y,
1601 v4.x,
1602 v4.y,
1603 v2.x,
1604 v2.y,
1605 v1.x,
1606 v1.y,
1607 v4.x,
1608 v4.y
1609 ]), 2));
1610 cubie.add(mesh);
1611 });
1612 addImageSticker(true);
1613 addImageSticker(false);
1614 }))();
1615 }
1616 cubieFaceletInfo.push(faceletInfo);
1617 }
1618 cubie.matrix.copy(piece.matrix);
1619 cubie.matrixAutoUpdate = false;
1620 this.add(cubie);
1621 return cubie;
1622 }
1623 createCubieFoundation() {
1624 const box = sharedCubieFoundationGeometry();
1625 return new Mesh(box, this.options.experimentalStickering === "picture" ? blackMesh : blackTranslucentMesh);
1626 }
1627 createSticker(posAxisInfo, materialAxisInfo, isHint) {
1628 const geo = this.options.experimentalStickering === "picture" ? newStickerGeometry() : sharedStickerGeometry();
1629 const stickerMesh = new Mesh(geo, isHint ? materialAxisInfo.hintStickerMaterial.regular : materialAxisInfo.stickerMaterial.regular);
1630 stickerMesh.setRotationFromEuler(posAxisInfo.fromZ);
1631 stickerMesh.position.copy(posAxisInfo.vector);
1632 stickerMesh.position.multiplyScalar(isHint ? this.options.experimentalStickering === "picture" ? EXPERIMENTAL_PICTURE_CUBE_HINT_ELEVATION : cubieDimensions.hintStickerElevation : cubieDimensions.stickerElevation);
1633 return stickerMesh;
1634 }
1635 experimentalSetFoundationOpacity(opacity) {
1636 this.experimentalFoundationMeshes[0].material.opacity = opacity;
1637 }
1638 experimentalSetStickerWidth(width) {
1639 for (const orbitInfo of Object.values(this.kpuzzleFaceletInfo)) {
1640 for (const pieceInfo of orbitInfo) {
1641 for (const faceletInfo of pieceInfo) {
1642 faceletInfo.facelet.scale.setScalar(width / cubieDimensions.stickerWidth);
1643 }
1644 }
1645 }
1646 }
1647 experimentalSetCenterStickerWidth(width) {
1648 for (const orbitInfo of [this.kpuzzleFaceletInfo["CENTERS"]]) {
1649 for (const pieceInfo of orbitInfo) {
1650 for (const faceletInfo of pieceInfo) {
1651 faceletInfo.facelet.scale.setScalar(width / cubieDimensions.stickerWidth);
1652 }
1653 }
1654 }
1655 }
1656 ease(fraction) {
1657 return smootherStep(fraction);
1658 }
1659};
1660
1661// src/cubing/twisty/3D/puzzles/PG3D.ts
1662import {
1663 BufferAttribute as BufferAttribute2,
1664 BufferGeometry as BufferGeometry2,
1665 Color,
1666 DoubleSide as DoubleSide2,
1667 Euler as Euler2,
1668 Group as Group2,
1669 Mesh as Mesh2,
1670 MeshBasicMaterial as MeshBasicMaterial2,
1671 Object3D as Object3D2,
1672 Triangle,
1673 Vector3 as Vector36
1674} from "three";
1675var foundationMaterial = new MeshBasicMaterial2({
1676 side: DoubleSide2,
1677 color: 0
1678});
1679var stickerMaterial = new MeshBasicMaterial2({
1680 vertexColors: true
1681});
1682var polyMaterial = new MeshBasicMaterial2({
1683 visible: false
1684});
1685var Filler = class {
1686 constructor(sz, colored = true) {
1687 this.sz = sz;
1688 this.colored = colored;
1689 this.vertices = new Float32Array(9 * sz);
1690 if (colored) {
1691 this.colors = new Uint8Array(9 * sz);
1692 this.ind = new Uint8Array(sz);
1693 }
1694 this.pos = 0;
1695 this.ipos = 0;
1696 }
1697 add(pt, c) {
1698 this.vertices[this.pos] = pt[0];
1699 this.vertices[this.pos + 1] = pt[1];
1700 this.vertices[this.pos + 2] = pt[2];
1701 this.colors[this.pos] = c >> 16;
1702 this.colors[this.pos + 1] = c >> 8 & 255;
1703 this.colors[this.pos + 2] = c & 255;
1704 this.pos += 3;
1705 }
1706 addUncolored(pt) {
1707 this.vertices[this.pos] = pt[0];
1708 this.vertices[this.pos + 1] = pt[1];
1709 this.vertices[this.pos + 2] = pt[2];
1710 this.pos += 3;
1711 }
1712 setind(i) {
1713 this.ind[this.ipos++] = i;
1714 }
1715 setAttributes(geo) {
1716 geo.setAttribute("position", new BufferAttribute2(this.vertices, 3));
1717 if (this.colored) {
1718 geo.setAttribute("color", new BufferAttribute2(this.colors, 3, true));
1719 }
1720 }
1721 makeGroups(geo) {
1722 geo.clearGroups();
1723 for (let i = 0; i < this.ipos; ) {
1724 const si = i++;
1725 const iv = this.ind[si];
1726 while (this.ind[i] === iv) {
1727 i++;
1728 }
1729 geo.addGroup(3 * si, 3 * (i - si), iv);
1730 }
1731 }
1732};
1733function makePoly(filler, coords, color, scale, ind, faceArray) {
1734 let ncoords = coords;
1735 if (scale !== 1) {
1736 ncoords = [];
1737 for (const v of coords) {
1738 const v2 = [v[0] * scale, v[1] * scale, v[2] * scale];
1739 ncoords.push(v2);
1740 }
1741 }
1742 for (let g = 1; g + 1 < ncoords.length; g++) {
1743 faceArray.push(filler.ipos);
1744 filler.add(ncoords[0], color);
1745 filler.add(ncoords[g], color);
1746 filler.add(ncoords[g + 1], color);
1747 filler.setind(ind);
1748 }
1749}
1750var StickerDef = class {
1751 constructor(filler, stickerDat, hintStickers, options) {
1752 this.filler = filler;
1753 this.faceArray = [];
1754 this.twistVal = -1;
1755 const sdColor = new Color(stickerDat.color).getHex();
1756 this.origColor = sdColor;
1757 this.origColorAppearance = sdColor;
1758 if (options == null ? void 0 : options.appearance) {
1759 this.setAppearance(options.appearance);
1760 }
1761 this.faceColor = sdColor;
1762 const coords = stickerDat.coords;
1763 makePoly(filler, coords, this.faceColor, 1, 0, this.faceArray);
1764 if (hintStickers) {
1765 let highArea = 0;
1766 let goodFace = null;
1767 for (const f of this.faceArray) {
1768 const t2 = new Triangle(new Vector36(filler.vertices[9 * f], filler.vertices[9 * f + 1], filler.vertices[9 * f + 2]), new Vector36(filler.vertices[9 * f + 3], filler.vertices[9 * f + 4], filler.vertices[9 * f + 5]), new Vector36(filler.vertices[9 * f + 6], filler.vertices[9 * f + 7], filler.vertices[9 * f + 8]));
1769 const a = t2.getArea();
1770 if (a > highArea) {
1771 highArea = a;
1772 goodFace = t2;
1773 }
1774 }
1775 const norm = new Vector36();
1776 goodFace.getNormal(norm);
1777 norm.multiplyScalar(0.5);
1778 const hintCoords = [];
1779 for (let i = 0; i < coords.length; i++) {
1780 const j = coords.length - 1 - i;
1781 hintCoords.push([
1782 coords[j][0] + norm.x,
1783 coords[j][1] + norm.y,
1784 coords[j][2] + norm.z
1785 ]);
1786 }
1787 makePoly(filler, hintCoords, this.faceColor, 1, 0, this.faceArray);
1788 }
1789 }
1790 addFoundation(filler, foundationDat, black) {
1791 makePoly(filler, foundationDat.coords, black, 0.999, 2, this.faceArray);
1792 }
1793 setAppearance(faceletMeshAppearance) {
1794 switch (faceletMeshAppearance) {
1795 case "regular":
1796 this.origColorAppearance = this.origColor;
1797 break;
1798 case "dim":
1799 if (this.origColor === 16777215) {
1800 this.origColorAppearance = 14540253;
1801 } else {
1802 this.origColorAppearance = new Color(this.origColor).multiplyScalar(0.5).getHex();
1803 }
1804 break;
1805 case "oriented":
1806 this.origColorAppearance = 16746751;
1807 break;
1808 case "ignored":
1809 this.origColorAppearance = 4473924;
1810 break;
1811 case "invisible":
1812 throw new Error("unimplemented");
1813 }
1814 }
1815 setColor(c) {
1816 if (this.faceColor !== c) {
1817 this.faceColor = c;
1818 const r2 = c >> 16;
1819 const g = c >> 8 & 255;
1820 const b = c & 255;
1821 for (const f of this.faceArray) {
1822 for (let i = 0; i < 9; i += 3) {
1823 this.filler.colors[9 * f + i] = r2;
1824 this.filler.colors[9 * f + i + 1] = g;
1825 this.filler.colors[9 * f + i + 2] = b;
1826 }
1827 }
1828 return 1;
1829 } else {
1830 return 0;
1831 }
1832 }
1833};
1834var HitPlaneDef = class {
1835 constructor(hitface) {
1836 this.cubie = new Group2();
1837 const coords = hitface.coords;
1838 const filler = new Filler(coords.length - 2, true);
1839 for (let g = 1; g + 1 < coords.length; g++) {
1840 filler.addUncolored(coords[0]);
1841 filler.addUncolored(coords[g]);
1842 filler.addUncolored(coords[g + 1]);
1843 }
1844 this.geo = new BufferGeometry2();
1845 filler.setAttributes(this.geo);
1846 const obj = new Mesh2(this.geo, polyMaterial);
1847 obj.userData.name = hitface.name;
1848 this.cubie.scale.setScalar(0.99);
1849 this.cubie.add(obj);
1850 }
1851};
1852var AxisInfo2 = class {
1853 constructor(axisDat) {
1854 const vec = axisDat[0];
1855 this.axis = new Vector36(vec[0], vec[1], vec[2]);
1856 this.order = axisDat[2];
1857 }
1858};
1859var PG_SCALE = 0.5;
1860var _pendingStickeringUpdate;
1861var PG3D = class extends Object3D2 {
1862 constructor(cursor, scheduleRenderCallback, definition, pgdat, showFoundation = false, hintStickers = false, params = {}) {
1863 super();
1864 this.scheduleRenderCallback = scheduleRenderCallback;
1865 this.definition = definition;
1866 this.pgdat = pgdat;
1867 this.params = params;
1868 this.stickerTargets = [];
1869 this.controlTargets = [];
1870 _pendingStickeringUpdate.set(this, false);
1871 this.axesInfo = {};
1872 const axesDef = this.pgdat.axis;
1873 for (const axis of axesDef) {
1874 this.axesInfo[axis[1]] = new AxisInfo2(axis);
1875 }
1876 const stickers = this.pgdat.stickers;
1877 this.stickers = {};
1878 const materialArray1 = [
1879 stickerMaterial,
1880 polyMaterial,
1881 foundationMaterial,
1882 polyMaterial
1883 ];
1884 const materialArray2 = [
1885 polyMaterial,
1886 stickerMaterial,
1887 polyMaterial,
1888 foundationMaterial
1889 ];
1890 let triangleCount = 0;
1891 let multiplier = 1;
1892 if (hintStickers) {
1893 multiplier++;
1894 }
1895 if (showFoundation) {
1896 multiplier++;
1897 }
1898 for (let si = 0; si < stickers.length; si++) {
1899 const sides = stickers[si].coords.length;
1900 triangleCount += multiplier * (sides - 2);
1901 }
1902 const filler = new Filler(triangleCount);
1903 const black = 0;
1904 for (let si = 0; si < stickers.length; si++) {
1905 const sticker = stickers[si];
1906 const orbit = sticker.orbit;
1907 const ord = sticker.ord;
1908 const ori = sticker.ori;
1909 if (!this.stickers[orbit]) {
1910 this.stickers[orbit] = [];
1911 }
1912 if (!this.stickers[orbit][ori]) {
1913 this.stickers[orbit][ori] = [];
1914 }
1915 const options = {};
1916 if (params.appearance) {
1917 options.appearance = getFaceletAppearance(params.appearance, orbit, ord, ori, false);
1918 }
1919 const stickerdef = new StickerDef(filler, sticker, hintStickers, options);
1920 this.stickers[orbit][ori][ord] = stickerdef;
1921 }
1922 this.foundationBound = filler.ipos;
1923 if (showFoundation) {
1924 for (let si = 0; si < stickers.length; si++) {
1925 const sticker = stickers[si];
1926 const foundation = this.pgdat.foundations[si];
1927 const orbit = sticker.orbit;
1928 const ord = sticker.ord;
1929 const ori = sticker.ori;
1930 this.stickers[orbit][ori][ord].addFoundation(filler, foundation, black);
1931 }
1932 }
1933 const fixedGeo = new BufferGeometry2();
1934 filler.setAttributes(fixedGeo);
1935 filler.makeGroups(fixedGeo);
1936 const obj = new Mesh2(fixedGeo, materialArray1);
1937 obj.scale.set(PG_SCALE, PG_SCALE, PG_SCALE);
1938 this.add(obj);
1939 const obj2 = new Mesh2(fixedGeo, materialArray2);
1940 obj2.scale.set(PG_SCALE, PG_SCALE, PG_SCALE);
1941 this.add(obj2);
1942 const hitfaces = this.pgdat.faces;
1943 this.movingObj = obj2;
1944 this.fixedGeo = fixedGeo;
1945 this.filler = filler;
1946 for (const hitface of hitfaces) {
1947 const facedef = new HitPlaneDef(hitface);
1948 facedef.cubie.scale.set(PG_SCALE, PG_SCALE, PG_SCALE);
1949 this.add(facedef.cubie);
1950 this.controlTargets.push(facedef.cubie.children[0]);
1951 }
1952 cursor.addPositionListener(this);
1953 }
1954 dispose() {
1955 if (this.fixedGeo) {
1956 this.fixedGeo.dispose();
1957 }
1958 }
1959 experimentalGetStickerTargets() {
1960 return this.stickerTargets;
1961 }
1962 experimentalGetControlTargets() {
1963 return this.controlTargets;
1964 }
1965 experimentalSetAppearance(appearance) {
1966 this.params.appearance = appearance;
1967 for (const orbitName in this.definition.orbits) {
1968 const {numPieces, orientations} = this.definition.orbits[orbitName];
1969 for (let pieceIdx = 0; pieceIdx < numPieces; pieceIdx++) {
1970 for (let faceletIdx = 0; faceletIdx < orientations; faceletIdx++) {
1971 const faceletAppearance = getFaceletAppearance(appearance, orbitName, pieceIdx, faceletIdx, false);
1972 const stickerDef = this.stickers[orbitName][faceletIdx][pieceIdx];
1973 stickerDef.setAppearance(faceletAppearance);
1974 }
1975 }
1976 }
1977 if (this.scheduleRenderCallback) {
1978 __privateSet(this, _pendingStickeringUpdate, true);
1979 this.onPositionChange(this.lastPos);
1980 this.scheduleRenderCallback();
1981 }
1982 }
1983 onPositionChange(p) {
1984 const state = p.state;
1985 const noRotation = new Euler2();
1986 this.movingObj.rotation.copy(noRotation);
1987 let colormods = 0;
1988 if (!this.lastPos || __privateGet(this, _pendingStickeringUpdate) || !areTransformationsEquivalent(this.definition, this.lastPos.state, state)) {
1989 for (const orbit in this.stickers) {
1990 const pieces = this.stickers[orbit];
1991 const pos2 = state[orbit];
1992 const orin = pieces.length;
1993 if (orin === 1) {
1994 const pieces2 = pieces[0];
1995 for (let i = 0; i < pieces2.length; i++) {
1996 const ni = pos2.permutation[i];
1997 colormods += pieces2[i].setColor(pieces2[ni].origColorAppearance);
1998 }
1999 } else {
2000 for (let ori = 0; ori < orin; ori++) {
2001 const pieces2 = pieces[ori];
2002 for (let i = 0; i < pieces2.length; i++) {
2003 const nori = (ori + orin - pos2.orientation[i]) % orin;
2004 const ni = pos2.permutation[i];
2005 colormods += pieces2[i].setColor(pieces[nori][ni].origColorAppearance);
2006 }
2007 }
2008 }
2009 }
2010 this.lastPos = p;
2011 __privateSet(this, _pendingStickeringUpdate, false);
2012 }
2013 let vismods = 0;
2014 for (const moveProgress of p.movesInProgress) {
2015 const externalMove = moveProgress.move;
2016 const unswizzled = this.pgdat.unswizzle(externalMove);
2017 const move = this.pgdat.notationMapper.notationToInternal(externalMove);
2018 if (move === null) {
2019 throw Error("Bad blockmove " + externalMove.family);
2020 }
2021 const quantumTransformation = transformationForQuantumMove(this.definition, externalMove.quantum);
2022 const ax = this.axesInfo[unswizzled];
2023 const turnNormal = ax.axis;
2024 const angle = -this.ease(moveProgress.fraction) * moveProgress.direction * move.amount * TAU / ax.order;
2025 this.movingObj.rotateOnAxis(turnNormal, angle);
2026 if (this.lastMove !== quantumTransformation) {
2027 for (const orbit in this.stickers) {
2028 const pieces = this.stickers[orbit];
2029 const orin = pieces.length;
2030 const bmv = quantumTransformation[orbit];
2031 for (let ori = 0; ori < orin; ori++) {
2032 const pieces2 = pieces[ori];
2033 for (let i = 0; i < pieces2.length; i++) {
2034 const ni = bmv.permutation[i];
2035 let tv = 0;
2036 if (ni !== i || bmv.orientation[i] !== 0) {
2037 tv = 1;
2038 }
2039 if (tv !== pieces2[i].twistVal) {
2040 if (tv) {
2041 for (const f of pieces2[i].faceArray) {
2042 this.filler.ind[f] |= 1;
2043 }
2044 } else {
2045 for (const f of pieces2[i].faceArray) {
2046 this.filler.ind[f] &= ~1;
2047 }
2048 }
2049 pieces2[i].twistVal = tv;
2050 vismods++;
2051 }
2052 }
2053 }
2054 }
2055 this.lastMove = quantumTransformation;
2056 }
2057 }
2058 if (vismods) {
2059 this.filler.makeGroups(this.fixedGeo);
2060 }
2061 if (colormods) {
2062 this.fixedGeo.getAttribute("color").updateRange = {
2063 offset: 0,
2064 count: 9 * this.foundationBound
2065 };
2066 this.fixedGeo.getAttribute("color").needsUpdate = true;
2067 }
2068 this.scheduleRenderCallback();
2069 }
2070 ease(fraction) {
2071 return smootherStep(fraction);
2072 }
2073};
2074_pendingStickeringUpdate = new WeakMap();
2075
2076// src/cubing/twisty/3D/Twisty3DScene.ts
2077import {Scene as ThreeScene} from "three";
2078var Twisty3DScene = class extends ThreeScene {
2079 constructor() {
2080 super();
2081 this.renderTargets = new Set();
2082 this.twisty3Ds = new Set();
2083 }
2084 addRenderTarget(renderTarget) {
2085 this.renderTargets.add(renderTarget);
2086 }
2087 scheduleRender() {
2088 for (const renderTarget of this.renderTargets) {
2089 renderTarget.scheduleRender();
2090 }
2091 }
2092 addTwisty3DPuzzle(twisty3DPuzzle) {
2093 this.twisty3Ds.add(twisty3DPuzzle);
2094 this.add(twisty3DPuzzle);
2095 }
2096 removeTwisty3DPuzzle(twisty3DPuzzle) {
2097 this.twisty3Ds.delete(twisty3DPuzzle);
2098 this.remove(twisty3DPuzzle);
2099 }
2100 clearPuzzles() {
2101 for (const puz of this.twisty3Ds) {
2102 this.remove(puz);
2103 }
2104 this.twisty3Ds.clear();
2105 }
2106};
2107
2108// src/cubing/twisty/3D/puzzles/KPuzzleWrapper.ts
2109var PuzzleWrapper = class {
2110 multiply(state, amount) {
2111 if (amount < 0) {
2112 return this.invert(this.multiply(state, -amount));
2113 }
2114 if (amount === 0) {
2115 return this.identity();
2116 }
2117 while (amount % 2 === 0) {
2118 amount = amount / 2;
2119 state = this.combine(state, state);
2120 }
2121 let newState = state;
2122 amount--;
2123 while (amount > 0) {
2124 if (amount % 2 === 1) {
2125 newState = this.combine(newState, state);
2126 }
2127 amount = Math.floor(amount / 2);
2128 if (amount > 0) {
2129 state = this.combine(state, state);
2130 }
2131 }
2132 return newState;
2133 }
2134};
2135var KPuzzleWrapper = class extends PuzzleWrapper {
2136 constructor(definition) {
2137 super();
2138 this.definition = definition;
2139 this.moveCache = {};
2140 }
2141 static fromID(id) {
2142 return __async(this, null, function* () {
2143 return new KPuzzleWrapper(yield puzzles[id].def());
2144 });
2145 }
2146 startState() {
2147 return this.definition.startPieces;
2148 }
2149 invert(state) {
2150 return invertTransformation(this.definition, state);
2151 }
2152 combine(s1, s2) {
2153 return combineTransformations(this.definition, s1, s2);
2154 }
2155 stateFromMove(move) {
2156 const key = move.toString();
2157 if (!this.moveCache[key]) {
2158 this.moveCache[key] = transformationForMove(this.definition, move);
2159 }
2160 return this.moveCache[key];
2161 }
2162 identity() {
2163 return identityTransformation(this.definition);
2164 }
2165 equivalent(s1, s2) {
2166 return areStatesEquivalent(this.definition, s1, s2);
2167 }
2168};
2169
2170// src/cubing/twisty/animation/indexer/tree/chunkAlgs.ts
2171var MIN_CHUNKING_THRESHOLD = 16;
2172function chunkifyAlg(alg, chunkMaxLength) {
2173 const mainAlgBuilder = new AlgBuilder();
2174 const chunkAlgBuilder = new AlgBuilder();
2175 for (const unit of alg.units()) {
2176 chunkAlgBuilder.push(unit);
2177 if (chunkAlgBuilder.experimentalNumUnits() >= chunkMaxLength) {
2178 mainAlgBuilder.push(new Grouping(chunkAlgBuilder.toAlg()));
2179 chunkAlgBuilder.reset();
2180 }
2181 }
2182 mainAlgBuilder.push(new Grouping(chunkAlgBuilder.toAlg()));
2183 return mainAlgBuilder.toAlg();
2184}
2185var ChunkAlgs = class extends TraversalUp {
2186 traverseAlg(alg) {
2187 const algLength = alg.experimentalNumUnits();
2188 if (algLength < MIN_CHUNKING_THRESHOLD) {
2189 return alg;
2190 }
2191 return chunkifyAlg(alg, Math.ceil(Math.sqrt(algLength)));
2192 }
2193 traverseGrouping(grouping) {
2194 return new Grouping(this.traverseAlg(grouping.alg), grouping.amount);
2195 }
2196 traverseMove(move) {
2197 return move;
2198 }
2199 traverseCommutator(commutator) {
2200 return new Conjugate(this.traverseAlg(commutator.A), this.traverseAlg(commutator.B));
2201 }
2202 traverseConjugate(conjugate) {
2203 return new Conjugate(this.traverseAlg(conjugate.A), this.traverseAlg(conjugate.B));
2204 }
2205 traversePause(pause) {
2206 return pause;
2207 }
2208 traverseNewline(newline) {
2209 return newline;
2210 }
2211 traverseLineComment(comment) {
2212 return comment;
2213 }
2214};
2215var chunkAlgsInstance = new ChunkAlgs();
2216var chunkAlgs = chunkAlgsInstance.traverseAlg.bind(chunkAlgsInstance);
2217
2218// src/cubing/twisty/animation/indexer/AlgDuration.ts
2219function defaultDurationForAmount(amount) {
2220 switch (Math.abs(amount)) {
2221 case 0:
2222 return 0;
2223 case 1:
2224 return 1e3;
2225 case 2:
2226 return 1500;
2227 default:
2228 return 2e3;
2229 }
2230}
2231var AlgDuration = class extends TraversalUp {
2232 constructor(durationForAmount = defaultDurationForAmount) {
2233 super();
2234 this.durationForAmount = durationForAmount;
2235 }
2236 traverseAlg(alg) {
2237 let total = 0;
2238 for (const unit of alg.units()) {
2239 total += this.traverseUnit(unit);
2240 }
2241 return total;
2242 }
2243 traverseGrouping(grouping) {
2244 return grouping.amount * this.traverseAlg(grouping.alg);
2245 }
2246 traverseMove(move) {
2247 return this.durationForAmount(move.amount);
2248 }
2249 traverseCommutator(commutator) {
2250 return 2 * (this.traverseAlg(commutator.A) + this.traverseAlg(commutator.B));
2251 }
2252 traverseConjugate(conjugate) {
2253 return 2 * this.traverseAlg(conjugate.A) + this.traverseAlg(conjugate.B);
2254 }
2255 traversePause(_pause) {
2256 return this.durationForAmount(1);
2257 }
2258 traverseNewline(_newline) {
2259 return this.durationForAmount(1);
2260 }
2261 traverseLineComment(_comment) {
2262 return this.durationForAmount(0);
2263 }
2264};
2265
2266// src/cubing/twisty/animation/indexer/tree/AlgWalker.ts
2267var AlgPartDecoration = class {
2268 constructor(_puz, moveCount, duration, forward, backward, children = []) {
2269 this.moveCount = moveCount;
2270 this.duration = duration;
2271 this.forward = forward;
2272 this.backward = backward;
2273 this.children = children;
2274 }
2275};
2276var DecoratorConstructor = class extends TraversalUp {
2277 constructor(puz) {
2278 super();
2279 this.puz = puz;
2280 this.durationFn = new AlgDuration(defaultDurationForAmount);
2281 this.cache = {};
2282 this.identity = puz.identity();
2283 this.dummyLeaf = new AlgPartDecoration(puz, 0, 0, this.identity, this.identity, []);
2284 }
2285 traverseAlg(alg) {
2286 let moveCount = 0;
2287 let duration = 0;
2288 let state = this.identity;
2289 const child = [];
2290 for (const unit of alg.units()) {
2291 const apd = this.traverseUnit(unit);
2292 moveCount += apd.moveCount;
2293 duration += apd.duration;
2294 if (state === this.identity) {
2295 state = apd.forward;
2296 } else {
2297 state = this.puz.combine(state, apd.forward);
2298 }
2299 child.push(apd);
2300 }
2301 return new AlgPartDecoration(this.puz, moveCount, duration, state, this.puz.invert(state), child);
2302 }
2303 traverseGrouping(grouping) {
2304 const dec = this.traverseAlg(grouping.alg);
2305 return this.mult(dec, grouping.amount, [dec]);
2306 }
2307 traverseMove(move) {
2308 const key = move.toString();
2309 let r2 = this.cache[key];
2310 if (r2) {
2311 return r2;
2312 }
2313 r2 = new AlgPartDecoration(this.puz, 1, this.durationFn.traverseUnit(move), this.puz.stateFromMove(move), this.puz.stateFromMove(move.invert()));
2314 this.cache[key] = r2;
2315 return r2;
2316 }
2317 traverseCommutator(commutator) {
2318 const decA = this.traverseAlg(commutator.A);
2319 const decB = this.traverseAlg(commutator.B);
2320 const AB = this.puz.combine(decA.forward, decB.forward);
2321 const ApBp = this.puz.combine(decA.backward, decB.backward);
2322 const ABApBp = this.puz.combine(AB, ApBp);
2323 const dec = new AlgPartDecoration(this.puz, 2 * (decA.moveCount + decB.moveCount), 2 * (decA.duration + decB.duration), ABApBp, this.puz.invert(ABApBp), [decA, decB]);
2324 return this.mult(dec, 1, [dec, decA, decB]);
2325 }
2326 traverseConjugate(conjugate) {
2327 const decA = this.traverseAlg(conjugate.A);
2328 const decB = this.traverseAlg(conjugate.B);
2329 const AB = this.puz.combine(decA.forward, decB.forward);
2330 const ABAp = this.puz.combine(AB, decA.backward);
2331 const dec = new AlgPartDecoration(this.puz, 2 * decA.moveCount + decB.moveCount, 2 * decA.duration + decB.duration, ABAp, this.puz.invert(ABAp), [decA, decB]);
2332 return this.mult(dec, 1, [dec, decA, decB]);
2333 }
2334 traversePause(pause) {
2335 return new AlgPartDecoration(this.puz, 1, this.durationFn.traverseUnit(pause), this.identity, this.identity);
2336 }
2337 traverseNewline(_newline) {
2338 return this.dummyLeaf;
2339 }
2340 traverseLineComment(_comment) {
2341 return this.dummyLeaf;
2342 }
2343 mult(apd, n, child) {
2344 const absn = Math.abs(n);
2345 const st = this.puz.multiply(apd.forward, n);
2346 return new AlgPartDecoration(this.puz, apd.moveCount * absn, apd.duration * absn, st, this.puz.invert(st), child);
2347 }
2348};
2349var WalkerDown = class {
2350 constructor(apd, back) {
2351 this.apd = apd;
2352 this.back = back;
2353 }
2354};
2355var AlgWalker = class extends TraversalDownUp {
2356 constructor(puz, algOrUnit, apd) {
2357 super();
2358 this.puz = puz;
2359 this.algOrUnit = algOrUnit;
2360 this.apd = apd;
2361 this.i = -1;
2362 this.dur = -1;
2363 this.goali = -1;
2364 this.goaldur = -1;
2365 this.move = void 0;
2366 this.back = false;
2367 this.moveDuration = 0;
2368 this.st = this.puz.identity();
2369 this.root = new WalkerDown(this.apd, false);
2370 }
2371 moveByIndex(loc) {
2372 if (this.i >= 0 && this.i === loc) {
2373 return this.move !== void 0;
2374 }
2375 return this.dosearch(loc, Infinity);
2376 }
2377 moveByDuration(dur) {
2378 if (this.dur >= 0 && this.dur < dur && this.dur + this.moveDuration >= dur) {
2379 return this.move !== void 0;
2380 }
2381 return this.dosearch(Infinity, dur);
2382 }
2383 dosearch(loc, dur) {
2384 this.goali = loc;
2385 this.goaldur = dur;
2386 this.i = 0;
2387 this.dur = 0;
2388 this.move = void 0;
2389 this.moveDuration = 0;
2390 this.back = false;
2391 this.st = this.puz.identity();
2392 const r2 = this.algOrUnit.is(Alg) ? this.traverseAlg(this.algOrUnit, this.root) : this.traverseUnit(this.algOrUnit, this.root);
2393 return r2;
2394 }
2395 traverseAlg(alg, wd) {
2396 if (!this.firstcheck(wd)) {
2397 return false;
2398 }
2399 let i = wd.back ? alg.experimentalNumUnits() - 1 : 0;
2400 for (const unit of directedGenerator(alg.units(), wd.back ? IterationDirection.Backwards : IterationDirection.Forwards)) {
2401 if (this.traverseUnit(unit, new WalkerDown(wd.apd.children[i], wd.back))) {
2402 return true;
2403 }
2404 i += wd.back ? -1 : 1;
2405 }
2406 return false;
2407 }
2408 traverseGrouping(grouping, wd) {
2409 if (!this.firstcheck(wd)) {
2410 return false;
2411 }
2412 const back = this.domult(wd, grouping.amount);
2413 return this.traverseAlg(grouping.alg, new WalkerDown(wd.apd.children[0], back));
2414 }
2415 traverseMove(move, wd) {
2416 if (!this.firstcheck(wd)) {
2417 return false;
2418 }
2419 this.move = move;
2420 this.moveDuration = wd.apd.duration;
2421 this.back = wd.back;
2422 return true;
2423 }
2424 traverseCommutator(commutator, wd) {
2425 if (!this.firstcheck(wd)) {
2426 return false;
2427 }
2428 const back = this.domult(wd, 1);
2429 if (back) {
2430 return this.traverseAlg(commutator.B, new WalkerDown(wd.apd.children[2], !back)) || this.traverseAlg(commutator.A, new WalkerDown(wd.apd.children[1], !back)) || this.traverseAlg(commutator.B, new WalkerDown(wd.apd.children[2], back)) || this.traverseAlg(commutator.A, new WalkerDown(wd.apd.children[1], back));
2431 } else {
2432 return this.traverseAlg(commutator.A, new WalkerDown(wd.apd.children[1], back)) || this.traverseAlg(commutator.B, new WalkerDown(wd.apd.children[2], back)) || this.traverseAlg(commutator.A, new WalkerDown(wd.apd.children[1], !back)) || this.traverseAlg(commutator.B, new WalkerDown(wd.apd.children[2], !back));
2433 }
2434 }
2435 traverseConjugate(conjugate, wd) {
2436 if (!this.firstcheck(wd)) {
2437 return false;
2438 }
2439 const back = this.domult(wd, 1);
2440 if (back) {
2441 return this.traverseAlg(conjugate.A, new WalkerDown(wd.apd.children[1], !back)) || this.traverseAlg(conjugate.B, new WalkerDown(wd.apd.children[2], back)) || this.traverseAlg(conjugate.A, new WalkerDown(wd.apd.children[1], back));
2442 } else {
2443 return this.traverseAlg(conjugate.A, new WalkerDown(wd.apd.children[1], back)) || this.traverseAlg(conjugate.B, new WalkerDown(wd.apd.children[2], back)) || this.traverseAlg(conjugate.A, new WalkerDown(wd.apd.children[1], !back));
2444 }
2445 }
2446 traversePause(pause, wd) {
2447 if (!this.firstcheck(wd)) {
2448 return false;
2449 }
2450 this.move = pause;
2451 this.moveDuration = wd.apd.duration;
2452 this.back = wd.back;
2453 return true;
2454 }
2455 traverseNewline(_newline, _wd) {
2456 return false;
2457 }
2458 traverseLineComment(_lineComment, _wd) {
2459 return false;
2460 }
2461 firstcheck(wd) {
2462 if (wd.apd.moveCount + this.i <= this.goali && wd.apd.duration + this.dur < this.goaldur) {
2463 return this.keepgoing(wd);
2464 }
2465 return true;
2466 }
2467 domult(wd, amount) {
2468 let back = wd.back;
2469 if (amount === 0) {
2470 return back;
2471 }
2472 if (amount < 0) {
2473 back = !back;
2474 amount = -amount;
2475 }
2476 const base = wd.apd.children[0];
2477 const full = Math.min(Math.floor((this.goali - this.i) / base.moveCount), Math.ceil((this.goaldur - this.dur) / base.duration - 1));
2478 if (full > 0) {
2479 this.keepgoing(new WalkerDown(base, back), full);
2480 }
2481 return back;
2482 }
2483 keepgoing(wd, mul = 1) {
2484 this.i += mul * wd.apd.moveCount;
2485 this.dur += mul * wd.apd.duration;
2486 if (mul !== 1) {
2487 if (wd.back) {
2488 this.st = this.puz.combine(this.st, this.puz.multiply(wd.apd.backward, mul));
2489 } else {
2490 this.st = this.puz.combine(this.st, this.puz.multiply(wd.apd.forward, mul));
2491 }
2492 } else {
2493 if (wd.back) {
2494 this.st = this.puz.combine(this.st, wd.apd.backward);
2495 } else {
2496 this.st = this.puz.combine(this.st, wd.apd.forward);
2497 }
2498 }
2499 return false;
2500 }
2501};
2502
2503// src/cubing/twisty/animation/indexer/tree/TreeAlgIndexer.ts
2504var TreeAlgIndexer = class {
2505 constructor(puzzle, alg) {
2506 this.puzzle = puzzle;
2507 const deccon = new DecoratorConstructor(this.puzzle);
2508 const chunkedAlg = chunkAlgs(alg);
2509 this.decoration = deccon.traverseAlg(chunkedAlg);
2510 this.walker = new AlgWalker(this.puzzle, chunkedAlg, this.decoration);
2511 }
2512 getMove(index) {
2513 if (this.walker.moveByIndex(index)) {
2514 if (!this.walker.move) {
2515 throw new Error("`this.walker.mv` missing");
2516 }
2517 const move = this.walker.move;
2518 if (this.walker.back) {
2519 return move.invert();
2520 }
2521 return move;
2522 }
2523 return null;
2524 }
2525 indexToMoveStartTimestamp(index) {
2526 if (this.walker.moveByIndex(index) || this.walker.i === index) {
2527 return this.walker.dur;
2528 }
2529 throw new Error("Out of algorithm: index " + index);
2530 }
2531 indexToMovesInProgress(index) {
2532 if (this.walker.moveByIndex(index) || this.walker.i === index) {
2533 return this.walker.dur;
2534 }
2535 throw new Error("Out of algorithm: index " + index);
2536 }
2537 stateAtIndex(index, startTransformation) {
2538 this.walker.moveByIndex(index);
2539 return this.puzzle.combine(startTransformation != null ? startTransformation : this.puzzle.startState(), this.walker.st);
2540 }
2541 transformAtIndex(index) {
2542 this.walker.moveByIndex(index);
2543 return this.walker.st;
2544 }
2545 numMoves() {
2546 return this.decoration.moveCount;
2547 }
2548 timestampToIndex(timestamp) {
2549 this.walker.moveByDuration(timestamp);
2550 return this.walker.i;
2551 }
2552 algDuration() {
2553 return this.decoration.duration;
2554 }
2555 moveDuration(index) {
2556 this.walker.moveByIndex(index);
2557 return this.walker.moveDuration;
2558 }
2559};
2560
2561// src/cubing/twisty/animation/cursor/CursorTypes.ts
2562var Direction;
2563(function(Direction2) {
2564 Direction2[Direction2["Forwards"] = 1] = "Forwards";
2565 Direction2[Direction2["Paused"] = 0] = "Paused";
2566 Direction2[Direction2["Backwards"] = -1] = "Backwards";
2567})(Direction || (Direction = {}));
2568function directionScalar(direction) {
2569 return direction;
2570}
2571var BoundaryType;
2572(function(BoundaryType2) {
2573 BoundaryType2[BoundaryType2["Move"] = 0] = "Move";
2574 BoundaryType2[BoundaryType2["EntireTimeline"] = 1] = "EntireTimeline";
2575})(BoundaryType || (BoundaryType = {}));
2576
2577// src/cubing/twisty/animation/cursor/AlgCursor.ts
2578var AlgCursor = class {
2579 constructor(timeline, def, alg, startStateAlg, indexerConstructor) {
2580 this.timeline = timeline;
2581 this.def = def;
2582 this.alg = alg;
2583 this.positionListeners = new Set();
2584 this.indexerConstructor = TreeAlgIndexer;
2585 this.ksolvePuzzle = new KPuzzleWrapper(def);
2586 if (indexerConstructor) {
2587 this.indexerConstructor = this.indexerConstructor;
2588 }
2589 this.instantiateIndexer(alg);
2590 this.startState = startStateAlg ? this.algToState(startStateAlg) : this.ksolvePuzzle.startState();
2591 timeline.addTimestampListener(this);
2592 }
2593 setStartState(startState) {
2594 this.startState = startState;
2595 this.dispatchPositionForTimestamp(this.timeline.timestamp);
2596 }
2597 experimentalSetIndexer(indexerConstructor) {
2598 this.indexerConstructor = indexerConstructor;
2599 this.instantiateIndexer(this.alg);
2600 this.timeline.onCursorChange(this);
2601 this.dispatchPositionForTimestamp(this.timeline.timestamp);
2602 }
2603 instantiateIndexer(alg) {
2604 this.indexer = new this.indexerConstructor(this.ksolvePuzzle, alg);
2605 }
2606 algToState(s) {
2607 const kpuzzle = new KPuzzle(this.def);
2608 kpuzzle.applyAlg(s);
2609 return this.ksolvePuzzle.combine(this.def.startPieces, kpuzzle.state);
2610 }
2611 timeRange() {
2612 return {
2613 start: 0,
2614 end: this.indexer.algDuration()
2615 };
2616 }
2617 experimentalTimestampForStartOfLastMove() {
2618 const numMoves = this.indexer.numMoves();
2619 if (numMoves > 0) {
2620 return this.indexer.indexToMoveStartTimestamp(numMoves - 1);
2621 }
2622 return 0;
2623 }
2624 addPositionListener(positionListener) {
2625 this.positionListeners.add(positionListener);
2626 this.dispatchPositionForTimestamp(this.timeline.timestamp, [
2627 positionListener
2628 ]);
2629 }
2630 removePositionListener(positionListener) {
2631 this.positionListeners.delete(positionListener);
2632 }
2633 onTimelineTimestampChange(timestamp) {
2634 this.dispatchPositionForTimestamp(timestamp);
2635 }
2636 dispatchPositionForTimestamp(timestamp, listeners = this.positionListeners) {
2637 var _a;
2638 let position;
2639 if (this.indexer.timestampToPosition) {
2640 position = this.indexer.timestampToPosition(timestamp, this.startState);
2641 } else {
2642 const idx = this.indexer.timestampToIndex(timestamp);
2643 const state = this.indexer.stateAtIndex(idx, this.startState);
2644 position = {
2645 state,
2646 movesInProgress: []
2647 };
2648 if (this.indexer.numMoves() > 0) {
2649 const move = (_a = this.indexer.getMove(idx)) == null ? void 0 : _a.as(Move);
2650 if (!move) {
2651 return;
2652 }
2653 const fraction = (timestamp - this.indexer.indexToMoveStartTimestamp(idx)) / this.indexer.moveDuration(idx);
2654 if (fraction === 1) {
2655 position.state = this.ksolvePuzzle.combine(state, this.ksolvePuzzle.stateFromMove(move));
2656 } else if (fraction > 0) {
2657 if (move) {
2658 position.movesInProgress.push({
2659 move,
2660 direction: Direction.Forwards,
2661 fraction
2662 });
2663 }
2664 }
2665 }
2666 }
2667 for (const listener of listeners) {
2668 listener.onPositionChange(position);
2669 }
2670 }
2671 onTimeRangeChange(_timeRange) {
2672 }
2673 setAlg(alg, indexerConstructor) {
2674 indexerConstructor != null ? indexerConstructor : indexerConstructor = this.indexerConstructor;
2675 if (alg.isIdentical(this.alg) && this.indexerConstructor === indexerConstructor) {
2676 return;
2677 }
2678 this.indexerConstructor = indexerConstructor;
2679 this.alg = alg;
2680 this.instantiateIndexer(alg);
2681 this.timeline.onCursorChange(this);
2682 this.dispatchPositionForTimestamp(this.timeline.timestamp);
2683 }
2684 moveBoundary(timestamp, direction) {
2685 if (this.indexer.numMoves() === 0) {
2686 return null;
2687 }
2688 const offsetHack = directionScalar(direction) * 1e-3;
2689 const idx = this.indexer.timestampToIndex(timestamp + offsetHack);
2690 const moveStart = this.indexer.indexToMoveStartTimestamp(idx);
2691 if (direction === Direction.Backwards) {
2692 return timestamp >= moveStart ? moveStart : null;
2693 } else {
2694 const moveEnd = moveStart + this.indexer.moveDuration(idx);
2695 return timestamp <= moveEnd ? moveEnd : null;
2696 }
2697 }
2698 setPuzzle(def, alg = this.alg, startStateAlg) {
2699 this.ksolvePuzzle = new KPuzzleWrapper(def);
2700 this.def = def;
2701 this.indexer = new this.indexerConstructor(this.ksolvePuzzle, alg);
2702 if (alg !== this.alg) {
2703 this.timeline.onCursorChange(this);
2704 }
2705 this.setStartState(startStateAlg ? this.algToState(startStateAlg) : this.ksolvePuzzle.startState());
2706 this.alg = alg;
2707 }
2708 experimentalTimestampFromIndex(index) {
2709 return this.indexer.indexToMoveStartTimestamp(index);
2710 }
2711 experimentalIndexFromTimestamp(timestamp) {
2712 return this.indexer.timestampToIndex(timestamp);
2713 }
2714 experimentalMoveAtIndex(index) {
2715 return this.indexer.getMove(index);
2716 }
2717};
2718
2719// src/cubing/twisty/animation/indexer/SimpleAlgIndexer.ts
2720var SimpleAlgIndexer = class {
2721 constructor(puzzle, alg) {
2722 this.puzzle = puzzle;
2723 this.durationFn = new AlgDuration(defaultDurationForAmount);
2724 this.moves = new Alg(alg.experimentalExpand());
2725 }
2726 getMove(index) {
2727 return Array.from(this.moves.units())[index];
2728 }
2729 indexToMoveStartTimestamp(index) {
2730 const alg = new Alg(Array.from(this.moves.units()).slice(0, index));
2731 return this.durationFn.traverseAlg(alg);
2732 }
2733 timestampToIndex(timestamp) {
2734 let cumulativeTime = 0;
2735 let i;
2736 for (i = 0; i < this.numMoves(); i++) {
2737 cumulativeTime += this.durationFn.traverseMove(this.getMove(i));
2738 if (cumulativeTime >= timestamp) {
2739 return i;
2740 }
2741 }
2742 return i;
2743 }
2744 stateAtIndex(index) {
2745 return this.puzzle.combine(this.puzzle.startState(), this.transformAtIndex(index));
2746 }
2747 transformAtIndex(index) {
2748 let state = this.puzzle.identity();
2749 for (const move of Array.from(this.moves.units()).slice(0, index)) {
2750 state = this.puzzle.combine(state, this.puzzle.stateFromMove(move));
2751 }
2752 return state;
2753 }
2754 algDuration() {
2755 return this.durationFn.traverseAlg(this.moves);
2756 }
2757 numMoves() {
2758 return countAnimatedMoves(this.moves);
2759 }
2760 moveDuration(index) {
2761 return this.durationFn.traverseMove(this.getMove(index));
2762 }
2763};
2764
2765// src/cubing/twisty/animation/indexer/simultaneous-moves/simul-moves.ts
2766var axisLookup = {
2767 u: "y",
2768 l: "x",
2769 f: "z",
2770 r: "x",
2771 b: "z",
2772 d: "y",
2773 m: "x",
2774 e: "y",
2775 s: "z",
2776 x: "x",
2777 y: "y",
2778 z: "z"
2779};
2780function isSameAxis(move1, move2) {
2781 return axisLookup[move1.family[0].toLowerCase()] === axisLookup[move2.family[0].toLowerCase()];
2782}
2783var LocalSimulMoves = class extends TraversalUp {
2784 traverseAlg(alg) {
2785 const processed = [];
2786 for (const nestedUnit of alg.units()) {
2787 processed.push(this.traverseUnit(nestedUnit));
2788 }
2789 return Array.prototype.concat(...processed);
2790 }
2791 traverseGroupingOnce(alg) {
2792 if (alg.experimentalIsEmpty()) {
2793 return [];
2794 }
2795 for (const unit of alg.units()) {
2796 if (!unit.is(Move))
2797 return this.traverseAlg(alg);
2798 }
2799 const moves = Array.from(alg.units());
2800 let maxSimulDur = defaultDurationForAmount(moves[0].amount);
2801 for (let i = 0; i < moves.length - 1; i++) {
2802 for (let j = 1; j < moves.length; j++) {
2803 if (!isSameAxis(moves[i], moves[j])) {
2804 return this.traverseAlg(alg);
2805 }
2806 }
2807 maxSimulDur = Math.max(maxSimulDur, defaultDurationForAmount(moves[i].amount));
2808 }
2809 const localMovesWithRange = moves.map((blockMove) => {
2810 return {
2811 move: blockMove,
2812 msUntilNext: 0,
2813 duration: maxSimulDur
2814 };
2815 });
2816 localMovesWithRange[localMovesWithRange.length - 1].msUntilNext = maxSimulDur;
2817 return localMovesWithRange;
2818 }
2819 traverseGrouping(grouping) {
2820 const processed = [];
2821 const segmentOnce = grouping.amount > 0 ? grouping.alg : grouping.alg.invert();
2822 for (let i = 0; i < Math.abs(grouping.amount); i++) {
2823 processed.push(this.traverseGroupingOnce(segmentOnce));
2824 }
2825 return Array.prototype.concat(...processed);
2826 }
2827 traverseMove(move) {
2828 const duration = defaultDurationForAmount(move.amount);
2829 return [
2830 {
2831 move,
2832 msUntilNext: duration,
2833 duration
2834 }
2835 ];
2836 }
2837 traverseCommutator(commutator) {
2838 const processed = [];
2839 const segmentsOnce = [
2840 commutator.A,
2841 commutator.B,
2842 commutator.A.invert(),
2843 commutator.B.invert()
2844 ];
2845 for (const segment of segmentsOnce) {
2846 processed.push(this.traverseGroupingOnce(segment));
2847 }
2848 return Array.prototype.concat(...processed);
2849 }
2850 traverseConjugate(conjugate) {
2851 const processed = [];
2852 const segmentsOnce = [
2853 conjugate.A,
2854 conjugate.B,
2855 conjugate.A.invert()
2856 ];
2857 for (const segment of segmentsOnce) {
2858 processed.push(this.traverseGroupingOnce(segment));
2859 }
2860 return Array.prototype.concat(...processed);
2861 }
2862 traversePause(_pause) {
2863 return [];
2864 }
2865 traverseNewline(_newline) {
2866 return [];
2867 }
2868 traverseLineComment(_comment) {
2869 return [];
2870 }
2871};
2872var localSimulMovesInstance = new LocalSimulMoves();
2873var localSimulMoves = localSimulMovesInstance.traverseAlg.bind(localSimulMovesInstance);
2874function simulMoves(a) {
2875 let timestamp = 0;
2876 const l = localSimulMoves(a).map((localSimulMove) => {
2877 const moveWithRange = {
2878 move: localSimulMove.move,
2879 start: timestamp,
2880 end: timestamp + localSimulMove.duration
2881 };
2882 timestamp += localSimulMove.msUntilNext;
2883 return moveWithRange;
2884 });
2885 return l;
2886}
2887
2888// src/cubing/twisty/animation/indexer/simultaneous-moves/SimultaneousMoveIndexer.ts
2889var demos = {
2890 "y' y' U' E D R2 r2 F2 B2 U E D' R2 L2' z2 S2 U U D D S2 F2' B2": [
2891 {move: new Move("y", -1), start: 0, end: 1e3},
2892 {move: new Move("y", -1), start: 1e3, end: 2e3},
2893 {move: new Move("U", -1), start: 1e3, end: 1600},
2894 {move: new Move("E", 1), start: 1200, end: 1800},
2895 {move: new Move("D"), start: 1400, end: 2e3},
2896 {move: new Move("R", 2), start: 2e3, end: 3500},
2897 {move: new Move("r", 2), start: 2e3, end: 3500},
2898 {move: new Move("F", 2), start: 3500, end: 4200},
2899 {move: new Move("B", 2), start: 3800, end: 4500},
2900 {move: new Move("U", 1), start: 4500, end: 5500},
2901 {move: new Move("E", 1), start: 4500, end: 5500},
2902 {move: new Move("D", -1), start: 4500, end: 5500},
2903 {move: new Move("R", 2), start: 5500, end: 6500},
2904 {move: new Move("L", -2), start: 5500, end: 6500},
2905 {move: new Move("z", 2), start: 5500, end: 6500},
2906 {move: new Move("S", 2), start: 6500, end: 7500},
2907 {move: new Move("U"), start: 7500, end: 8e3},
2908 {move: new Move("U"), start: 8e3, end: 8500},
2909 {move: new Move("D"), start: 7750, end: 8250},
2910 {move: new Move("D"), start: 8250, end: 8750},
2911 {move: new Move("S", 2), start: 8750, end: 9250},
2912 {move: new Move("F", -2), start: 8750, end: 1e4},
2913 {move: new Move("B", 2), start: 8750, end: 1e4}
2914 ],
2915 "M' R' U' D' M R": [
2916 {move: new Move("M", -1), start: 0, end: 1e3},
2917 {move: new Move("R", -1), start: 0, end: 1e3},
2918 {move: new Move("U", -1), start: 1e3, end: 2e3},
2919 {move: new Move("D", -1), start: 1e3, end: 2e3},
2920 {move: new Move("M"), start: 2e3, end: 3e3},
2921 {move: new Move("R"), start: 2e3, end: 3e3}
2922 ],
2923 "U' E' r E r2' E r U E": [
2924 {move: new Move("U", -1), start: 0, end: 1e3},
2925 {move: new Move("E", -1), start: 0, end: 1e3},
2926 {move: new Move("r"), start: 1e3, end: 2500},
2927 {move: new Move("E"), start: 2500, end: 3500},
2928 {move: new Move("r", -2), start: 3500, end: 5e3},
2929 {move: new Move("E"), start: 5e3, end: 6e3},
2930 {move: new Move("r"), start: 6e3, end: 7e3},
2931 {move: new Move("U"), start: 7e3, end: 8e3},
2932 {move: new Move("E"), start: 7e3, end: 8e3}
2933 ]
2934};
2935var SimultaneousMoveIndexer = class {
2936 constructor(puzzle, alg) {
2937 this.puzzle = puzzle;
2938 var _a;
2939 this.moves = (_a = demos[alg.toString()]) != null ? _a : simulMoves(alg);
2940 }
2941 getMove(index) {
2942 return this.moves[Math.min(index, this.moves.length - 1)].move;
2943 }
2944 getMoveWithRange(index) {
2945 return this.moves[Math.min(index, this.moves.length - 1)];
2946 }
2947 indexToMoveStartTimestamp(index) {
2948 let start = 0;
2949 if (this.moves.length > 0) {
2950 start = this.moves[Math.min(index, this.moves.length - 1)].start;
2951 }
2952 return start;
2953 }
2954 timestampToIndex(timestamp) {
2955 let i = 0;
2956 for (i = 0; i < this.moves.length; i++) {
2957 if (this.moves[i].start >= timestamp) {
2958 return Math.max(0, i - 1);
2959 }
2960 }
2961 return Math.max(0, i - 1);
2962 }
2963 timestampToPosition(timestamp, startTransformation) {
2964 const position = {
2965 state: startTransformation != null ? startTransformation : this.puzzle.identity(),
2966 movesInProgress: []
2967 };
2968 for (const moveWithRange of this.moves) {
2969 if (moveWithRange.end <= timestamp) {
2970 position.state = this.puzzle.combine(position.state, this.puzzle.stateFromMove(moveWithRange.move));
2971 } else if (moveWithRange.start < timestamp && timestamp < moveWithRange.end) {
2972 position.movesInProgress.push({
2973 move: moveWithRange.move,
2974 direction: Direction.Forwards,
2975 fraction: (timestamp - moveWithRange.start) / (moveWithRange.end - moveWithRange.start)
2976 });
2977 } else if (timestamp < moveWithRange.start) {
2978 continue;
2979 }
2980 }
2981 return position;
2982 }
2983 stateAtIndex(index, startTransformation) {
2984 let state = startTransformation != null ? startTransformation : this.puzzle.startState();
2985 for (let i = 0; i < this.moves.length && i < index; i++) {
2986 const moveWithRange = this.moves[i];
2987 state = this.puzzle.combine(state, this.puzzle.stateFromMove(moveWithRange.move));
2988 }
2989 return state;
2990 }
2991 transformAtIndex(index) {
2992 let state = this.puzzle.identity();
2993 for (const moveWithRange of this.moves.slice(0, index)) {
2994 state = this.puzzle.combine(state, this.puzzle.stateFromMove(moveWithRange.move));
2995 }
2996 return state;
2997 }
2998 algDuration() {
2999 let max = 0;
3000 for (const moveWithRange of this.moves) {
3001 max = Math.max(max, moveWithRange.end);
3002 }
3003 return max;
3004 }
3005 numMoves() {
3006 return this.moves.length;
3007 }
3008 moveDuration(index) {
3009 const move = this.getMoveWithRange(index);
3010 return move.end - move.start;
3011 }
3012};
3013
3014// src/cubing/twisty/animation/Timeline.ts
3015var PAUSE_ON_JUMP = true;
3016var TimelineAction;
3017(function(TimelineAction2) {
3018 TimelineAction2["StartingToPlay"] = "StartingToPlay";
3019 TimelineAction2["Pausing"] = "Pausing";
3020 TimelineAction2["Jumping"] = "Jumping";
3021})(TimelineAction || (TimelineAction = {}));
3022var TimestampLocationType;
3023(function(TimestampLocationType2) {
3024 TimestampLocationType2["StartOfTimeline"] = "Start";
3025 TimestampLocationType2["EndOfTimeline"] = "End";
3026 TimestampLocationType2["StartOfMove"] = "StartOfMove";
3027 TimestampLocationType2["EndOfMove"] = "EndOfMove";
3028 TimestampLocationType2["MiddleOfMove"] = "MiddleOfMove";
3029 TimestampLocationType2["BetweenMoves"] = "BetweenMoves";
3030})(TimestampLocationType || (TimestampLocationType = {}));
3031function getNow() {
3032 return Math.round(performance.now());
3033}
3034var Timeline = class {
3035 constructor() {
3036 this.animating = false;
3037 this.tempoScale = 1;
3038 this.cursors = new Set();
3039 this.timestampListeners = new Set();
3040 this.actionListeners = new Set();
3041 this.timestamp = 0;
3042 this.lastAnimFrameNow = 0;
3043 this.direction = Direction.Forwards;
3044 this.boundaryType = BoundaryType.EntireTimeline;
3045 const animFrame = (_now) => {
3046 if (this.animating) {
3047 const now = getNow();
3048 this.timestamp = this.timestamp + this.tempoScale * directionScalar(this.direction) * (now - this.lastAnimFrameNow);
3049 this.lastAnimFrameNow = now;
3050 const atOrPastBoundary = this.direction === Direction.Backwards ? this.timestamp <= this.cachedNextBoundary : this.timestamp >= this.cachedNextBoundary;
3051 if (atOrPastBoundary) {
3052 this.timestamp = this.cachedNextBoundary;
3053 if (this.animating) {
3054 this.animating = false;
3055 this.dispatchAction(TimelineAction.Pausing);
3056 }
3057 }
3058 }
3059 if (this.timestamp !== this.lastAnimFrameTimestamp) {
3060 this.dispatchTimestamp();
3061 this.lastAnimFrameTimestamp = this.timestamp;
3062 }
3063 if (this.animating) {
3064 this.scheduler.requestAnimFrame();
3065 }
3066 };
3067 this.scheduler = new RenderScheduler(animFrame);
3068 }
3069 addCursor(cursor) {
3070 this.cursors.add(cursor);
3071 this.dispatchTimeRange();
3072 }
3073 removeCursor(cursor) {
3074 this.cursors.delete(cursor);
3075 this.clampTimestampToRange();
3076 this.dispatchTimeRange();
3077 }
3078 clampTimestampToRange() {
3079 const timeRange = this.timeRange();
3080 if (this.timestamp < timeRange.start) {
3081 this.setTimestamp(timeRange.start);
3082 }
3083 if (this.timestamp > timeRange.end) {
3084 this.setTimestamp(timeRange.end);
3085 }
3086 }
3087 onCursorChange(_cursor) {
3088 if (this.timestamp > this.maxTimestamp()) {
3089 this.timestamp = this.maxTimestamp();
3090 }
3091 this.dispatchTimeRange();
3092 }
3093 timeRange() {
3094 let start = 0;
3095 let end = 0;
3096 for (const cursor of this.cursors) {
3097 const cursorTimeRange = cursor.timeRange();
3098 start = Math.min(start, cursorTimeRange.start);
3099 end = Math.max(end, cursorTimeRange.end);
3100 }
3101 return {start, end};
3102 }
3103 minTimestamp() {
3104 return this.timeRange().start;
3105 }
3106 maxTimestamp() {
3107 return this.timeRange().end;
3108 }
3109 dispatchTimeRange() {
3110 const timeRange = this.timeRange();
3111 for (const listener of this.cursors) {
3112 listener.onTimeRangeChange(timeRange);
3113 }
3114 for (const listener of this.timestampListeners) {
3115 listener.onTimeRangeChange(timeRange);
3116 }
3117 }
3118 dispatchTimestamp() {
3119 for (const listener of this.cursors) {
3120 listener.onTimelineTimestampChange(this.timestamp);
3121 }
3122 for (const listener of this.timestampListeners) {
3123 listener.onTimelineTimestampChange(this.timestamp);
3124 }
3125 }
3126 addTimestampListener(timestampListener) {
3127 this.timestampListeners.add(timestampListener);
3128 }
3129 removeTimestampListener(timestampListener) {
3130 this.timestampListeners.delete(timestampListener);
3131 }
3132 addActionListener(actionListener) {
3133 this.actionListeners.add(actionListener);
3134 }
3135 removeActionListener(actionListener) {
3136 this.actionListeners.delete(actionListener);
3137 }
3138 play() {
3139 this.experimentalPlay(Direction.Forwards, BoundaryType.EntireTimeline);
3140 }
3141 experimentalPlay(direction, boundaryType = BoundaryType.EntireTimeline) {
3142 this.direction = direction;
3143 this.boundaryType = boundaryType;
3144 const nextBoundary = this.nextBoundary(this.timestamp, direction, this.boundaryType);
3145 if (nextBoundary === null) {
3146 return;
3147 }
3148 this.cachedNextBoundary = nextBoundary;
3149 if (!this.animating) {
3150 this.animating = true;
3151 this.lastAnimFrameNow = getNow();
3152 this.dispatchAction(TimelineAction.StartingToPlay);
3153 this.scheduler.requestAnimFrame();
3154 }
3155 }
3156 nextBoundary(timestamp, direction, boundaryType = BoundaryType.EntireTimeline) {
3157 switch (boundaryType) {
3158 case BoundaryType.EntireTimeline: {
3159 switch (direction) {
3160 case Direction.Backwards:
3161 return timestamp <= this.minTimestamp() ? null : this.minTimestamp();
3162 case Direction.Forwards:
3163 return timestamp >= this.maxTimestamp() ? null : this.maxTimestamp();
3164 default:
3165 throw new Error("invalid direction");
3166 }
3167 }
3168 case BoundaryType.Move: {
3169 let result = null;
3170 for (const cursor of this.cursors) {
3171 const boundaryTimestamp = cursor.moveBoundary(timestamp, direction);
3172 if (boundaryTimestamp !== null) {
3173 switch (direction) {
3174 case Direction.Backwards: {
3175 result = Math.min(result != null ? result : boundaryTimestamp, boundaryTimestamp);
3176 break;
3177 }
3178 case Direction.Forwards: {
3179 result = Math.max(result != null ? result : boundaryTimestamp, boundaryTimestamp);
3180 break;
3181 }
3182 default:
3183 throw new Error("invalid direction");
3184 }
3185 }
3186 }
3187 return result;
3188 }
3189 default:
3190 throw new Error("invalid boundary type");
3191 }
3192 }
3193 pause() {
3194 if (this.animating) {
3195 this.animating = false;
3196 this.dispatchAction(TimelineAction.Pausing);
3197 this.scheduler.requestAnimFrame();
3198 }
3199 }
3200 playPause() {
3201 if (this.animating) {
3202 this.pause();
3203 } else {
3204 if (this.timestamp >= this.maxTimestamp()) {
3205 this.timestamp = 0;
3206 }
3207 this.experimentalPlay(Direction.Forwards, BoundaryType.EntireTimeline);
3208 }
3209 }
3210 setTimestamp(timestamp) {
3211 const oldTimestamp = this.timestamp;
3212 this.timestamp = timestamp;
3213 this.lastAnimFrameNow = getNow();
3214 if (oldTimestamp !== timestamp) {
3215 this.dispatchAction(TimelineAction.Jumping);
3216 this.scheduler.requestAnimFrame();
3217 }
3218 if (PAUSE_ON_JUMP) {
3219 this.animating = false;
3220 this.dispatchAction(TimelineAction.Pausing);
3221 }
3222 }
3223 jumpToStart() {
3224 this.setTimestamp(this.minTimestamp());
3225 }
3226 jumpToEnd() {
3227 this.setTimestamp(this.maxTimestamp());
3228 }
3229 experimentalJumpToLastMove() {
3230 var _a;
3231 let max = 0;
3232 for (const cursor of this.cursors) {
3233 max = Math.max(max, (_a = cursor.experimentalTimestampForStartOfLastMove()) != null ? _a : 0);
3234 }
3235 this.setTimestamp(max);
3236 }
3237 dispatchAction(event) {
3238 let locationType = TimestampLocationType.MiddleOfMove;
3239 switch (this.timestamp) {
3240 case this.minTimestamp():
3241 locationType = TimestampLocationType.StartOfTimeline;
3242 break;
3243 case this.maxTimestamp():
3244 locationType = TimestampLocationType.EndOfTimeline;
3245 break;
3246 }
3247 const actionEvent = {
3248 action: event,
3249 locationType
3250 };
3251 for (const listener of this.actionListeners) {
3252 listener.onTimelineAction(actionEvent);
3253 }
3254 }
3255};
3256
3257// src/cubing/twisty/dom/controls/buttons.css_.ts
3258var buttonGridCSS = new CSSSource(`
3259:host {
3260 width: 384px;
3261 height: 24px;
3262 display: grid;
3263}
3264
3265.wrapper {
3266 width: 100%;
3267 height: 100%;
3268 display: grid;
3269 overflow: hidden;
3270 backdrop-filter: blur(4px);
3271 -webkit-backdrop-filter: blur(4px);
3272}
3273
3274.wrapper {
3275 grid-auto-flow: column;
3276}
3277
3278.viewer-link-none .twizzle-link-button {
3279 display: none;
3280}
3281
3282.wrapper twisty-control-button {
3283 width: inherit;
3284 height: inherit;
3285}
3286`);
3287var buttonCSS = new CSSSource(`
3288:host {
3289 width: 48px;
3290 height: 24px;
3291 display: grid;
3292}
3293
3294.wrapper {
3295 width: 100%;
3296 height: 100%;
3297}
3298
3299button {
3300 width: 100%;
3301 height: 100%;
3302 border: none;
3303
3304 background-position: center;
3305 background-repeat: no-repeat;
3306 background-size: contain;
3307
3308 background-color: rgba(196, 196, 196, 0.75);
3309}
3310
3311button:enabled {
3312 background-color: rgba(196, 196, 196, 0.75)
3313}
3314
3315button:disabled {
3316 background-color: rgba(0, 0, 0, 0.4);
3317 opacity: 0.25;
3318 pointer-events: none;
3319}
3320
3321button:enabled:hover {
3322 background-color: rgba(255, 255, 255, 0.75);
3323 box-shadow: 0 0 1em rgba(0, 0, 0, 0.25);
3324 cursor: pointer;
3325}
3326
3327/* TODO: fullscreen icons have too much padding?? */
3328button.svg-skip-to-start {
3329 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTg0IiBoZWlnaHQ9IjM1ODQiIHZpZXdCb3g9IjAgMCAzNTg0IDM1ODQiPjxwYXRoIGQ9Ik0yNjQzIDEwMzdxMTktMTkgMzItMTN0MTMgMzJ2MTQ3MnEwIDI2LTEzIDMydC0zMi0xM2wtNzEwLTcxMHEtOS05LTEzLTE5djcxMHEwIDI2LTEzIDMydC0zMi0xM2wtNzEwLTcxMHEtOS05LTEzLTE5djY3OHEwIDI2LTE5IDQ1dC00NSAxOUg5NjBxLTI2IDAtNDUtMTl0LTE5LTQ1VjEwODhxMC0yNiAxOS00NXQ0NS0xOWgxMjhxMjYgMCA0NSAxOXQxOSA0NXY2NzhxNC0xMSAxMy0xOWw3MTAtNzEwcTE5LTE5IDMyLTEzdDEzIDMydjcxMHE0LTExIDEzLTE5eiIvPjwvc3ZnPg==");
3330}
3331
3332button.svg-skip-to-end {
3333 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTg0IiBoZWlnaHQ9IjM1ODQiIHZpZXdCb3g9IjAgMCAzNTg0IDM1ODQiPjxwYXRoIGQ9Ik05NDEgMjU0N3EtMTkgMTktMzIgMTN0LTEzLTMyVjEwNTZxMC0yNiAxMy0zMnQzMiAxM2w3MTAgNzEwcTggOCAxMyAxOXYtNzEwcTAtMjYgMTMtMzJ0MzIgMTNsNzEwIDcxMHE4IDggMTMgMTl2LTY3OHEwLTI2IDE5LTQ1dDQ1LTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1djE0MDhxMCAyNi0xOSA0NXQtNDUgMTloLTEyOHEtMjYgMC00NS0xOXQtMTktNDV2LTY3OHEtNSAxMC0xMyAxOWwtNzEwIDcxMHEtMTkgMTktMzIgMTN0LTEzLTMydi03MTBxLTUgMTAtMTMgMTl6Ii8+PC9zdmc+");
3334}
3335
3336button.svg-step-forward {
3337 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTg0IiBoZWlnaHQ9IjM1ODQiIHZpZXdCb3g9IjAgMCAzNTg0IDM1ODQiPjxwYXRoIGQ9Ik0yNjg4IDE1NjhxMCAyNi0xOSA0NWwtNTEyIDUxMnEtMTkgMTktNDUgMTl0LTQ1LTE5cS0xOS0xOS0xOS00NXYtMjU2aC0yMjRxLTk4IDAtMTc1LjUgNnQtMTU0IDIxLjVxLTc2LjUgMTUuNS0xMzMgNDIuNXQtMTA1LjUgNjkuNXEtNDkgNDIuNS04MCAxMDF0LTQ4LjUgMTM4LjVxLTE3LjUgODAtMTcuNSAxODEgMCA1NSA1IDEyMyAwIDYgMi41IDIzLjV0Mi41IDI2LjVxMCAxNS04LjUgMjV0LTIzLjUgMTBxLTE2IDAtMjgtMTctNy05LTEzLTIydC0xMy41LTMwcS03LjUtMTctMTAuNS0yNC0xMjctMjg1LTEyNy00NTEgMC0xOTkgNTMtMzMzIDE2Mi00MDMgODc1LTQwM2gyMjR2LTI1NnEwLTI2IDE5LTQ1dDQ1LTE5cTI2IDAgNDUgMTlsNTEyIDUxMnExOSAxOSAxOSA0NXoiLz48L3N2Zz4=");
3338}
3339
3340button.svg-step-backward {
3341 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTg0IiBoZWlnaHQ9IjM1ODQiIHZpZXdCb3g9IjAgMCAzNTg0IDM1ODQiPjxwYXRoIGQ9Ik0yNjg4IDIwNDhxMCAxNjYtMTI3IDQ1MS0zIDctMTAuNSAyNHQtMTMuNSAzMHEtNiAxMy0xMyAyMi0xMiAxNy0yOCAxNy0xNSAwLTIzLjUtMTB0LTguNS0yNXEwLTkgMi41LTI2LjV0Mi41LTIzLjVxNS02OCA1LTEyMyAwLTEwMS0xNy41LTE4MXQtNDguNS0xMzguNXEtMzEtNTguNS04MC0xMDF0LTEwNS41LTY5LjVxLTU2LjUtMjctMTMzLTQyLjV0LTE1NC0yMS41cS03Ny41LTYtMTc1LjUtNmgtMjI0djI1NnEwIDI2LTE5IDQ1dC00NSAxOXEtMjYgMC00NS0xOWwtNTEyLTUxMnEtMTktMTktMTktNDV0MTktNDVsNTEyLTUxMnExOS0xOSA0NS0xOXQ0NSAxOXExOSAxOSAxOSA0NXYyNTZoMjI0cTcxMyAwIDg3NSA0MDMgNTMgMTM0IDUzIDMzM3oiLz48L3N2Zz4=");
3342}
3343
3344button.svg-pause {
3345 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTg0IiBoZWlnaHQ9IjM1ODQiIHZpZXdCb3g9IjAgMCAzNTg0IDM1ODQiPjxwYXRoIGQ9Ik0yNTYwIDEwODh2MTQwOHEwIDI2LTE5IDQ1dC00NSAxOWgtNTEycS0yNiAwLTQ1LTE5dC0xOS00NVYxMDg4cTAtMjYgMTktNDV0NDUtMTloNTEycTI2IDAgNDUgMTl0MTkgNDV6bS04OTYgMHYxNDA4cTAgMjYtMTkgNDV0LTQ1IDE5aC01MTJxLTI2IDAtNDUtMTl0LTE5LTQ1VjEwODhxMC0yNiAxOS00NXQ0NS0xOWg1MTJxMjYgMCA0NSAxOXQxOSA0NXoiLz48L3N2Zz4=");
3346}
3347
3348button.svg-play {
3349 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTg0IiBoZWlnaHQ9IjM1ODQiIHZpZXdCb3g9IjAgMCAzNTg0IDM1ODQiPjxwYXRoIGQ9Ik0yNDcyLjUgMTgyM2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNlYxMDU2cTAtMjYgMTYuNS0zNnQzOS41IDNsMTMyOCA3MzhxMjMgMTMgMjMgMzF0LTIzIDMxeiIvPjwvc3ZnPg==");
3350}
3351
3352button.svg-enter-fullscreen {
3353 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjgiIHZpZXdCb3g9IjAgMCAyOCAyOCIgd2lkdGg9IjI4Ij48cGF0aCBkPSJNMiAyaDI0djI0SDJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTZIN3Y1aDV2LTJIOXYtM3ptLTItNGgyVjloM1Y3SDd2NXptMTIgN2gtM3YyaDV2LTVoLTJ2M3pNMTYgN3YyaDN2M2gyVjdoLTV6Ii8+PC9zdmc+");
3354}
3355
3356button.svg-exit-fullscreen {
3357 background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjgiIHZpZXdCb3g9IjAgMCAyOCAyOCIgd2lkdGg9IjI4Ij48cGF0aCBkPSJNMiAyaDI0djI0SDJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTcgMThoM3YzaDJ2LTVIN3Yyem0zLThIN3YyaDVWN2gtMnYzem02IDExaDJ2LTNoM3YtMmgtNXY1em0yLTExVjdoLTJ2NWg1di0yaC0zeiIvPjwvc3ZnPg==");
3358}
3359
3360button.svg-twizzle-tw {
3361 background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODY0IiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMzk3LjU4MSAxNTEuMTh2NTcuMDg0aC04OS43MDN2MjQwLjM1MmgtNjYuOTU1VjIwOC4yNjRIMTUxLjIydi01Ny4wODNoMjQ2LjM2MXptNTQuMzEgNzEuNjc3bDcuNTEyIDMzLjY5MmMyLjcxOCAxMi4xNiA1LjU4IDI0LjY4IDguNTg0IDM3LjU1NWEyMTgwLjc3NSAyMTgwLjc3NSAwIDAwOS40NDIgMzguODQzIDEyNjYuMyAxMjY2LjMgMCAwMDEwLjA4NiAzNy41NTVjMy43Mi0xMi41OSA3LjM2OC0yNS40NjYgMTAuOTQ1LTM4LjYyOCAzLjU3Ni0xMy4xNjIgNy4wMS0yNi4xMSAxMC4zLTM4Ljg0M2w1Ljc2OS0yMi40NTZjMS4yNDgtNC44ODcgMi40NzItOS43MDUgMy42NzQtMTQuNDU1IDMuMDA0LTExLjg3NSA1LjY1MS0yMi45NjIgNy45NC0zMy4yNjNoNDYuMzU0bDIuMzg0IDEwLjU2M2EyMDAwLjc3IDIwMDAuNzcgMCAwMDMuOTM1IDE2LjgyOGw2LjcxMSAyNy43MWMxLjIxMyA0Ljk1NiAyLjQ1IDkuOTggMy43MDkgMTUuMDczYTMxMTkuNzc3IDMxMTkuNzc3IDAgMDA5Ljg3MSAzOC44NDMgMTI0OS4yMjcgMTI0OS4yMjcgMCAwMDEwLjczIDM4LjYyOCAxOTA3LjYwNSAxOTA3LjYwNSAwIDAwMTAuMzAxLTM3LjU1NSAxMzk3Ljk0IDEzOTcuOTQgMCAwMDkuNjU3LTM4Ljg0M2w0LjQtMTkuMDQ2Yy43MTUtMy4xMyAxLjQyMS02LjIzNiAyLjExOC05LjMyMWw5LjU3Ny00Mi44OGg2Ni41MjZhMjk4OC43MTggMjk4OC43MTggMCAwMS0xOS41MjkgNjYuMzExbC01LjcyOCAxOC40ODJhMzIzNy40NiAzMjM3LjQ2IDAgMDEtMTQuMDE1IDQzLjc1MmMtNi40MzggMTkuNi0xMi43MzMgMzcuNjk4LTE4Ljg4NSA1NC4yOTRsLTMuMzA2IDguODI1Yy00Ljg4NCAxMi44OTgtOS40MzMgMjQuMjYzLTEzLjY0NyAzNC4wOTVoLTQ5Ljc4N2E4NDE3LjI4OSA4NDE3LjI4OSAwIDAxLTIxLjAzMS02NC44MDkgMTI4OC42ODYgMTI4OC42ODYgMCAwMS0xOC44ODUtNjQuODEgMTk3Mi40NDQgMTk3Mi40NDQgMCAwMS0xOC4yNCA2NC44MSAyNTc5LjQxMiAyNTc5LjQxMiAwIDAxLTIwLjM4OCA2NC44MWgtNDkuNzg3Yy00LjY4Mi0xMC45MjYtOS43Mi0yMy43NDMtMTUuMTEtMzguNDUxbC0xLjYyOS00LjQ3Yy01LjI1OC0xNC41MjEtMTAuNjgtMzAuMTkyLTE2LjI2Ni00Ny4wMTRsLTIuNDA0LTcuMjhjLTYuNDM4LTE5LjYtMTMuMDItNDAuMzQ0LTE5Ljc0My02Mi4yMzRhMjk4OC43MDcgMjk4OC43MDcgMCAwMS0xOS41MjktNjYuMzExaDY3LjM4NXoiIGZpbGw9IiM0Mjg1RjQiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==");
3362}
3363`);
3364
3365// src/cubing/twisty/dom/controls/buttons.ts
3366var TwistyControlButton = class extends ManagedCustomElement {
3367 constructor(timeline, timelineCommand, options) {
3368 super();
3369 this.currentIconName = null;
3370 this.button = document.createElement("button");
3371 this.fullscreenElement = null;
3372 this.visitTwizzleLinkCallback = null;
3373 var _a, _b;
3374 this.fullscreenElement = (_a = options == null ? void 0 : options.fullscreenElement) != null ? _a : null;
3375 this.visitTwizzleLinkCallback = (_b = options == null ? void 0 : options.visitTwizzleLinkCallback) != null ? _b : null;
3376 if (!timeline) {
3377 console.warn("Must have timeline!");
3378 }
3379 this.timeline = timeline;
3380 if (!timelineCommand) {
3381 console.warn("Must have timelineCommand!");
3382 }
3383 this.timelineCommand = timelineCommand;
3384 this.addCSS(buttonCSS);
3385 this.setIcon(this.initialIcon());
3386 this.setHoverTitle(this.initialHoverTitle());
3387 this.addElement(this.button);
3388 this.addEventListener("click", this.onPress.bind(this));
3389 switch (this.timelineCommand) {
3390 case "fullscreen":
3391 if (!document.fullscreenEnabled) {
3392 this.button.disabled = true;
3393 }
3394 break;
3395 case "jump-to-start":
3396 case "play-step-backwards":
3397 this.button.disabled = true;
3398 break;
3399 }
3400 if (this.timeline) {
3401 this.timeline.addActionListener(this);
3402 switch (this.timelineCommand) {
3403 case "play-pause":
3404 case "play-step-backwards":
3405 case "play-step":
3406 this.timeline.addTimestampListener(this);
3407 break;
3408 }
3409 this.autoSetTimelineBasedDisabled();
3410 }
3411 }
3412 autoSetTimelineBasedDisabled() {
3413 switch (this.timelineCommand) {
3414 case "jump-to-start":
3415 case "play-pause":
3416 case "play-step-backwards":
3417 case "play-step":
3418 case "jump-to-end": {
3419 const timeRange = this.timeline.timeRange();
3420 if (timeRange.start === timeRange.end) {
3421 this.button.disabled = true;
3422 return;
3423 }
3424 switch (this.timelineCommand) {
3425 case "jump-to-start":
3426 case "play-step-backwards":
3427 this.button.disabled = this.timeline.timestamp < this.timeline.maxTimestamp();
3428 break;
3429 case "jump-to-end":
3430 case "play-step":
3431 this.button.disabled = this.timeline.timestamp > this.timeline.minTimestamp();
3432 break;
3433 default:
3434 this.button.disabled = false;
3435 }
3436 break;
3437 }
3438 }
3439 }
3440 setIcon(buttonIconName) {
3441 if (this.currentIconName === buttonIconName) {
3442 return;
3443 }
3444 if (this.currentIconName) {
3445 this.button.classList.remove(`svg-${this.currentIconName}`);
3446 }
3447 this.button.classList.add(`svg-${buttonIconName}`);
3448 this.currentIconName = buttonIconName;
3449 }
3450 initialIcon() {
3451 const map = {
3452 "jump-to-start": "skip-to-start",
3453 "play-pause": "play",
3454 "play-step": "step-forward",
3455 "play-step-backwards": "step-backward",
3456 "jump-to-end": "skip-to-end",
3457 fullscreen: "enter-fullscreen",
3458 "twizzle-link": "twizzle-tw"
3459 };
3460 return map[this.timelineCommand];
3461 }
3462 initialHoverTitle() {
3463 const map = {
3464 "jump-to-start": "Restart",
3465 "play-pause": "Play",
3466 "play-step": "Step forward",
3467 "play-step-backwards": "Step backward",
3468 "jump-to-end": "Skip to End",
3469 fullscreen: "Enter fullscreen",
3470 "twizzle-link": "View at Twizzle"
3471 };
3472 return map[this.timelineCommand];
3473 }
3474 setHoverTitle(title) {
3475 this.button.title = title;
3476 }
3477 onPress() {
3478 switch (this.timelineCommand) {
3479 case "fullscreen":
3480 if (document.fullscreenElement === this.fullscreenElement) {
3481 document.exitFullscreen();
3482 } else {
3483 this.setIcon("exit-fullscreen");
3484 this.fullscreenElement.requestFullscreen().then(() => {
3485 const onFullscreen = () => {
3486 if (document.fullscreenElement !== this.fullscreenElement) {
3487 this.setIcon("enter-fullscreen");
3488 window.removeEventListener("fullscreenchange", onFullscreen);
3489 }
3490 };
3491 window.addEventListener("fullscreenchange", onFullscreen);
3492 });
3493 }
3494 break;
3495 case "jump-to-start":
3496 this.timeline.setTimestamp(0);
3497 break;
3498 case "jump-to-end":
3499 this.timeline.jumpToEnd();
3500 break;
3501 case "play-pause":
3502 this.timeline.playPause();
3503 break;
3504 case "play-step":
3505 this.timeline.experimentalPlay(Direction.Forwards, BoundaryType.Move);
3506 break;
3507 case "play-step-backwards":
3508 this.timeline.experimentalPlay(Direction.Backwards, BoundaryType.Move);
3509 break;
3510 case "twizzle-link":
3511 if (this.visitTwizzleLinkCallback) {
3512 this.visitTwizzleLinkCallback();
3513 }
3514 break;
3515 }
3516 }
3517 onTimelineAction(actionEvent) {
3518 switch (this.timelineCommand) {
3519 case "jump-to-start":
3520 this.button.disabled = actionEvent.locationType === TimestampLocationType.StartOfTimeline && actionEvent.action !== TimelineAction.StartingToPlay;
3521 break;
3522 case "jump-to-end":
3523 this.button.disabled = actionEvent.locationType === TimestampLocationType.EndOfTimeline && actionEvent.action !== TimelineAction.StartingToPlay;
3524 break;
3525 case "play-pause":
3526 switch (actionEvent.action) {
3527 case TimelineAction.Pausing:
3528 this.setIcon("play");
3529 this.setHoverTitle("Play");
3530 break;
3531 case TimelineAction.StartingToPlay:
3532 this.setIcon("pause");
3533 this.setHoverTitle("Pause");
3534 break;
3535 }
3536 break;
3537 case "play-step":
3538 this.button.disabled = actionEvent.locationType === TimestampLocationType.EndOfTimeline && actionEvent.action !== TimelineAction.StartingToPlay;
3539 break;
3540 case "play-step-backwards":
3541 this.button.disabled = actionEvent.locationType === TimestampLocationType.StartOfTimeline && actionEvent.action !== TimelineAction.StartingToPlay;
3542 break;
3543 }
3544 }
3545 onTimelineTimestampChange(_timestamp) {
3546 }
3547 onTimeRangeChange(_timeRange) {
3548 this.autoSetTimelineBasedDisabled();
3549 }
3550};
3551customElementsShim.define("twisty-control-button", TwistyControlButton);
3552var _viewerLinkClassListManager;
3553var TwistyControlButtonPanel = class extends ManagedCustomElement {
3554 constructor(timeline, options) {
3555 super();
3556 _viewerLinkClassListManager.set(this, new ClassListManager(this, "viewer-link-", ["none", "twizzle"]));
3557 var _a;
3558 this.addCSS(buttonGridCSS);
3559 __privateGet(this, _viewerLinkClassListManager).setValue((_a = options == null ? void 0 : options.viewerLink) != null ? _a : "none");
3560 this.addElement(new TwistyControlButton(timeline, "fullscreen", {
3561 fullscreenElement: options == null ? void 0 : options.fullscreenElement
3562 }));
3563 this.addElement(new TwistyControlButton(timeline, "jump-to-start"));
3564 this.addElement(new TwistyControlButton(timeline, "play-step-backwards"));
3565 this.addElement(new TwistyControlButton(timeline, "play-pause"));
3566 this.addElement(new TwistyControlButton(timeline, "play-step"));
3567 this.addElement(new TwistyControlButton(timeline, "jump-to-end"));
3568 this.addElement(new TwistyControlButton(timeline, "twizzle-link", {
3569 visitTwizzleLinkCallback: options == null ? void 0 : options.viewerLinkCallback
3570 })).classList.add("twizzle-link-button");
3571 }
3572 setViewerLink(viewerLink) {
3573 __privateGet(this, _viewerLinkClassListManager).setValue(viewerLink);
3574 }
3575};
3576_viewerLinkClassListManager = new WeakMap();
3577customElementsShim.define("twisty-control-button-panel", TwistyControlButtonPanel);
3578
3579// src/cubing/twisty/dom/controls/TwistyScrubber.css_.ts
3580var twistyScrubberCSS = new CSSSource(`
3581:host {
3582 width: 384px;
3583 height: 16px;
3584 display: grid;
3585}
3586
3587.wrapper {
3588 width: 100%;
3589 height: 100%;
3590 display: grid;
3591 overflow: hidden;
3592 backdrop-filter: blur(4px);
3593 -webkit-backdrop-filter: blur(4px);
3594}
3595
3596input {
3597 margin: 0; width: 100%;
3598}
3599
3600input {
3601 background: none;
3602}
3603
3604::-moz-range-track {
3605 background: rgba(0, 0, 0, 0.25);
3606 height: 50%;
3607 border: 1px solid rgba(0, 0, 0, 0.1);
3608}
3609
3610::-webkit-slider-runnable-track {
3611 background: rgba(0, 0, 0, 0.05);
3612}
3613
3614::-moz-range-progress {
3615 background: #3273F6;
3616 height: 50%;
3617 border: 1px solid rgba(0, 0, 0, 0.1);
3618}
3619
3620::-ms-fill-lower {
3621 background: #3273F6;
3622 height: 50%;
3623 border: 1px solid rgba(0, 0, 0, 0.1);
3624}
3625`);
3626
3627// src/cubing/twisty/dom/controls/TwistyScrubber.ts
3628var TwistyScrubber = class extends ManagedCustomElement {
3629 constructor(timeline) {
3630 super();
3631 this.range = document.createElement("input");
3632 var _a, _b, _c, _d;
3633 this.timeline = timeline;
3634 this.addCSS(twistyScrubberCSS);
3635 (_a = this.timeline) == null ? void 0 : _a.addTimestampListener(this);
3636 this.range.type = "range";
3637 this.range.step = 1 .toString();
3638 this.range.min = (_b = this.timeline) == null ? void 0 : _b.minTimestamp().toString();
3639 this.range.max = (_c = this.timeline) == null ? void 0 : _c.maxTimestamp().toString();
3640 this.range.value = (_d = this.timeline) == null ? void 0 : _d.timestamp.toString();
3641 this.range.addEventListener("input", this.onInput.bind(this));
3642 this.addElement(this.range);
3643 }
3644 onTimelineTimestampChange(timestamp) {
3645 this.range.value = timestamp.toString();
3646 }
3647 onTimeRangeChange(timeRange) {
3648 this.range.min = timeRange.start.toString();
3649 this.range.max = timeRange.end.toString();
3650 }
3651 onInput() {
3652 this.timeline.setTimestamp(parseInt(this.range.value, 10));
3653 }
3654};
3655customElementsShim.define("twisty-scrubber", TwistyScrubber);
3656
3657// src/cubing/twisty/dom/TwistyPlayer.css_.ts
3658var twistyPlayerCSS = new CSSSource(`
3659:host {
3660 width: 384px;
3661 height: 256px;
3662 display: grid;
3663}
3664
3665.wrapper {
3666 display: grid;
3667 overflow: hidden;
3668 grid-template-rows: 7fr 1em 1fr;
3669}
3670
3671.wrapper > * {
3672 width: inherit;
3673 height: inherit;
3674 overflow: hidden;
3675}
3676
3677.wrapper.controls-none {
3678 grid-template-rows: 7fr;
3679}
3680
3681.wrapper.controls-none twisty-scrubber,
3682.wrapper.controls-none twisty-control-button-panel {
3683 display: none;
3684}
3685
3686twisty-scrubber {
3687 background: rgba(196, 196, 196, 0.5);
3688}
3689
3690.wrapper.checkered {
3691 background-color: #EAEAEA;
3692 background-image: linear-gradient(45deg, #DDD 25%, transparent 25%, transparent 75%, #DDD 75%, #DDD),
3693 linear-gradient(45deg, #DDD 25%, transparent 25%, transparent 75%, #DDD 75%, #DDD);
3694 background-size: 32px 32px;
3695 background-position: 0 0, 16px 16px;
3696}
3697`);
3698
3699// src/cubing/twisty/dom/viewers/Twisty2DSVGView.css_.ts
3700var twisty2DSVGCSS = new CSSSource(`
3701:host {
3702 width: 384px;
3703 height: 256px;
3704 display: grid;
3705}
3706
3707.wrapper {
3708 width: 100%;
3709 height: 100%;
3710 display: grid;
3711 overflow: hidden;
3712}
3713
3714.svg-wrapper,
3715twisty-2d-svg,
3716svg {
3717 width: 100%;
3718 height: 100%;
3719 display: grid;
3720 min-height: 0;
3721}
3722`);
3723
3724// src/cubing/twisty/dom/viewers/Twisty2DSVG.ts
3725var _cachedPosition;
3726var Twisty2DSVG = class extends ManagedCustomElement {
3727 constructor(cursor, def, svgSource, options, puzzleLoader) {
3728 super();
3729 this.svgSource = svgSource;
3730 this.options = options;
3731 this.puzzleLoader = puzzleLoader;
3732 this.scheduler = new RenderScheduler(this.render.bind(this));
3733 _cachedPosition.set(this, null);
3734 var _a;
3735 this.addCSS(twisty2DSVGCSS);
3736 this.definition = def;
3737 this.resetSVG();
3738 cursor == null ? void 0 : cursor.addPositionListener(this);
3739 if ((_a = this.options) == null ? void 0 : _a.experimentalStickering) {
3740 this.experimentalSetStickering(this.options.experimentalStickering);
3741 }
3742 }
3743 onPositionChange(position) {
3744 if (position.movesInProgress.length > 0) {
3745 const move = position.movesInProgress[0].move;
3746 const def = this.definition;
3747 let partialMove = move;
3748 if (position.movesInProgress[0].direction === Direction.Backwards) {
3749 partialMove = move.invert();
3750 }
3751 const newState = combineTransformations(def, position.state, transformationForMove(def, partialMove));
3752 this.svg.draw(this.definition, position.state, newState, position.movesInProgress[0].fraction);
3753 } else {
3754 this.svg.draw(this.definition, position.state);
3755 __privateSet(this, _cachedPosition, position);
3756 }
3757 }
3758 scheduleRender() {
3759 this.scheduler.requestAnimFrame();
3760 }
3761 experimentalSetStickering(stickering) {
3762 (() => __async(this, null, function* () {
3763 var _a;
3764 if (!((_a = this.puzzleLoader) == null ? void 0 : _a.appearance)) {
3765 return;
3766 }
3767 const appearance = yield this.puzzleLoader.appearance(stickering);
3768 this.resetSVG(appearance);
3769 }))();
3770 }
3771 resetSVG(appearance) {
3772 if (this.svg) {
3773 this.removeElement(this.svg.element);
3774 }
3775 if (!this.definition) {
3776 return;
3777 }
3778 this.svg = new KPuzzleSVGWrapper(this.definition, this.svgSource, appearance);
3779 this.addElement(this.svg.element);
3780 if (__privateGet(this, _cachedPosition)) {
3781 this.onPositionChange(__privateGet(this, _cachedPosition));
3782 }
3783 }
3784 render() {
3785 }
3786};
3787_cachedPosition = new WeakMap();
3788customElementsShim.define("twisty-2d-svg", Twisty2DSVG);
3789
3790// src/cubing/twisty/dom/TwistyPlayer.ts
3791function is3DVisualization(visualizationFormat) {
3792 return ["3D", "PG3D"].includes(visualizationFormat);
3793}
3794var indexerMap = {
3795 simple: SimpleAlgIndexer,
3796 tree: TreeAlgIndexer,
3797 simultaneous: SimultaneousMoveIndexer
3798};
3799var _config, _connected, _legacyExperimentalPG3DViewConfig, _experimentalStartStateOverride, _hackyPendingFinalMoveCoalesce, _viewerWrapper, _controlsClassListManager, _experimentalInvalidInitialAlgCallback, _initialized, _setCursorStartState, setCursorStartState_fn, _cursorStartAlg, cursorStartAlg_fn, _cursorIndexerName, _indexerConstructor, indexerConstructor_fn, _pendingPuzzleUpdates, _renderMode, _clearRenderMode, clearRenderMode_fn, _setRenderMode2D, setRenderMode2D_fn, _setTwisty2DSVG, setTwisty2DSVG_fn, _setRenderMode3D, setRenderMode3D_fn, _setTwisty3D, setTwisty3D_fn, _setCursor, setCursor_fn, _getPG3DAppearance, getPG3DAppearance_fn, _createBackViewer, createBackViewer_fn, _removeBackViewerElem, removeBackViewerElem_fn, _hackyCoalescePending, hackyCoalescePending_fn;
3800var TwistyPlayer = class extends ManagedCustomElement {
3801 constructor(initialConfig = {}, legacyExperimentalPG3DViewConfig = null, experimentalInvalidInitialAlgCallback = () => {
3802 }) {
3803 super();
3804 _setCursorStartState.add(this);
3805 _cursorStartAlg.add(this);
3806 _indexerConstructor.add(this);
3807 _clearRenderMode.add(this);
3808 _setRenderMode2D.add(this);
3809 _setTwisty2DSVG.add(this);
3810 _setRenderMode3D.add(this);
3811 _setTwisty3D.add(this);
3812 _setCursor.add(this);
3813 _getPG3DAppearance.add(this);
3814 _createBackViewer.add(this);
3815 _removeBackViewerElem.add(this);
3816 _hackyCoalescePending.add(this);
3817 _config.set(this, void 0);
3818 this.scene = null;
3819 this.twisty3D = null;
3820 _connected.set(this, false);
3821 _legacyExperimentalPG3DViewConfig.set(this, null);
3822 this.legacyExperimentalPG3D = null;
3823 _experimentalStartStateOverride.set(this, null);
3824 this.viewerElems = [];
3825 this.controlElems = [];
3826 _hackyPendingFinalMoveCoalesce.set(this, false);
3827 _viewerWrapper.set(this, void 0);
3828 this.legacyExperimentalCoalesceModFunc = (_move) => 0;
3829 _controlsClassListManager.set(this, new ClassListManager(this, "controls-", ["none", "bottom-row"]));
3830 _experimentalInvalidInitialAlgCallback.set(this, void 0);
3831 _initialized.set(this, false);
3832 _cursorIndexerName.set(this, "tree");
3833 _pendingPuzzleUpdates.set(this, []);
3834 _renderMode.set(this, null);
3835 this.addCSS(twistyPlayerCSS);
3836 __privateSet(this, _config, new TwistyPlayerConfig(this, initialConfig));
3837 __privateSet(this, _experimentalInvalidInitialAlgCallback, experimentalInvalidInitialAlgCallback);
3838 this.timeline = new Timeline();
3839 this.timeline.addActionListener(this);
3840 this.contentWrapper.classList.add("checkered");
3841 __privateSet(this, _legacyExperimentalPG3DViewConfig, legacyExperimentalPG3DViewConfig);
3842 }
3843 set alg(newAlg) {
3844 var _a;
3845 if (typeof newAlg === "string") {
3846 newAlg = Alg.fromString(newAlg);
3847 }
3848 __privateGet(this, _config).attributes["alg"].setValue(newAlg);
3849 (_a = this.cursor) == null ? void 0 : _a.setAlg(newAlg, __privateMethod(this, _indexerConstructor, indexerConstructor_fn).call(this));
3850 __privateMethod(this, _setCursorStartState, setCursorStartState_fn).call(this);
3851 this.dispatchEvent(new CustomEvent("experimental-alg-update", {detail: {alg: this.alg}}));
3852 }
3853 get alg() {
3854 return __privateGet(this, _config).attributes["alg"].value;
3855 }
3856 set experimentalSetupAlg(newAlg) {
3857 if (typeof newAlg === "string") {
3858 console.warn("`experimentalSetupAlg` for a `TwistyPlayer` was set using a string. It should be set using a `Sequence`!");
3859 newAlg = new Alg(newAlg);
3860 }
3861 __privateGet(this, _config).attributes["experimental-setup-alg"].setValue(newAlg);
3862 __privateMethod(this, _setCursorStartState, setCursorStartState_fn).call(this);
3863 }
3864 get experimentalSetupAlg() {
3865 return __privateGet(this, _config).attributes["experimental-setup-alg"].value;
3866 }
3867 set experimentalSetupAnchor(setupToLocation) {
3868 __privateGet(this, _config).attributes["experimental-setup-anchor"].setValue(setupToLocation);
3869 __privateMethod(this, _setCursorStartState, setCursorStartState_fn).call(this);
3870 }
3871 get experimentalSetupAnchor() {
3872 return __privateGet(this, _config).attributes["experimental-setup-anchor"].value;
3873 }
3874 set puzzle(puzzle) {
3875 if (__privateGet(this, _config).attributes["puzzle"].setValue(puzzle)) {
3876 this.updatePuzzleDOM();
3877 }
3878 }
3879 get puzzle() {
3880 return __privateGet(this, _config).attributes["puzzle"].value;
3881 }
3882 set visualization(visualization) {
3883 if (__privateGet(this, _config).attributes["visualization"].setValue(visualization)) {
3884 this.updatePuzzleDOM();
3885 }
3886 }
3887 get visualization() {
3888 return __privateGet(this, _config).attributes["visualization"].value;
3889 }
3890 set hintFacelets(hintFacelets) {
3891 if (__privateGet(this, _config).attributes["hint-facelets"].setValue(hintFacelets)) {
3892 if (this.twisty3D instanceof Cube3D) {
3893 this.twisty3D.experimentalUpdateOptions({hintFacelets});
3894 }
3895 }
3896 }
3897 get hintFacelets() {
3898 return __privateGet(this, _config).attributes["hint-facelets"].value;
3899 }
3900 get experimentalStickering() {
3901 return __privateGet(this, _config).attributes["experimental-stickering"].value;
3902 }
3903 set experimentalStickering(experimentalStickering) {
3904 if (__privateGet(this, _config).attributes["experimental-stickering"].setValue(experimentalStickering)) {
3905 const twisty3D = this.twisty3D;
3906 if (twisty3D instanceof Cube3D) {
3907 twisty3D.experimentalUpdateOptions({
3908 experimentalStickering
3909 });
3910 }
3911 if (twisty3D instanceof PG3D) {
3912 (() => __async(this, null, function* () {
3913 const appearance = yield __privateMethod(this, _getPG3DAppearance, getPG3DAppearance_fn).call(this);
3914 twisty3D.experimentalSetAppearance(appearance);
3915 }))();
3916 }
3917 if (this.viewerElems[0] instanceof Twisty2DSVG) {
3918 this.viewerElems[0].experimentalSetStickering(this.experimentalStickering);
3919 }
3920 }
3921 }
3922 set background(background) {
3923 if (__privateGet(this, _config).attributes["background"].setValue(background)) {
3924 this.contentWrapper.classList.toggle("checkered", background === "checkered");
3925 }
3926 }
3927 get background() {
3928 return __privateGet(this, _config).attributes["background"].value;
3929 }
3930 set controlPanel(controlPanel) {
3931 __privateGet(this, _config).attributes["control-panel"].setValue(controlPanel);
3932 __privateGet(this, _controlsClassListManager).setValue(controlPanel);
3933 }
3934 get controlPanel() {
3935 return __privateGet(this, _config).attributes["control-panel"].value;
3936 }
3937 set controls(controls) {
3938 this.controlPanel = controls;
3939 }
3940 get controls() {
3941 return this.controlPanel;
3942 }
3943 set backView(backView) {
3944 __privateGet(this, _config).attributes["back-view"].setValue(backView);
3945 if (backView !== "none" && this.viewerElems.length === 1) {
3946 __privateMethod(this, _createBackViewer, createBackViewer_fn).call(this);
3947 }
3948 if (backView === "none" && this.viewerElems.length > 1) {
3949 __privateMethod(this, _removeBackViewerElem, removeBackViewerElem_fn).call(this);
3950 }
3951 if (__privateGet(this, _viewerWrapper) && __privateGet(this, _viewerWrapper).setBackView(backView)) {
3952 for (const viewer of this.viewerElems) {
3953 viewer.makeInvisibleUntilRender();
3954 }
3955 }
3956 }
3957 get backView() {
3958 return __privateGet(this, _config).attributes["back-view"].value;
3959 }
3960 set experimentalCameraPosition(cameraPosition) {
3961 var _a, _b, _c, _d, _e, _f;
3962 __privateGet(this, _config).attributes["experimental-camera-position"].setValue(cameraPosition);
3963 if (this.viewerElems && ["3D", "PG3D"].includes(__privateGet(this, _config).attributes["visualization"].value)) {
3964 (_a = this.viewerElems[0]) == null ? void 0 : _a.camera.position.copy(this.effectiveCameraPosition);
3965 (_b = this.viewerElems[0]) == null ? void 0 : _b.camera.lookAt(new Vector37(0, 0, 0));
3966 (_c = this.viewerElems[0]) == null ? void 0 : _c.scheduleRender();
3967 (_d = this.viewerElems[1]) == null ? void 0 : _d.camera.position.copy(this.effectiveCameraPosition).multiplyScalar(-1);
3968 (_e = this.viewerElems[1]) == null ? void 0 : _e.camera.lookAt(new Vector37(0, 0, 0));
3969 (_f = this.viewerElems[1]) == null ? void 0 : _f.scheduleRender();
3970 }
3971 }
3972 get experimentalCameraPosition() {
3973 return __privateGet(this, _config).attributes["experimental-camera-position"].value;
3974 }
3975 set viewerLink(viewerLinkPage) {
3976 __privateGet(this, _config).attributes["viewer-link"].setValue(viewerLinkPage);
3977 const maybePanel = this.controlElems[1];
3978 if (maybePanel == null ? void 0 : maybePanel.setViewerLink) {
3979 maybePanel.setViewerLink(viewerLinkPage);
3980 }
3981 }
3982 get viewerLink() {
3983 return __privateGet(this, _config).attributes["viewer-link"].value;
3984 }
3985 get effectiveCameraPosition() {
3986 var _a;
3987 return (_a = this.experimentalCameraPosition) != null ? _a : this.defaultCameraPosition;
3988 }
3989 get defaultCameraPosition() {
3990 return this.puzzle[1] === "x" ? cubeCameraPosition : centeredCameraPosition;
3991 }
3992 static get observedAttributes() {
3993 return TwistyPlayerConfig.observedAttributes;
3994 }
3995 attributeChangedCallback(attributeName, oldValue, newValue) {
3996 __privateGet(this, _config).attributeChangedCallback(attributeName, oldValue, newValue);
3997 }
3998 experimentalSetStartStateOverride(state) {
3999 __privateSet(this, _experimentalStartStateOverride, state);
4000 __privateMethod(this, _setCursorStartState, setCursorStartState_fn).call(this);
4001 }
4002 experimentalSetCursorIndexer(cursorName) {
4003 var _a;
4004 if (__privateGet(this, _cursorIndexerName) === cursorName) {
4005 return;
4006 }
4007 __privateSet(this, _cursorIndexerName, cursorName);
4008 (_a = this.cursor) == null ? void 0 : _a.experimentalSetIndexer(__privateMethod(this, _indexerConstructor, indexerConstructor_fn).call(this));
4009 }
4010 connectedCallback() {
4011 this.contentWrapper.classList.toggle("checkered", this.background === "checkered");
4012 const setBackView = this.backView && is3DVisualization(this.visualization);
4013 const backView = setBackView ? this.backView : "none";
4014 __privateSet(this, _viewerWrapper, new TwistyViewerWrapper({
4015 backView
4016 }));
4017 this.addElement(__privateGet(this, _viewerWrapper));
4018 const scrubber = new TwistyScrubber(this.timeline);
4019 const controlButtonGrid = new TwistyControlButtonPanel(this.timeline, {
4020 fullscreenElement: this,
4021 viewerLinkCallback: this.visitTwizzleLink.bind(this),
4022 viewerLink: this.viewerLink
4023 });
4024 this.controlElems = [scrubber, controlButtonGrid];
4025 __privateGet(this, _controlsClassListManager).setValue(this.controlPanel);
4026 this.addElement(this.controlElems[0]);
4027 this.addElement(this.controlElems[1]);
4028 __privateSet(this, _connected, true);
4029 this.updatePuzzleDOM(true).then(() => {
4030 if (!__privateGet(this, _initialized)) {
4031 __privateSet(this, _initialized, true);
4032 this.dispatchEvent(new CustomEvent("initialized"));
4033 }
4034 });
4035 }
4036 get initialized() {
4037 return __privateGet(this, _initialized);
4038 }
4039 twizzleLink() {
4040 const url = new URL("https://experiments.cubing.net/cubing.js/alg.cubing.net/index.html");
4041 if (!this.alg.experimentalIsEmpty()) {
4042 url.searchParams.set("alg", this.alg.toString());
4043 }
4044 if (!this.experimentalSetupAlg.experimentalIsEmpty()) {
4045 url.searchParams.set("experimental-setup-alg", this.experimentalSetupAlg.toString());
4046 }
4047 if (this.experimentalSetupAnchor !== "start") {
4048 url.searchParams.set("experimental-setup-anchor", this.experimentalSetupAnchor);
4049 }
4050 if (this.experimentalStickering !== "full") {
4051 url.searchParams.set("experimental-stickering", this.experimentalStickering);
4052 }
4053 if (this.puzzle !== "3x3x3") {
4054 url.searchParams.set("puzzle", this.puzzle);
4055 }
4056 return url.toString();
4057 }
4058 visitTwizzleLink() {
4059 const a = document.createElement("a");
4060 a.href = this.twizzleLink();
4061 a.target = "_blank";
4062 a.click();
4063 }
4064 updatePuzzleDOM(initial = false) {
4065 return __async(this, null, function* () {
4066 var _a, _b, _c, _d, _e;
4067 if (!__privateGet(this, _connected)) {
4068 return;
4069 }
4070 let puzzleLoader;
4071 if (this.puzzle === "custom") {
4072 puzzleLoader = {
4073 id: "custom",
4074 fullName: "Custom (PG3D)",
4075 def: () => Promise.resolve(__privateGet(this, _legacyExperimentalPG3DViewConfig).def),
4076 svg: () => __async(this, null, function* () {
4077 throw "unimplemented";
4078 })
4079 };
4080 } else {
4081 puzzleLoader = puzzles[this.puzzle];
4082 }
4083 for (const pendingPuzzleUpdate2 of __privateGet(this, _pendingPuzzleUpdates)) {
4084 pendingPuzzleUpdate2.cancelled = true;
4085 }
4086 __privateSet(this, _pendingPuzzleUpdates, []);
4087 const pendingPuzzleUpdate = {cancelled: false};
4088 __privateGet(this, _pendingPuzzleUpdates).push(pendingPuzzleUpdate);
4089 const def = yield puzzleLoader.def();
4090 if (pendingPuzzleUpdate.cancelled) {
4091 return;
4092 }
4093 let cursor;
4094 try {
4095 cursor = new AlgCursor(this.timeline, def, this.alg, __privateMethod(this, _cursorStartAlg, cursorStartAlg_fn).call(this), __privateMethod(this, _indexerConstructor, indexerConstructor_fn).call(this));
4096 __privateMethod(this, _setCursor, setCursor_fn).call(this, cursor);
4097 } catch (e) {
4098 if (initial) {
4099 __privateGet(this, _experimentalInvalidInitialAlgCallback).call(this, this.alg);
4100 }
4101 cursor = new AlgCursor(this.timeline, def, new Alg(), new Alg(), __privateMethod(this, _indexerConstructor, indexerConstructor_fn).call(this));
4102 __privateMethod(this, _setCursor, setCursor_fn).call(this, cursor);
4103 }
4104 if (initial && this.experimentalSetupAlg.experimentalIsEmpty() && this.experimentalSetupAnchor !== "end") {
4105 this.timeline.jumpToEnd();
4106 }
4107 switch (this.visualization) {
4108 case "2D":
4109 case "experimental-2D-LL":
4110 {
4111 const options = {};
4112 if (this.experimentalStickering) {
4113 options.experimentalStickering = this.experimentalStickering;
4114 }
4115 __privateMethod(this, _setRenderMode2D, setRenderMode2D_fn).call(this);
4116 const svgPromiseFn = this.visualization === "2D" ? puzzleLoader.svg : (_a = puzzleLoader.llSVG) != null ? _a : puzzleLoader.svg;
4117 const mainViewer = new Twisty2DSVG(cursor, def, yield svgPromiseFn(), options, puzzleLoader);
4118 if (!pendingPuzzleUpdate.cancelled) {
4119 __privateMethod(this, _setTwisty2DSVG, setTwisty2DSVG_fn).call(this, mainViewer);
4120 }
4121 }
4122 break;
4123 case "3D":
4124 case "PG3D":
4125 {
4126 __privateMethod(this, _setRenderMode3D, setRenderMode3D_fn).call(this);
4127 const scene = this.scene;
4128 let twisty3D;
4129 if (this.visualization === "3D" && this.puzzle === "3x3x3") {
4130 twisty3D = new Cube3D(def, cursor, scene.scheduleRender.bind(scene), {
4131 hintFacelets: this.hintFacelets,
4132 experimentalStickering: this.experimentalStickering
4133 });
4134 } else {
4135 let def2;
4136 let stickerDat;
4137 const pgGetter = puzzleLoader.pg;
4138 if (this.puzzle === "custom") {
4139 def2 = __privateGet(this, _legacyExperimentalPG3DViewConfig).def;
4140 stickerDat = __privateGet(this, _legacyExperimentalPG3DViewConfig).stickerDat;
4141 } else if (pgGetter) {
4142 const pg = yield pgGetter();
4143 if (pendingPuzzleUpdate.cancelled) {
4144 return;
4145 }
4146 def2 = pg.writekpuzzle(true);
4147 stickerDat = pg.get3d();
4148 } else {
4149 throw "Unimplemented!";
4150 }
4151 const options = {};
4152 const pg3d = new PG3D(cursor, scene.scheduleRender.bind(scene), def2, stickerDat, (_c = (_b = __privateGet(this, _legacyExperimentalPG3DViewConfig)) == null ? void 0 : _b.showFoundation) != null ? _c : true, (_e = (_d = __privateGet(this, _legacyExperimentalPG3DViewConfig)) == null ? void 0 : _d.hintStickers) != null ? _e : this.hintFacelets === "floating", options);
4153 (() => __async(this, null, function* () {
4154 const appearance = yield __privateMethod(this, _getPG3DAppearance, getPG3DAppearance_fn).call(this);
4155 if (appearance) {
4156 pg3d.experimentalSetAppearance(appearance);
4157 }
4158 }))();
4159 this.legacyExperimentalPG3D = pg3d;
4160 twisty3D = pg3d;
4161 }
4162 __privateMethod(this, _setTwisty3D, setTwisty3D_fn).call(this, twisty3D);
4163 }
4164 break;
4165 }
4166 });
4167 }
4168 setCustomPuzzleGeometry(legacyExperimentalPG3DViewConfig) {
4169 return __async(this, null, function* () {
4170 this.puzzle = "custom";
4171 __privateSet(this, _legacyExperimentalPG3DViewConfig, legacyExperimentalPG3DViewConfig);
4172 yield this.updatePuzzleDOM();
4173 });
4174 }
4175 experimentalAddMove(move, coalesce = false, coalesceDelayed = false) {
4176 if (__privateGet(this, _hackyPendingFinalMoveCoalesce)) {
4177 __privateMethod(this, _hackyCoalescePending, hackyCoalescePending_fn).call(this);
4178 }
4179 const oldNumMoves = countMoves(this.alg);
4180 const newAlg = experimentalAppendMove(this.alg, move, {
4181 coalesce: coalesce && !coalesceDelayed,
4182 mod: this.legacyExperimentalCoalesceModFunc(move)
4183 });
4184 if (coalesce && coalesceDelayed) {
4185 __privateSet(this, _hackyPendingFinalMoveCoalesce, true);
4186 }
4187 this.alg = newAlg;
4188 if (oldNumMoves <= countMoves(newAlg)) {
4189 this.timeline.experimentalJumpToLastMove();
4190 } else {
4191 this.timeline.jumpToEnd();
4192 }
4193 this.timeline.play();
4194 }
4195 onTimelineAction(actionEvent) {
4196 if (actionEvent.action === TimelineAction.Pausing && actionEvent.locationType === TimestampLocationType.EndOfTimeline && __privateGet(this, _hackyPendingFinalMoveCoalesce)) {
4197 __privateMethod(this, _hackyCoalescePending, hackyCoalescePending_fn).call(this);
4198 this.timeline.jumpToEnd();
4199 }
4200 }
4201 fullscreen() {
4202 this.requestFullscreen();
4203 }
4204};
4205_config = new WeakMap();
4206_connected = new WeakMap();
4207_legacyExperimentalPG3DViewConfig = new WeakMap();
4208_experimentalStartStateOverride = new WeakMap();
4209_hackyPendingFinalMoveCoalesce = new WeakMap();
4210_viewerWrapper = new WeakMap();
4211_controlsClassListManager = new WeakMap();
4212_experimentalInvalidInitialAlgCallback = new WeakMap();
4213_initialized = new WeakMap();
4214_setCursorStartState = new WeakSet();
4215setCursorStartState_fn = function() {
4216 var _a;
4217 if (this.cursor) {
4218 this.cursor.setStartState((_a = __privateGet(this, _experimentalStartStateOverride)) != null ? _a : this.cursor.algToState(__privateMethod(this, _cursorStartAlg, cursorStartAlg_fn).call(this)));
4219 }
4220};
4221_cursorStartAlg = new WeakSet();
4222cursorStartAlg_fn = function() {
4223 let startAlg = this.experimentalSetupAlg;
4224 if (this.experimentalSetupAnchor === "end") {
4225 startAlg = startAlg.concat(this.alg.invert());
4226 }
4227 return startAlg;
4228};
4229_cursorIndexerName = new WeakMap();
4230_indexerConstructor = new WeakSet();
4231indexerConstructor_fn = function() {
4232 return indexerMap[__privateGet(this, _cursorIndexerName)];
4233};
4234_pendingPuzzleUpdates = new WeakMap();
4235_renderMode = new WeakMap();
4236_clearRenderMode = new WeakSet();
4237clearRenderMode_fn = function() {
4238 switch (__privateGet(this, _renderMode)) {
4239 case "3D":
4240 this.scene = null;
4241 this.twisty3D = null;
4242 this.legacyExperimentalPG3D = null;
4243 this.viewerElems = [];
4244 __privateGet(this, _viewerWrapper).clear();
4245 break;
4246 case "2D":
4247 this.viewerElems = [];
4248 __privateGet(this, _viewerWrapper).clear();
4249 break;
4250 }
4251 __privateSet(this, _renderMode, null);
4252};
4253_setRenderMode2D = new WeakSet();
4254setRenderMode2D_fn = function() {
4255 if (__privateGet(this, _renderMode) === "2D") {
4256 return;
4257 }
4258 __privateMethod(this, _clearRenderMode, clearRenderMode_fn).call(this);
4259 __privateSet(this, _renderMode, "2D");
4260};
4261_setTwisty2DSVG = new WeakSet();
4262setTwisty2DSVG_fn = function(twisty2DSVG) {
4263 __privateMethod(this, _setRenderMode2D, setRenderMode2D_fn).call(this);
4264 __privateGet(this, _viewerWrapper).clear();
4265 __privateGet(this, _viewerWrapper).addElement(twisty2DSVG);
4266 this.viewerElems.push(twisty2DSVG);
4267};
4268_setRenderMode3D = new WeakSet();
4269setRenderMode3D_fn = function() {
4270 if (__privateGet(this, _renderMode) === "3D") {
4271 return;
4272 }
4273 __privateMethod(this, _clearRenderMode, clearRenderMode_fn).call(this);
4274 this.scene = new Twisty3DScene();
4275 const mainViewer = new Twisty3DCanvas(this.scene, {
4276 experimentalCameraPosition: this.effectiveCameraPosition
4277 });
4278 this.viewerElems.push(mainViewer);
4279 __privateGet(this, _viewerWrapper).addElement(mainViewer);
4280 if (this.backView !== "none") {
4281 __privateMethod(this, _createBackViewer, createBackViewer_fn).call(this);
4282 }
4283 __privateSet(this, _renderMode, "3D");
4284};
4285_setTwisty3D = new WeakSet();
4286setTwisty3D_fn = function(twisty3D) {
4287 if (this.twisty3D) {
4288 this.scene.removeTwisty3DPuzzle(this.twisty3D);
4289 if (this.twisty3D instanceof PG3D) {
4290 this.twisty3D.dispose();
4291 }
4292 this.twisty3D = null;
4293 }
4294 this.twisty3D = twisty3D;
4295 this.scene.addTwisty3DPuzzle(twisty3D);
4296};
4297_setCursor = new WeakSet();
4298setCursor_fn = function(cursor) {
4299 const oldCursor = this.cursor;
4300 this.cursor = cursor;
4301 try {
4302 this.cursor.setAlg(this.alg, __privateMethod(this, _indexerConstructor, indexerConstructor_fn).call(this));
4303 __privateMethod(this, _setCursorStartState, setCursorStartState_fn).call(this);
4304 } catch (e) {
4305 this.cursor.setAlg(new Alg(), __privateMethod(this, _indexerConstructor, indexerConstructor_fn).call(this));
4306 this.cursor.setStartState(this.cursor.algToState(new Alg()));
4307 __privateGet(this, _experimentalInvalidInitialAlgCallback).call(this, this.alg);
4308 }
4309 __privateMethod(this, _setCursorStartState, setCursorStartState_fn).call(this);
4310 this.timeline.addCursor(cursor);
4311 if (oldCursor) {
4312 this.timeline.removeCursor(oldCursor);
4313 this.timeline.removeTimestampListener(oldCursor);
4314 }
4315 this.experimentalSetCursorIndexer(__privateGet(this, _cursorIndexerName));
4316};
4317_getPG3DAppearance = new WeakSet();
4318getPG3DAppearance_fn = function() {
4319 return __async(this, null, function* () {
4320 var _a;
4321 const puzzleLoader = puzzles[this.puzzle];
4322 if (puzzleLoader == null ? void 0 : puzzleLoader.appearance) {
4323 return puzzleLoader.appearance((_a = this.experimentalStickering) != null ? _a : "full");
4324 }
4325 return null;
4326 });
4327};
4328_createBackViewer = new WeakSet();
4329createBackViewer_fn = function() {
4330 if (!is3DVisualization(this.visualization)) {
4331 throw new Error("Back viewer requires a 3D visualization");
4332 }
4333 const backViewer = new Twisty3DCanvas(this.scene, {
4334 experimentalCameraPosition: this.effectiveCameraPosition,
4335 negateCameraPosition: true
4336 });
4337 this.viewerElems.push(backViewer);
4338 this.viewerElems[0].setMirror(backViewer);
4339 __privateGet(this, _viewerWrapper).addElement(backViewer);
4340};
4341_removeBackViewerElem = new WeakSet();
4342removeBackViewerElem_fn = function() {
4343 if (this.viewerElems.length !== 2) {
4344 throw new Error("Tried to remove non-existent back view!");
4345 }
4346 __privateGet(this, _viewerWrapper).removeElement(this.viewerElems.pop());
4347};
4348_hackyCoalescePending = new WeakSet();
4349hackyCoalescePending_fn = function() {
4350 const units = Array.from(this.alg.units());
4351 const length = units.length;
4352 const pending = __privateGet(this, _hackyPendingFinalMoveCoalesce);
4353 __privateSet(this, _hackyPendingFinalMoveCoalesce, false);
4354 if (pending && length > 1 && units[length - 1].is(Move)) {
4355 const finalMove = units[length - 1];
4356 const newAlg = experimentalAppendMove(new Alg(units.slice(0, length - 1)), finalMove, {
4357 coalesce: true,
4358 mod: this.legacyExperimentalCoalesceModFunc(finalMove)
4359 });
4360 this.alg = newAlg;
4361 }
4362};
4363customElementsShim.define("twisty-player", TwistyPlayer);
4364
4365// src/cubing/twisty/dom/TwistyAlgViewer.ts
4366var DEFAULT_OFFSET_MS = 250;
4367var TwistyAlgLeafElem = class extends HTMLElementShim {
4368 constructor(className, text, dataDown, algOrUnit, offsetIntoMove) {
4369 super();
4370 this.algOrUnit = algOrUnit;
4371 this.textContent = text;
4372 this.classList.add(className);
4373 this.addEventListener("click", () => {
4374 dataDown.twistyAlgViewer.jumpToIndex(dataDown.earliestMoveIndex, offsetIntoMove);
4375 });
4376 }
4377 pathToIndex(_index) {
4378 return [];
4379 }
4380};
4381customElementsShim.define("twisty-alg-leaf-elem", TwistyAlgLeafElem);
4382var TwistyAlgWrapperElem = class extends HTMLElementShim {
4383 constructor(className, algOrUnit) {
4384 super();
4385 this.algOrUnit = algOrUnit;
4386 this.queue = [];
4387 this.classList.add(className);
4388 }
4389 addString(str) {
4390 this.queue.push(document.createTextNode(str));
4391 }
4392 addElem(dataUp) {
4393 this.queue.push(dataUp.element);
4394 return dataUp.moveCount;
4395 }
4396 flushQueue(direction = IterationDirection.Forwards) {
4397 for (const node of maybeReverseList(this.queue, direction)) {
4398 this.append(node);
4399 }
4400 this.queue = [];
4401 }
4402 pathToIndex(_index) {
4403 return [];
4404 }
4405};
4406customElementsShim.define("twisty-alg-wrapper-elem", TwistyAlgWrapperElem);
4407function oppositeDirection(direction) {
4408 return direction === IterationDirection.Forwards ? IterationDirection.Backwards : IterationDirection.Forwards;
4409}
4410function updateDirectionByAmount(currentDirection, amount) {
4411 return amount < 0 ? oppositeDirection(currentDirection) : currentDirection;
4412}
4413function maybeReverseList(l, direction) {
4414 if (direction === IterationDirection.Forwards) {
4415 return l;
4416 }
4417 const copy = Array.from(l);
4418 copy.reverse();
4419 return copy;
4420}
4421var AlgToDOMTree = class extends TraversalDownUp {
4422 traverseAlg(alg, dataDown) {
4423 let moveCount = 0;
4424 const element = new TwistyAlgWrapperElem("twisty-alg-alg", alg);
4425 let first = true;
4426 for (const unit of direct(alg.units(), dataDown.direction)) {
4427 if (!first) {
4428 element.addString(" ");
4429 }
4430 first = false;
4431 moveCount += element.addElem(this.traverseUnit(unit, {
4432 earliestMoveIndex: dataDown.earliestMoveIndex + moveCount,
4433 twistyAlgViewer: dataDown.twistyAlgViewer,
4434 direction: dataDown.direction
4435 }));
4436 }
4437 element.flushQueue(dataDown.direction);
4438 return {
4439 moveCount,
4440 element
4441 };
4442 }
4443 traverseGrouping(grouping, dataDown) {
4444 const square1Tuple = grouping.experimentalAsSquare1Tuple();
4445 const direction = updateDirectionByAmount(dataDown.direction, grouping.amount);
4446 let moveCount = 0;
4447 const element = new TwistyAlgWrapperElem("twisty-alg-grouping", grouping);
4448 element.addString("(");
4449 if (square1Tuple) {
4450 moveCount += element.addElem({
4451 moveCount: 1,
4452 element: new TwistyAlgLeafElem("twisty-alg-move", square1Tuple[0].amount.toString(), dataDown, square1Tuple[0], true)
4453 });
4454 element.addString(", ");
4455 moveCount += element.addElem({
4456 moveCount: 1,
4457 element: new TwistyAlgLeafElem("twisty-alg-move", square1Tuple[1].amount.toString(), dataDown, square1Tuple[1], true)
4458 });
4459 } else {
4460 moveCount += element.addElem(this.traverseAlg(grouping.alg, {
4461 earliestMoveIndex: dataDown.earliestMoveIndex + moveCount,
4462 twistyAlgViewer: dataDown.twistyAlgViewer,
4463 direction
4464 }));
4465 }
4466 element.addString(")" + grouping.experimentalRepetitionSuffix);
4467 element.flushQueue();
4468 return {
4469 moveCount: moveCount * Math.abs(grouping.amount),
4470 element
4471 };
4472 }
4473 traverseMove(move, dataDown) {
4474 const element = new TwistyAlgLeafElem("twisty-alg-move", move.toString(), dataDown, move, true);
4475 dataDown.twistyAlgViewer.highlighter.addMove(move.startCharIndex, element);
4476 return {
4477 moveCount: 1,
4478 element
4479 };
4480 }
4481 traverseCommutator(commutator, dataDown) {
4482 let moveCount = 0;
4483 const element = new TwistyAlgWrapperElem("twisty-alg-commutator", commutator);
4484 element.addString("[");
4485 element.flushQueue();
4486 const [first, second] = maybeReverseList([commutator.A, commutator.B], dataDown.direction);
4487 moveCount += element.addElem(this.traverseAlg(first, {
4488 earliestMoveIndex: dataDown.earliestMoveIndex + moveCount,
4489 twistyAlgViewer: dataDown.twistyAlgViewer,
4490 direction: dataDown.direction
4491 }));
4492 element.addString(", ");
4493 moveCount += element.addElem(this.traverseAlg(second, {
4494 earliestMoveIndex: dataDown.earliestMoveIndex + moveCount,
4495 twistyAlgViewer: dataDown.twistyAlgViewer,
4496 direction: dataDown.direction
4497 }));
4498 element.flushQueue(dataDown.direction);
4499 element.addString("]");
4500 element.flushQueue();
4501 return {
4502 moveCount: moveCount * 2,
4503 element
4504 };
4505 }
4506 traverseConjugate(conjugate, dataDown) {
4507 let moveCount = 0;
4508 const element = new TwistyAlgWrapperElem("twisty-alg-conjugate", conjugate);
4509 element.addString("[");
4510 const aLen = element.addElem(this.traverseAlg(conjugate.A, {
4511 earliestMoveIndex: dataDown.earliestMoveIndex + moveCount,
4512 twistyAlgViewer: dataDown.twistyAlgViewer,
4513 direction: dataDown.direction
4514 }));
4515 moveCount += aLen;
4516 element.addString(": ");
4517 moveCount += element.addElem(this.traverseAlg(conjugate.B, {
4518 earliestMoveIndex: dataDown.earliestMoveIndex + moveCount,
4519 twistyAlgViewer: dataDown.twistyAlgViewer,
4520 direction: dataDown.direction
4521 }));
4522 element.addString("]");
4523 element.flushQueue();
4524 return {
4525 moveCount: moveCount + aLen,
4526 element
4527 };
4528 }
4529 traversePause(pause, dataDown) {
4530 return {
4531 moveCount: 1,
4532 element: new TwistyAlgLeafElem("twisty-alg-pause", ".", dataDown, pause, true)
4533 };
4534 }
4535 traverseNewline(newline, _dataDown) {
4536 const element = new TwistyAlgWrapperElem("twisty-alg-newline", newline);
4537 element.append(document.createElement("br"));
4538 return {
4539 moveCount: 0,
4540 element
4541 };
4542 }
4543 traverseLineComment(lineComment, dataDown) {
4544 return {
4545 moveCount: 0,
4546 element: new TwistyAlgLeafElem("twisty-alg-line-comment", `//${lineComment.text}`, dataDown, lineComment, false)
4547 };
4548 }
4549};
4550var algToDOMTreeInstance = new AlgToDOMTree();
4551var algToDOMTree = algToDOMTreeInstance.traverseAlg.bind(algToDOMTreeInstance);
4552var MoveHighlighter = class {
4553 constructor() {
4554 this.moveCharIndexMap = new Map();
4555 this.currentElem = null;
4556 }
4557 addMove(charIndex, elem) {
4558 this.moveCharIndexMap.set(charIndex, elem);
4559 }
4560 set(move) {
4561 var _a, _b;
4562 const newElem = move ? (_a = this.moveCharIndexMap.get(move.startCharIndex)) != null ? _a : null : null;
4563 if (this.currentElem === newElem) {
4564 return;
4565 }
4566 (_b = this.currentElem) == null ? void 0 : _b.classList.remove("twisty-alg-current-move");
4567 newElem == null ? void 0 : newElem.classList.add("twisty-alg-current-move");
4568 this.currentElem = newElem;
4569 }
4570};
4571var _domTree;
4572var TwistyAlgViewer = class extends HTMLElementShim {
4573 constructor(options) {
4574 super();
4575 this.highlighter = new MoveHighlighter();
4576 _domTree.set(this, void 0);
4577 this.twistyPlayer = null;
4578 this.lastClickTimestamp = null;
4579 if (options == null ? void 0 : options.twistyPlayer) {
4580 this.setTwistyPlayer(options == null ? void 0 : options.twistyPlayer);
4581 }
4582 }
4583 connectedCallback() {
4584 }
4585 setAlg(alg) {
4586 __privateSet(this, _domTree, algToDOMTree(alg, {
4587 earliestMoveIndex: 0,
4588 twistyAlgViewer: this,
4589 direction: IterationDirection.Forwards
4590 }).element);
4591 this.textContent = "";
4592 this.appendChild(__privateGet(this, _domTree));
4593 }
4594 setTwistyPlayer(twistyPlayer) {
4595 if (this.twistyPlayer) {
4596 console.warn("twisty-player reassignment is not supported");
4597 return;
4598 }
4599 this.twistyPlayer = twistyPlayer;
4600 this.twistyPlayer.addEventListener("experimental-alg-update", (e) => {
4601 this.setAlg(e.detail.alg);
4602 });
4603 const sourceAlg = this.twistyPlayer.alg;
4604 const parsedAlg = "charIndex" in sourceAlg ? sourceAlg : Alg.fromString(sourceAlg.toString());
4605 this.setAlg(parsedAlg);
4606 (() => __async(this, null, function* () {
4607 const wrapper = new KPuzzleWrapper(yield puzzles[twistyPlayer.puzzle].def());
4608 const indexer = new TreeAlgIndexer(wrapper, parsedAlg);
4609 twistyPlayer.timeline.addTimestampListener({
4610 onTimelineTimestampChange: (timestamp) => {
4611 this.highlighter.set(indexer.getMove(indexer.timestampToIndex(timestamp)));
4612 },
4613 onTimeRangeChange(_timeRange) {
4614 }
4615 });
4616 }))();
4617 twistyPlayer.timeline.addTimestampListener({
4618 onTimelineTimestampChange: (timestamp) => {
4619 var _a, _b, _c;
4620 if (timestamp !== this.lastClickTimestamp) {
4621 this.lastClickTimestamp = null;
4622 }
4623 const index = (_c = (_b = (_a = this.twistyPlayer) == null ? void 0 : _a.cursor) == null ? void 0 : _b.experimentalIndexFromTimestamp(timestamp)) != null ? _c : null;
4624 if (index !== null) {
4625 }
4626 },
4627 onTimeRangeChange: (_timeRange) => {
4628 }
4629 });
4630 }
4631 jumpToIndex(index, offsetIntoMove) {
4632 var _a, _b;
4633 if (this.twistyPlayer && this.twistyPlayer.cursor) {
4634 const offset = offsetIntoMove ? DEFAULT_OFFSET_MS : 0;
4635 const timestamp = ((_a = this.twistyPlayer.cursor.experimentalTimestampFromIndex(index)) != null ? _a : -offset) + offset;
4636 (_b = this.twistyPlayer) == null ? void 0 : _b.timeline.setTimestamp(timestamp);
4637 if (this.lastClickTimestamp === timestamp) {
4638 this.twistyPlayer.timeline.play();
4639 this.lastClickTimestamp = null;
4640 } else {
4641 this.lastClickTimestamp = timestamp;
4642 }
4643 }
4644 }
4645 attributeChangedCallback(attributeName, _oldValue, newValue) {
4646 if (attributeName === "for") {
4647 const elem = document.getElementById(newValue);
4648 if (!elem) {
4649 console.warn("for= elem does not exist");
4650 return;
4651 }
4652 if (!(elem instanceof TwistyPlayer)) {
4653 console.warn("for= elem is not a twisty-player");
4654 return;
4655 }
4656 this.setTwistyPlayer(elem);
4657 }
4658 }
4659 static get observedAttributes() {
4660 return ["for"];
4661 }
4662};
4663_domTree = new WeakMap();
4664customElementsShim.define("twisty-alg-viewer", TwistyAlgViewer);
4665
4666// src/cubing/twisty/dom/TwistyAlgEditor.css_.ts
4667var twistyAlgEditorCSS = new CSSSource(`
4668:host {
4669 width: 384px;
4670 display: grid;
4671}
4672
4673.wrapper {
4674 /*overflow: hidden;
4675 resize: horizontal;*/
4676
4677 background: var(--background, none);
4678 display: grid;
4679}
4680
4681textarea, .carbon-copy {
4682 grid-area: 1 / 1 / 2 / 2;
4683
4684 width: 100%;
4685 font-family: sans-serif;
4686 line-height: 1.2em;
4687
4688 font-size: var(--font-size, inherit);
4689 font-family: var(--font-family, sans-serif);
4690
4691 box-sizing: border-box;
4692
4693 padding: var(--padding, 0.5em);
4694}
4695
4696textarea {
4697 resize: none;
4698 background: none;
4699 z-index: 2;
4700 overflow: hidden;
4701 border: 1px solid var(--border-color, rgba(0, 0, 0, 0.25));
4702}
4703
4704.carbon-copy {
4705 white-space: pre-wrap;
4706 word-wrap: break-word;
4707 color: transparent;
4708 user-select: none;
4709 pointer-events: none;
4710
4711 z-index: 1;
4712}
4713
4714.carbon-copy .highlight {
4715 background: var(--highlight-color, rgba(255, 128, 0, 0.5));
4716 padding: 0.1em 0.2em;
4717 margin: -0.1em -0.2em;
4718 border-radius: 0.2em;
4719}
4720
4721.wrapper.issue-warning textarea,
4722.wrapper.valid-for-puzzle-warning textarea {
4723 outline: none;
4724 border: 1px solid rgba(200, 200, 0, 0.5);
4725 background: rgba(255, 255, 0, 0.1);
4726}
4727
4728.wrapper.issue-error textarea,
4729.wrapper.valid-for-puzzle-error textarea {
4730 outline: none;
4731 border: 1px solid red;
4732 background: rgba(255, 0, 0, 0.1);
4733}
4734`);
4735
4736// src/cubing/twisty/dom/TwistyAlgEditorStartCharSearch.ts
4737var TwistyAlgEditorCharSearch = class extends TraversalDownUp {
4738 traverseAlg(alg, dataDown) {
4739 let numMovesSofar = dataDown.numMovesSofar;
4740 for (const unit of alg.units()) {
4741 const newDown = {
4742 targetCharIdx: dataDown.targetCharIdx,
4743 numMovesSofar
4744 };
4745 const dataUp = this.traverseUnit(unit, newDown);
4746 if ("latestUnit" in dataUp) {
4747 return dataUp;
4748 }
4749 numMovesSofar += dataUp.animatedMoveCount;
4750 }
4751 return {animatedMoveCount: numMovesSofar - dataDown.numMovesSofar};
4752 }
4753 traverseGrouping(grouping, dataDown) {
4754 const dataUp = this.traverseAlg(grouping.alg, dataDown);
4755 if ("latestUnit" in dataUp) {
4756 return dataUp;
4757 }
4758 return {
4759 animatedMoveCount: dataUp.animatedMoveCount * Math.abs(grouping.amount)
4760 };
4761 }
4762 traverseMove(move, dataDown) {
4763 const asParsed = move;
4764 if (asParsed.endCharIndex > dataDown.targetCharIdx) {
4765 return {
4766 latestUnit: asParsed,
4767 animatedMoveIdx: dataDown.numMovesSofar
4768 };
4769 }
4770 return {
4771 animatedMoveCount: 1
4772 };
4773 }
4774 traverseCommutator(commutator, dataDown) {
4775 const dataUpA = this.traverseAlg(commutator.A, dataDown);
4776 if ("latestUnit" in dataUpA) {
4777 return dataUpA;
4778 }
4779 const dataDownB = {
4780 targetCharIdx: dataDown.targetCharIdx,
4781 numMovesSofar: dataDown.numMovesSofar + dataUpA.animatedMoveCount
4782 };
4783 const dataUpB = this.traverseAlg(commutator.B, dataDownB);
4784 if ("latestUnit" in dataUpB) {
4785 return dataUpB;
4786 }
4787 return {
4788 animatedMoveCount: 2 * (dataUpA.animatedMoveCount + dataUpB.animatedMoveCount)
4789 };
4790 }
4791 traverseConjugate(conjugate, dataDown) {
4792 const dataUpA = this.traverseAlg(conjugate.A, dataDown);
4793 if ("latestUnit" in dataUpA) {
4794 return dataUpA;
4795 }
4796 const dataDownB = {
4797 targetCharIdx: dataDown.targetCharIdx,
4798 numMovesSofar: dataDown.numMovesSofar + dataUpA.animatedMoveCount
4799 };
4800 const dataUpB = this.traverseAlg(conjugate.B, dataDownB);
4801 if ("latestUnit" in dataUpB) {
4802 return dataUpB;
4803 }
4804 return {
4805 animatedMoveCount: dataUpA.animatedMoveCount * 2 + dataUpB.animatedMoveCount
4806 };
4807 }
4808 traversePause(pause, dataDown) {
4809 const asParsed = pause;
4810 if (asParsed.endCharIndex > dataDown.targetCharIdx) {
4811 return {
4812 latestUnit: asParsed,
4813 animatedMoveIdx: dataDown.numMovesSofar
4814 };
4815 }
4816 return {
4817 animatedMoveCount: 1
4818 };
4819 }
4820 traverseNewline(_newline, _dataDown) {
4821 return {animatedMoveCount: 0};
4822 }
4823 traverseLineComment(_comment, _dataDown) {
4824 return {animatedMoveCount: 0};
4825 }
4826};
4827var TwistyAlgEditorCharSearchInstance = new TwistyAlgEditorCharSearch();
4828var twistyAlgEditorCharSearch = TwistyAlgEditorCharSearchInstance.traverseAlg.bind(TwistyAlgEditorCharSearchInstance);
4829
4830// src/cubing/twisty/dom/TwistyAlgEditor.ts
4831var ATTRIBUTE_FOR_TWISTY_PLAYER = "for-twisty-player";
4832var ATTRIBUTE_PLACEHOLDER = "placeholder";
4833var ATTRIBUTE_TWISTY_PLAYER_PROP = "twisty-player-prop";
4834var _alg, _textarea, _carbonCopy, _carbonCopyPrefix, _carbonCopyHighlight, _highlightedLeaf, _textareaClassListManager, _textareaClassListValidForPuzzleManager, _twistyPlayer, _twistyPlayerProp, _observer, _lastObserverRect;
4835var TwistyAlgEditor = class extends ManagedCustomElement {
4836 constructor(options) {
4837 super();
4838 _alg.set(this, new Alg());
4839 _textarea.set(this, document.createElement("textarea"));
4840 _carbonCopy.set(this, document.createElement("div"));
4841 _carbonCopyPrefix.set(this, document.createElement("span"));
4842 _carbonCopyHighlight.set(this, document.createElement("span"));
4843 _highlightedLeaf.set(this, null);
4844 _textareaClassListManager.set(this, new ClassListManager(this, "issue-", ["none", "warning", "error"]));
4845 _textareaClassListValidForPuzzleManager.set(this, new ClassListManager(this, "valid-for-puzzle-", [
4846 "none",
4847 "warning",
4848 "error"
4849 ]));
4850 _twistyPlayer.set(this, null);
4851 _twistyPlayerProp.set(this, void 0);
4852 _observer.set(this, new ResizeObserver((entries) => this.onResize(entries)));
4853 _lastObserverRect.set(this, null);
4854 var _a;
4855 __privateGet(this, _carbonCopy).classList.add("carbon-copy");
4856 this.addElement(__privateGet(this, _carbonCopy));
4857 __privateGet(this, _textarea).rows = 1;
4858 this.addElement(__privateGet(this, _textarea));
4859 __privateGet(this, _carbonCopyPrefix).classList.add("prefix");
4860 __privateGet(this, _carbonCopy).appendChild(__privateGet(this, _carbonCopyPrefix));
4861 __privateGet(this, _carbonCopyHighlight).classList.add("highlight");
4862 __privateGet(this, _carbonCopy).appendChild(__privateGet(this, _carbonCopyHighlight));
4863 __privateGet(this, _textarea).setAttribute("spellcheck", "false");
4864 this.addCSS(twistyAlgEditorCSS);
4865 __privateGet(this, _textarea).addEventListener("input", () => this.onInput());
4866 document.addEventListener("selectionchange", () => this.onSelectionChange());
4867 if (options == null ? void 0 : options.twistyPlayer) {
4868 this.twistyPlayer = options.twistyPlayer;
4869 }
4870 __privateSet(this, _twistyPlayerProp, (_a = options == null ? void 0 : options.twistyPlayerProp) != null ? _a : "alg");
4871 __privateGet(this, _observer).observe(this.contentWrapper);
4872 }
4873 onResize(entries) {
4874 var _a, _b;
4875 const rect = entries[0].contentRect;
4876 if (rect.height !== ((_a = __privateGet(this, _lastObserverRect)) == null ? void 0 : _a.height) || rect.width !== ((_b = __privateGet(this, _lastObserverRect)) == null ? void 0 : _b.width)) {
4877 __privateGet(this, _observer).unobserve(this.contentWrapper);
4878 this.resizeTextarea();
4879 requestAnimationFrame(() => {
4880 __privateGet(this, _observer).observe(this.contentWrapper);
4881 });
4882 __privateSet(this, _lastObserverRect, rect);
4883 }
4884 }
4885 set algString(s) {
4886 __privateGet(this, _textarea).value = s;
4887 this.onInput();
4888 }
4889 get algString() {
4890 return __privateGet(this, _textarea).value;
4891 }
4892 set placeholder(placeholderText) {
4893 __privateGet(this, _textarea).placeholder = placeholderText;
4894 }
4895 resizeTextarea() {
4896 if (__privateGet(this, _textarea).clientHeight < __privateGet(this, _textarea).scrollHeight) {
4897 while (__privateGet(this, _textarea).clientHeight < __privateGet(this, _textarea).scrollHeight) {
4898 __privateGet(this, _textarea).rows++;
4899 }
4900 return;
4901 } else {
4902 while (__privateGet(this, _textarea).clientHeight === __privateGet(this, _textarea).scrollHeight) {
4903 if (__privateGet(this, _textarea).rows === 1) {
4904 return;
4905 }
4906 __privateGet(this, _textarea).rows--;
4907 }
4908 __privateGet(this, _textarea).rows++;
4909 }
4910 }
4911 onInput() {
4912 __privateGet(this, _carbonCopyHighlight).hidden = true;
4913 this.resizeTextarea();
4914 __privateGet(this, _carbonCopyPrefix).textContent = __privateGet(this, _textarea).value;
4915 __privateGet(this, _carbonCopyHighlight).textContent = "";
4916 try {
4917 __privateSet(this, _alg, new Alg(__privateGet(this, _textarea).value));
4918 this.dispatchEvent(new CustomEvent("effectiveAlgChange", {detail: {alg: __privateGet(this, _alg)}}));
4919 __privateGet(this, _textareaClassListManager).setValue(__privateGet(this, _alg).toString().trimEnd() === __privateGet(this, _textarea).value.trimEnd() ? "none" : "warning");
4920 } catch (e) {
4921 __privateSet(this, _alg, new Alg());
4922 this.dispatchEvent(new CustomEvent("effectiveAlgChange", {detail: {alg: __privateGet(this, _alg)}}));
4923 __privateGet(this, _textareaClassListManager).setValue("error");
4924 }
4925 }
4926 onSelectionChange() {
4927 if (document.activeElement !== this || this.shadow.activeElement !== __privateGet(this, _textarea)) {
4928 return;
4929 }
4930 if (__privateGet(this, _twistyPlayerProp) !== "alg") {
4931 return;
4932 }
4933 const dataUp = twistyAlgEditorCharSearch(__privateGet(this, _alg), {
4934 targetCharIdx: __privateGet(this, _textarea).selectionStart,
4935 numMovesSofar: 0
4936 });
4937 if ("latestUnit" in dataUp) {
4938 this.dispatchEvent(new CustomEvent("animatedMoveIndexChange", {
4939 detail: {
4940 idx: dataUp.animatedMoveIdx,
4941 isAtStartOfLeaf: __privateGet(this, _textarea).selectionStart >= dataUp.latestUnit.startCharIndex && __privateGet(this, _textarea).selectionStart < dataUp.latestUnit.endCharIndex,
4942 leaf: dataUp.latestUnit
4943 }
4944 }));
4945 this.highlightLeaf(dataUp.latestUnit);
4946 } else {
4947 this.dispatchEvent(new CustomEvent("animatedMoveIndexChange", {
4948 detail: {idx: dataUp.animatedMoveCount, isPartiallyInLeaf: false}
4949 }));
4950 }
4951 }
4952 setAlgValidForPuzzle(valid) {
4953 __privateGet(this, _textareaClassListValidForPuzzleManager).setValue(valid ? "none" : "error");
4954 }
4955 highlightLeaf(leaf) {
4956 if (leaf === __privateGet(this, _highlightedLeaf)) {
4957 return;
4958 }
4959 __privateSet(this, _highlightedLeaf, leaf);
4960 __privateGet(this, _carbonCopyPrefix).textContent = __privateGet(this, _textarea).value.slice(0, leaf.startCharIndex);
4961 __privateGet(this, _carbonCopyHighlight).textContent = __privateGet(this, _textarea).value.slice(leaf.startCharIndex, leaf.endCharIndex);
4962 __privateGet(this, _carbonCopyHighlight).hidden = false;
4963 }
4964 get twistyPlayer() {
4965 return __privateGet(this, _twistyPlayer);
4966 }
4967 set twistyPlayer(twistyPlayer) {
4968 if (__privateGet(this, _twistyPlayer)) {
4969 console.warn("twisty-player reassignment/clearing is not supported");
4970 return;
4971 }
4972 __privateSet(this, _twistyPlayer, twistyPlayer);
4973 if (!twistyPlayer) {
4974 return;
4975 }
4976 this.algString = twistyPlayer.alg.toString();
4977 this.addEventListener("effectiveAlgChange", (e) => {
4978 try {
4979 twistyPlayer[__privateGet(this, _twistyPlayerProp)] = e.detail.alg;
4980 this.setAlgValidForPuzzle(true);
4981 } catch (e2) {
4982 console.error("cannot set alg for puzzle", e2);
4983 twistyPlayer[__privateGet(this, _twistyPlayerProp)] = new Alg();
4984 this.setAlgValidForPuzzle(false);
4985 }
4986 });
4987 if (__privateGet(this, _twistyPlayerProp) === "alg") {
4988 this.addEventListener("animatedMoveIndexChange", (e) => {
4989 try {
4990 const timestamp = twistyPlayer.cursor.experimentalTimestampFromIndex(e.detail.idx);
4991 twistyPlayer.timeline.setTimestamp(timestamp + (e.detail.isAtStartOfLeaf ? 250 : 0));
4992 } catch (e2) {
4993 twistyPlayer.timeline.timestamp = 0;
4994 }
4995 });
4996 twistyPlayer.timeline.addTimestampListener({
4997 onTimelineTimestampChange: (timestamp) => {
4998 const idx = twistyPlayer.cursor.experimentalIndexFromTimestamp(timestamp);
4999 const move = twistyPlayer.cursor.experimentalMoveAtIndex(idx);
5000 if (move) {
5001 this.highlightLeaf(move);
5002 }
5003 },
5004 onTimeRangeChange: (_timeRange) => {
5005 }
5006 });
5007 }
5008 }
5009 attributeChangedCallback(attributeName, _oldValue, newValue) {
5010 switch (attributeName) {
5011 case ATTRIBUTE_FOR_TWISTY_PLAYER:
5012 const elem = document.getElementById(newValue);
5013 if (!elem) {
5014 console.warn(`${ATTRIBUTE_FOR_TWISTY_PLAYER}= elem does not exist`);
5015 return;
5016 }
5017 if (!(elem instanceof TwistyPlayer)) {
5018 console.warn(`${ATTRIBUTE_FOR_TWISTY_PLAYER}=is not a twisty-player`);
5019 return;
5020 }
5021 this.twistyPlayer = elem;
5022 return;
5023 case ATTRIBUTE_PLACEHOLDER:
5024 this.placeholder = newValue;
5025 return;
5026 case ATTRIBUTE_TWISTY_PLAYER_PROP:
5027 if (__privateGet(this, _twistyPlayer)) {
5028 throw new Error("cannot set prop after twisty player");
5029 }
5030 __privateSet(this, _twistyPlayerProp, newValue);
5031 return;
5032 }
5033 }
5034 static get observedAttributes() {
5035 return [
5036 ATTRIBUTE_FOR_TWISTY_PLAYER,
5037 ATTRIBUTE_PLACEHOLDER,
5038 ATTRIBUTE_TWISTY_PLAYER_PROP
5039 ];
5040 }
5041};
5042_alg = new WeakMap();
5043_textarea = new WeakMap();
5044_carbonCopy = new WeakMap();
5045_carbonCopyPrefix = new WeakMap();
5046_carbonCopyHighlight = new WeakMap();
5047_highlightedLeaf = new WeakMap();
5048_textareaClassListManager = new WeakMap();
5049_textareaClassListValidForPuzzleManager = new WeakMap();
5050_twistyPlayer = new WeakMap();
5051_twistyPlayerProp = new WeakMap();
5052_observer = new WeakMap();
5053_lastObserverRect = new WeakMap();
5054customElementsShim.define("twisty-alg-editor", TwistyAlgEditor);
5055
5056export {
5057 __defProp,
5058 TreeAlgIndexer,
5059 SimpleAlgIndexer,
5060 TimestampLocationType,
5061 TwistyAlgEditor,
5062 experimentalShowRenderStats,
5063 experimentalSetShareAllNewRenderers,
5064 KPuzzleWrapper,
5065 Twisty3DCanvas,
5066 TwistyPlayer,
5067 Cube3D,
5068 PG3D,
5069 TwistyAlgViewer
5070};
5071//# sourceMappingURL=chunk-UOUXNU34.js.map