diff --git a/docs/cubing/api/index.html b/docs/cubing/api/index.html index 752b20d62..1ba461787 100644 --- a/docs/cubing/api/index.html +++ b/docs/cubing/api/index.html @@ -161,6 +161,7 @@

cubing/twisty

    backView?: BackViewLayout;
    experimentalCameraLatitude?: Vector3;
    experimentalCameraLongitude?: Vector3;
+    experimentalCameraLatitudeLimits?: "auto" | "none";

    viewerLink?: "twizzle" | "none";
  })
diff --git a/src/cubing/twisty/dom/TwistyPlayer.ts b/src/cubing/twisty/dom/TwistyPlayer.ts index 13734573c..db979898c 100644 --- a/src/cubing/twisty/dom/TwistyPlayer.ts +++ b/src/cubing/twisty/dom/TwistyPlayer.ts @@ -29,6 +29,7 @@ import { customElementsShim } from "./element/node-custom-element-shims"; import { twistyPlayerCSS } from "./TwistyPlayer.css_"; import { BackgroundTheme, + CameraLatitudeLimits, ControlsLocation, defaultCameraOrbitCoordinates, ExperimentalStickering, @@ -330,6 +331,15 @@ export class TwistyPlayer extends ManagedCustomElement { return (this.viewerElems[0] as Twisty3DCanvas)?.orbitControls ?? null; } + #backOrbitControls(): TwistyOrbitControls | null { + if ( + !["3D", "PG3D"].includes(this.#config.attributes["visualization"].value) + ) { + return null; + } + return (this.viewerElems[1] as Twisty3DCanvas)?.orbitControls ?? null; + } + set experimentalCameraLatitude(latitude: number | null) { this.#config.attributes["experimental-camera-latitude"].setValue(latitude); const orbitControls = this.#orbitControls(); @@ -370,6 +380,27 @@ export class TwistyPlayer extends ManagedCustomElement { return this.#orbitControls()?.longitude ?? null; } + set experimentalCameraLatitudeLimits(latitudeLimits: CameraLatitudeLimits) { + this.#config.attributes["experimental-camera-latitude-limits"].setValue( + latitudeLimits, + ); + const orbitControls = this.#orbitControls(); + console.log({ orbitControls }); + if (orbitControls) { + orbitControls.experimentalLatitudeLimits = latitudeLimits; + } + const backOrbitControls = this.#backOrbitControls(); // TODO: propagate through direct orbit controls as source of truth. + if (backOrbitControls) { + backOrbitControls.experimentalLatitudeLimits = latitudeLimits; + } + } + + get experimentalCameraLatitudeLimits(): CameraLatitudeLimits { + // TODO: sync with orbit controls + return this.#config.attributes["experimental-camera-latitude-limits"] + .value as CameraLatitudeLimits; + } + set viewerLink(viewerLinkPage: ViewerLinkPage) { this.#config.attributes["viewer-link"].setValue(viewerLinkPage); const maybePanel = this.controlElems[1] as @@ -584,6 +615,8 @@ export class TwistyPlayer extends ManagedCustomElement { const mainViewer = new Twisty3DCanvas(this.scene, { orbitCoordinates: this.experimentalDerivedCameraOrbitCoordinates(), }); + mainViewer.orbitControls.experimentalLatitudeLimits = + this.experimentalCameraLatitudeLimits; this.viewerElems.push(mainViewer); this.#viewerWrapper.addElement(mainViewer); @@ -805,6 +838,8 @@ export class TwistyPlayer extends ManagedCustomElement { orbitCoordinates: this.experimentalDerivedCameraOrbitCoordinates(), negateCameraPosition: true, }); + backViewer.orbitControls.experimentalLatitudeLimits = + this.experimentalCameraLatitudeLimits; this.viewerElems.push(backViewer); (this.viewerElems[0] as Twisty3DCanvas).setMirror(backViewer); this.#viewerWrapper.addElement(backViewer); diff --git a/src/cubing/twisty/dom/TwistyPlayerConfig.ts b/src/cubing/twisty/dom/TwistyPlayerConfig.ts index 3e7ecd36c..19e0be926 100644 --- a/src/cubing/twisty/dom/TwistyPlayerConfig.ts +++ b/src/cubing/twisty/dom/TwistyPlayerConfig.ts @@ -180,6 +180,12 @@ export interface ManagedAttribute { setValue(v: K): boolean; } +export const cameraLatitudeLimits = { + auto: true, // default + none: true, +}; +export type CameraLatitudeLimits = keyof typeof cameraLatitudeLimits; + type AnyManagedAttribute = ManagedAttribute; interface TwistyPlayerAttributes extends Record { @@ -202,6 +208,7 @@ interface TwistyPlayerAttributes extends Record { "back-view": StringEnumAttribute; "experimental-camera-latitude": RangedFloatAttribute; "experimental-camera-longitude": RangedFloatAttribute; + "experimental-camera-latitude-limits": StringEnumAttribute; // Interaction "viewer-link": StringEnumAttribute; @@ -223,6 +230,7 @@ export interface TwistyPlayerConfigValues { backView: BackViewLayout; experimentalCameraLatitude: number; experimentalCameraLongitude: number; + experimentalCameraLatitudeLimits: CameraLatitudeLimits; viewerLink: ViewerLinkPage; } @@ -248,6 +256,7 @@ const twistyPlayerAttributeMap: Record< "back-view": "backView", "experimental-camera-latitude": "experimentalCameraLatitude", "experimental-camera-longitude": "experimentalCameraLongitude", + "experimental-camera-latitude-limits": "experimentalCameraLatitudeLimits", "viewer-link": "viewerLink", }; @@ -301,16 +310,20 @@ export class TwistyPlayerConfig { 90, initialValues["experimentalCameraLatitude"], ), - "viewer-link": new StringEnumAttribute( - viewerLinkPages, - initialValues.viewerLink, - ), "experimental-camera-longitude": new RangedFloatAttribute( null, -180, 180, initialValues["experimentalCameraLongitude"], ), + "experimental-camera-latitude-limits": new StringEnumAttribute( + cameraLatitudeLimits, + initialValues["experimentalCameraLatitudeLimits"], + ), + "viewer-link": new StringEnumAttribute( + viewerLinkPages, + initialValues.viewerLink, + ), }; } diff --git a/src/cubing/twisty/dom/viewers/Twisty3DCanvas.ts b/src/cubing/twisty/dom/viewers/Twisty3DCanvas.ts index 092da37f2..b1edede04 100644 --- a/src/cubing/twisty/dom/viewers/Twisty3DCanvas.ts +++ b/src/cubing/twisty/dom/viewers/Twisty3DCanvas.ts @@ -8,6 +8,7 @@ import { OrbitCoordinates, TwistyOrbitControls } from "./TwistyOrbitControls"; import type { TwistyViewerElement } from "./TwistyViewerElement"; import { customElementsShim } from "../element/node-custom-element-shims"; import { Stats } from "../../../vendor/three/examples/jsm/libs/stats.module"; +import type { CameraLatitudeLimits } from "../TwistyPlayerConfig"; let SHOW_STATS = false; // Show render stats for newly contructed renderers. @@ -122,7 +123,7 @@ export class Twisty3DCanvas } /** @deprecated */ - public experimentalSetLatitudeLimits(limits: boolean): void { + public experimentalSetLatitudeLimits(limits: CameraLatitudeLimits): void { this.orbitControls.experimentalLatitudeLimits = limits; } diff --git a/src/cubing/twisty/dom/viewers/TwistyOrbitControls.ts b/src/cubing/twisty/dom/viewers/TwistyOrbitControls.ts index 392c7c7b3..b7f1ca010 100644 --- a/src/cubing/twisty/dom/viewers/TwistyOrbitControls.ts +++ b/src/cubing/twisty/dom/viewers/TwistyOrbitControls.ts @@ -1,6 +1,7 @@ import { Camera, Spherical, Vector3 } from "three"; import { DEGREES_PER_RADIAN } from "../../3D/TAU"; import { RenderScheduler } from "../../animation/RenderScheduler"; +import type { CameraLatitudeLimits } from "../TwistyPlayerConfig"; // Buffer at the end values of the latitude (phi), to prevent gymbal lock. // Without this, the puzzle would flip every frame if you try to push past the @@ -8,7 +9,7 @@ import { RenderScheduler } from "../../animation/RenderScheduler"; const EPSILON = 0.00000001; const INERTIA_DEFAULT: boolean = true; -const LATITUDE_LIMITS_DEFAULT: boolean = true; +const LATITUDE_LIMITS_DEFAULT: CameraLatitudeLimits = "auto"; const INERTIA_DURATION_MS = 500; // If the first inertial render is this long after the last move, we assume the @@ -166,7 +167,7 @@ export class TwistyOrbitControls { /** @deprecated */ experimentalInertia: boolean = INERTIA_DEFAULT; /** @deprecated */ - experimentalLatitudeLimits: boolean = LATITUDE_LIMITS_DEFAULT; + experimentalLatitudeLimits: CameraLatitudeLimits = LATITUDE_LIMITS_DEFAULT; private mirrorControls?: TwistyOrbitControls; private lastTouchClientX: number = 0; private lastTouchClientY: number = 0; @@ -322,7 +323,7 @@ export class TwistyOrbitControls { this.#spherical.theta += -2 * movementX; this.#spherical.phi += -2 * movementY; - if (this.experimentalLatitudeLimits) { + if (this.experimentalLatitudeLimits !== "none") { this.#spherical.phi = Math.max(this.#spherical.phi, Math.PI * 0.3); // TODO: Arctic circle: 1/6 this.#spherical.phi = Math.min(this.#spherical.phi, Math.PI * 0.7); // TODO: Antarctic circle: 5/6 } else { diff --git a/src/sites/alpha.twizzle.net/explore/app.ts b/src/sites/alpha.twizzle.net/explore/app.ts index 31f38acb2..4ecd3fb59 100644 --- a/src/sites/alpha.twizzle.net/explore/app.ts +++ b/src/sites/alpha.twizzle.net/explore/app.ts @@ -280,6 +280,7 @@ async function setAlgo(str: string, writeback: boolean): Promise { backView: getCheckbox("sidebyside") ? "side-by-side" : "top-right", experimentalCameraLatitude: initialCameraOrbitCoordinates.latitude, experimentalCameraLongitude: initialCameraOrbitCoordinates.longitude, + experimentalCameraLatitudeLimits: "none", // TODO: distance? viewerLink: "none", }, @@ -303,7 +304,6 @@ async function setAlgo(str: string, writeback: boolean): Promise { initialCameraOrbitCoordinates, ); for (const twisty3DCanvas of twisty3DCanvases) { - twisty3DCanvas.experimentalSetLatitudeLimits(false); twisty3DCanvas.canvas.addEventListener( "mouseup", onMouseClick.bind(onMouseClick, twisty3DCanvas, "U"), diff --git a/src/sites/experiments.cubing.net/cubing.js/flag/index.ts b/src/sites/experiments.cubing.net/cubing.js/flag/index.ts index f46eea5dd..28e22e4bb 100644 --- a/src/sites/experiments.cubing.net/cubing.js/flag/index.ts +++ b/src/sites/experiments.cubing.net/cubing.js/flag/index.ts @@ -22,7 +22,7 @@ const canvas = new Twisty3DCanvas(scene, { }); canvas.camera.position.y = 24; canvas.camera.far = 100; // Document this for others. -canvas.experimentalSetLatitudeLimits(false); +canvas.experimentalSetLatitudeLimits("none"); document.body.appendChild(canvas); function randomChoice(l: T[]): T { diff --git a/src/sites/experiments.cubing.net/cubing.js/twisty/attributes.html b/src/sites/experiments.cubing.net/cubing.js/twisty/attributes.html index 5d9ab1eec..3453ddaba 100644 --- a/src/sites/experiments.cubing.net/cubing.js/twisty/attributes.html +++ b/src/sites/experiments.cubing.net/cubing.js/twisty/attributes.html @@ -242,6 +242,24 @@

+
+

+ experimental-camera-latitude-limits="none"
+ alg="R U R' U R U2' R'"
+ experimental-camera-latitude="80" +

+
+ DOM attributes: + Constructor config object: + Dynamic property setter: + +
+
+

viewer-link="none"