diff --git a/.gitignore b/.gitignore index 31ca2587..d89351fd 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,9 @@ eslint.config.js # Generated results (local only) scripts/results/ +# Console utility scripts (developer-only, auto-downloads files) +scripts/PlayerPiano*.js + # LaTeX build artifacts *.aux *.out diff --git a/Geometry documents/Animations.md b/Geometry documents/Animations.md index b095d4f7..33d51b28 100644 --- a/Geometry documents/Animations.md +++ b/Geometry documents/Animations.md @@ -951,10 +951,106 @@ Currently, clicking ▶ in the "Camera" row only animates camera position — th **Resolution**: Phase 6 dual-row UI. Top-row ▶ is camera-only by design. Bottom-row ▶ calls `animateToViewFull()` which smoothly interpolates the cutplane value per frame and snaps remaining scene state at arrival. Exceeded original fix — cutplane now *animates* between views rather than snapping. +### ~~BUG: Camera slerp torque on Z-down transitions (View 12)~~ — RESOLVED `bfc1a1c` + +~~**Symptom**: When transitioning to `zdown` camera preset (e.g., PP-12 "All 5 geodesics, top-down"), the camera arrives at the correct position but at the last second torques ~180°.~~ + +**Root cause**: `animateToView()` hardcoded `camera.up.set(0, 0, 1)` every frame, but `setCameraPreset("zdown")` sets `camera.up.set(0, 1, 0)` (Y-up for top view). When the camera position approached (0,0,+d), the look direction (0,0,-1) became antiparallel to the forced Z-up vector — `lookAt()` singularity caused the flip. + +**Fix**: (1) Proper angle-based slerp `sin((1-t)ω)/sin(ω)` for direction interpolation (replaces lerp+normalize which breaks near antipodal). (2) Interpolated up vector — captures `startUp` from current camera, computes `endUp` using setCameraPreset's Z-pole logic (Y-up when camera is within ~18° of Z axis), and smoothly lerps between them. + +--- + +### ~~BUG: Planar/Radial matrix groups missing dissolve transitions (Views 13–19)~~ — PARTIALLY RESOLVED `a3dfe92` + +~~**Symptom**: During Player Piano playback (Camera + Scene ▶), planar matrices (cube matrix, tet matrix, etc.) and radial matrices pop in/out with no opacity dissolve. They appear at 100% opacity — fully opaque — regardless of the opacity slider setting.~~ + +**Partial fix** (`66de343`): All 10 matrix creation blocks in `rt-rendering.js` (5 planar + 5 radial) now read `dissolveOpacity` from the group's `userData` and compute `effectiveOpacity = opacity * dissolveOpacity` before passing to the matrix creator. The `userData.parameters.opacity` still stores the raw slider value for clean export/import (dissolve is transient during animation only). + +**Remaining issue**: Dissolve transitions for matrices remain visually glitchy even with the opacity pipeline fix. Tested with simple planar matrix views (4×4 cube matrix, no frequency ramps, t=8s) — no smooth fade comparable to regular polyhedra or tetrahelices. See "Known Limitation" below. + +--- + +### KNOWN LIMITATION: Planar/Radial matrix animation quality `a3dfe92` + +**Symptom**: Planar and radial matrix forms produce glitchy, non-smooth dissolve transitions during Player Piano playback. Unlike regular polyhedra and tetrahelices which fade cleanly, matrices exhibit visual artifacts during opacity transitions. + +**Underlying cause**: Matrix geometry is built from **copies of base forms** — each cell in an N×N planar matrix or F-frequency radial matrix contains its own independent mesh with its own materials. There is no geometry deduplication. This means: + +1. **Z-fighting**: Coplanar faces from adjacent cells fight for depth buffer priority, producing flickering/shimmer that is especially visible during opacity transitions when `depthWrite` behavior changes. +2. **Rebuild cost**: Each dissolve tick calls `updateGeometry()` which tears down and recreates the entire matrix mesh. For radial matrices at F3–F4 this takes 52–276ms per frame, causing rAF violations and frame drops. +3. **No material sharing**: Unlike `renderPolyhedron()` which creates a single mesh with shared materials, matrix creators build N² independent cell groups. Updating opacity requires rebuilding all of them. + +By contrast, tetrahelices **do** deduplicate geometry at each build step, producing clean shared faces with no coplanar overlap — which is why they dissolve smoothly. + +**Current status**: Users are not blocked from adding matrix views to animations, but results will be visually rough. The Player Piano demo reel generator (`scripts/PlayerPiano-generator.js`) should avoid matrix views until this is resolved. + +**Future pipeline**: +- Short-term: Optimize dissolve-only ticks to update `material.opacity` in-place rather than rebuilding (see workplan item below) +- Medium-term: Investigate instanced rendering (`THREE.InstancedMesh`) for matrix cells to eliminate per-cell material overhead +- Long-term: Geometry deduplication for matrix cells — merge coplanar shared faces (as tetrahelices already do) to eliminate z-fighting + +--- + +### ~~BUG: Matrix sub-control state not persisting across save/load~~ — RESOLVED `9326f11` + +~~**Symptom**: User report — matrix configurations (size slider, 45° toggle, radial frequency) revert to defaults or display incorrectly when reopening a saved file. Specifically: slider thumb at position 5 but display text shows "1×1"; 45° rotation toggle not restored; sub-control panels (size slider, rotation checkbox) hidden until parent checkbox toggled off/on.~~ + +**Root cause**: Three interrelated gaps in the state pipeline: + +1. **45° toggles not captured**: The 5 `*MatrixRotate45` checkboxes were absent from both `RTDelta._captureCheckboxes()` (view deltas) and `RTFileHandler.exportState()` (file export). The rendering code reads them from DOM but no persistence system saved them. +2. **Slider display text not updated**: `importState()` set `el.value` on matrix size sliders without dispatching 'input' events or updating the display element. The display ID `cubeMatrixSizeValue` doesn't match `_setSlider()`'s two lookup patterns (`*Display` and `*SliderValue`), so even the delta path's own display update missed it — though the dispatched 'input' event triggers the UI binding handler which uses the correct `formatValue: v => \`${v}×${v}\``. +3. **Sub-control panels not revealed**: `importState()` had explicit show/hide blocks for line, polygon, prism, cone, tetrahelix, penrose, and quadray controls — but zero entries for any of the 10 matrix control panels (5 planar + 5 radial). + +**Fix**: (1) Added 5 rotate45 checkboxes to `RTDelta._captureCheckboxes()` and `RTFileHandler.exportState()`/`importState()`. (2) Added explicit `N×N` display text updates for matrix size sliders and `Fn` display for radial freq sliders in `importState()`. (3) Added 10 matrix sub-control panel show/hide entries to `importState()`. (4) Added `_subControlMap` to `RTDelta` + visibility toggle in `applyDelta()` for view transitions. + +--- + +### ~~BUG: Radial matrix mode toggles not persisting across save/load~~ — RESOLVED + +~~**Symptom**: Radial matrix render mode toggles — "Space Filling" (Cube), "IVM Mode" (Tet), "IVM Scale" (Octa) — revert to defaults on export/import and are not captured in view deltas. User unchecks Space Filling to get a "Cube Cross" layout, but on reimport the toggle is lost and renders as space-filling.~~ + +**Root cause**: Three radial matrix mode checkboxes were absent from the entire persistence pipeline: +- `radialCubeSpaceFill` — "Space-filling" toggle (default: checked/true) +- `radialTetIVMMode` — "IVM Mode (fill oct voids)" toggle (default: unchecked/false) +- `radialOctIVMScale` — "IVM Scale (match tet faces)" toggle (default: unchecked/false) + +These toggles ARE declared in `rt-ui-binding-defs.js` (simpleCheckboxBindings) and ARE read by `rt-rendering.js` during geometry generation, but were NOT captured in `RTDelta._captureCheckboxes()`, NOT saved in `RTFileHandler.exportState()`, and NOT restored in `RTFileHandler.importState()`. Identical pattern to the earlier rotate45 gap. + +**Fix**: (1) Added 3 radial mode toggle IDs to `RTDelta._captureCheckboxes()`. (2) Added 3 toggles to `RTFileHandler.exportState()` sliders section (using `?? true` for spaceFill default, `|| false` for IVM modes). (3) Added restore loop in `RTFileHandler.importState()` after the rotate45 block, following the same `sliders[id]` pattern. + +--- + +### ~~BUG: Double updateGeometry() per animation tick in buildSteppedTick()~~ — RESOLVED + +~~**Symptom**: During Camera + Scene animated transitions, continuous `requestAnimationFrame` violations at ~77-83ms per frame. Radial matrix transitions especially slow, with occasional 287ms spikes.~~ + +**Root cause**: `buildSteppedTick()` in `rt-delta.js` called `_setSlider()` which dispatches an 'input' event — triggering `updateGeometry()` via the UI binding system. Then `buildSteppedTick()` ALSO called `updateGeometry()` explicitly at the end of each tick. Result: **two geometry rebuilds per tick** for every slider change during animated transitions. + +**Fix**: Removed the redundant explicit `updateGeometry()` call from `buildSteppedTick()`. The `_setSlider()` → 'input' event → UI binding handler already triggers geometry rebuilds. Explicit `updateGeometry()` is now only called for projection radio snaps (which don't dispatch events). This halves the geometry rebuild cost during animated transitions. + +**Note**: The continuous ~78ms rAF violations during radial matrix preview are partly inherent — F3-F4 radial matrices create many nested polyhedra shells, and the THREE.js render pass for this geometry is expensive. The double-rebuild fix helps, but complex radial matrices will still be GPU-heavy. + +--- + +### POSSIBLE BUG: Browser freeze after tab switch during preview + +**Symptom**: App froze/crashed when switching away from the browser tab and back during preview playback. May be caused by `requestAnimationFrame` queuing up while the tab is backgrounded, then firing in a burst when the tab regains focus — especially if `updateGeometry()` is called many times in rapid succession. If this reproduces, investigate throttling the animation tick when returning from a background state. + --- ## TO BE INVESTIGATED +### Optimize Matrix Dissolve — Skip Geometry Rebuilds for Opacity-Only Ticks + +Matrix dissolve transitions currently call `updateGeometry()` every tick, which destroys and recreates the entire matrix mesh just to change opacity. For radial matrices at F3-F4, each rebuild takes 52-276ms (see rAF violations in Logs). Since dissolve-only ticks change `dissolveOpacity` but not structure (no checkbox, slider, or projection changes), the system could instead traverse existing child meshes and update `material.opacity` directly — avoiding the expensive teardown/rebuild cycle. + +**Approach**: In `_setupFormDissolve()` (rt-animate.js), detect whether the group is a matrix type (check `_subControlMap` keys or a `userData.isMatrix` flag). For matrix groups, instead of calling `updateGeometry()`, walk the group's children and set `child.material.opacity = effectiveOpacity` on each mesh. Only fall back to `updateGeometry()` if structure actually changed (checkbox/slider delta in the same tick). + +**Player Piano implication**: Future demo reel scripts should dissolve radial matrices into view at their final target frequency rather than animating frequency ramps (F1→F4). Frequency stepping creates N expensive geometry rebuilds per transition. Instead, set the target frequency in the view delta and let the dissolve system fade the completed geometry in/out smoothly. Reserve stepped frequency animation for planar matrices where the rebuild cost is low (cube/tet at size 2-5 is cheap). + +--- + ### Per-Polyhedron Scale in Delta System The delta system currently captures `scaleSlider` (global) and `tetScaleSlider` (tetrahedron) — but polyhedra can also have **individual scale** set through the state manager. The sizes of things are recorded in export files, so this data exists in `RTFileHandler.exportState()` somewhere beyond the two sliders already in `RTDelta._sliderMap`. @@ -976,3 +1072,9 @@ The delta system currently captures `scaleSlider` (global) and `tetScaleSlider` - **`disposal=2`**: Critical for transparent animated GIFs (clears each frame before drawing next) - **SVG+SMIL browser support**: Chrome, Firefox, Safari, Edge — all modern browsers (not IE) - **Smoothstep easing** `t²(3-2t)`: Natural deceleration at keyframes, zero-velocity endpoints + +### Player Piano Demo Reel + +`PlayerPiano-generator.js` is a browser console script that generates a 30-view `.artviews` demo reel touring all major form categories (primitives, platonic solids, geodesic spheres, planar matrices with 45° rotations and size ramps, radial matrices with frequency ramps, tetrahelixes). Run in console → downloads `.artview` → import and play via Camera + Scene ▶ Preview. + +**Future enhancement**: A second Player Piano pass with cutplanes sweeping through geometry during transitions — each view enables a section cut that animates across the form, revealing interior structure. diff --git a/Geometry documents/Geometry Archived/Janus-Inversion-JAN23.tex b/Geometry documents/Geometry Archived/Janus-Inversion-JAN23.tex deleted file mode 100644 index 2d97f27b..00000000 --- a/Geometry documents/Geometry Archived/Janus-Inversion-JAN23.tex +++ /dev/null @@ -1,531 +0,0 @@ -\documentclass[11pt,a4paper]{article} -\usepackage[utf8]{inputenc} -\usepackage[T1]{fontenc} -\usepackage{amsmath,amssymb,amsthm} -\usepackage{geometry} -\usepackage{hyperref} -\usepackage{graphicx} -\usepackage{booktabs} -\usepackage{enumitem} -\usepackage{xcolor} -\usepackage{parskip} % Adds vertical space between paragraphs - -\geometry{margin=1in} - -\hypersetup{ - colorlinks=true, - linkcolor=blue, - urlcolor=blue, - citecolor=blue -} - -\newtheorem{conjecture}{Conjecture} -\newtheorem{definition}{Definition} -\newtheorem{observation}{Observation} - -\title{Geometric Janus Inversion:\\ -\large Extending the Janus Point from Temporal to Spatial Geometry\\ -via Tetrahedral (Quadray) Coordinates} - -\author{Andrew Thomson\\ -\small Open Building / ARTexplorer Project\\ -\small \href{mailto:andy@openbuilding.ca}{andy@openbuilding.ca}} - -\date{January 2026} - -\begin{document} - -\maketitle - -\begin{abstract} -This document proposes an extension of Julian Barbour's Janus Point concept from temporal cosmology to spatial geometry. Using tetrahedral coordinates (Quadray/WXYZ), we demonstrate that the origin serves as a dimensional transition point---a \emph{geometric} Janus Point---through which forms can scale into what we term ``negative dimensional space.'' We argue that the Cartesian coordinate system's symmetric positive/negative axes structurally obscured this possibility, while tetrahedral coordinates, with their all-positive basis spanning 3D space, naturally prompt the question: what constitutes a negative position? The answer points toward a complementary dimensional realm that may have physical implications for understanding phenomena from particles to black holes. -\end{abstract} - -\tableofcontents -\newpage - -%============================================================================== -\section{Introduction and Acknowledgment} -%============================================================================== - -This work draws significant inspiration from Julian Barbour's \emph{The Janus Point: A New Theory of Time} (2020) and the foundational paper with Koslowski and Mercati, ``Identification of a Gravitational Arrow of Time'' (\emph{Physical Review Letters} 113:181101, 2014). - -Barbour's insight that the Big Bang may represent not a beginning but a \emph{pivot point}---the Janus Point---from which time extends in two directions, provides the conceptual foundation for what we propose here: that spatial geometry may possess an analogous structure, with the origin serving as a dimensional transition point between positive and negative geometric spaces. - -We acknowledge that Barbour's work addresses \emph{temporal} reversal and the arrow of time, not spatial inversion. What follows is an independent extension of the Janus Point concept to geometry, developed through the lens of R. Buckminster Fuller's synergetic geometry, Kirby Urner's Quadray concepts and N.J. Wildberger's rational trigonometry. We present this not as established physics but as a geometric framework awaiting rigorous formalization. - -%============================================================================== -\section{Tetrahedral (Quadray) Coordinates} -%============================================================================== - -\subsection{Definition and Basis Vectors} - -Quadray coordinates employ four basis vectors emanating from a central origin toward the vertices of a regular tetrahedron: - -\begin{definition}[Quadray Basis Vectors] -The four basis vectors $\mathbf{W}, \mathbf{X}, \mathbf{Y}, \mathbf{Z}$ point from the origin to the vertices of a regular tetrahedron: -\begin{align} -\mathbf{W} &= (1, 0, 0, 0) \quad \longleftrightarrow \quad \frac{1}{\sqrt{3}}(+1, +1, +1) \text{ in Cartesian}\\ -\mathbf{X} &= (0, 1, 0, 0) \quad \longleftrightarrow \quad \frac{1}{\sqrt{3}}(+1, -1, -1)\\ -\mathbf{Y} &= (0, 0, 1, 0) \quad \longleftrightarrow \quad \frac{1}{\sqrt{3}}(-1, +1, -1)\\ -\mathbf{Z} &= (0, 0, 0, 1) \quad \longleftrightarrow \quad \frac{1}{\sqrt{3}}(-1, -1, +1) -\end{align} -\end{definition} - -\subsection{Key Properties} - -\begin{enumerate}[label=(\roman*)] -\item \textbf{Vectorial Neutrality:} $\mathbf{W} + \mathbf{X} + \mathbf{Y} + \mathbf{Z} = \mathbf{0}$ (sum to zero in Cartesian space) -\item \textbf{Tetrahedral Angle:} The angle between any pair of basis vectors is $\arccos(-\frac{1}{3}) \approx 109.47^\circ$ -\item \textbf{Spread:} Using rational trigonometry, the spread between any pair is $s = \frac{8}{9}$ -\item \textbf{Zero-Sum Constraint:} For any point $P = (w, x, y, z)$, normalization requires $w + x + y + z = k$ for some constant $k$, reducing 4 coordinates to 3 effective degrees of freedom -\item \textbf{All-Positive Spanning:} Critically, all points in ordinary 3D space can be expressed with \emph{non-negative} coordinates only -\end{enumerate} - -\subsection{The Standard Quadray Rules and Our Extension} - -Kirby Urner's introduction to Quadray coordinates\footnote{\url{http://www.grunch.net/synergetics/quadintro.html}} presents two fundamental rules: - -\begin{enumerate} -\item \textbf{Rule 1:} ``At least one of the four quadrays is always zero'' -\item \textbf{Rule 2:} ``Only positive numbers (and zero) are needed for any ray'' -\end{enumerate} - -These rules follow from the vectorial neutrality property: since $\mathbf{W} + \mathbf{X} + \mathbf{Y} + \mathbf{Z} = \mathbf{0}$, it follows that $-\mathbf{W} = \mathbf{X} + \mathbf{Y} + \mathbf{Z}$. To move in the ``opposite'' direction of W, you don't need a negative W coordinate---you simply use the sum of the other three basis vectors. Any point in 3D space lies in the positive span of at least three of the four basis vectors, making the fourth superfluous and one coordinate always zero. - -\textbf{Standard Quadray implementations enforce coordinate substitution:} -\begin{equation} -(-1, 0, 0, 0) \quad \longrightarrow \quad (0, 1, 1, 1) \quad \text{(equivalent position, all-positive form)} -\end{equation} - -This substitution maintains the all-positive rule by replacing negative coordinates with their equivalent positive-sum representation. - -\begin{observation}[ARTexplorer's Intentional Extension] -ARTexplorer \textbf{deliberately breaks} the all-positive rule to enable continuous motion through the origin. When you drag a form along $-\mathbf{W}$: -\begin{itemize} -\item The W coordinate continuously decreases through zero -\item It naturally becomes negative -\item We do \textbf{not} substitute $(0, 1, 1, 1)$---we preserve the signed coordinate path -\end{itemize} -This is not a bug or oversight. Coordinate substitution would obscure the geometry of scaling through origin and make Janus Inversion impossible to implement as a continuous operation. -\end{observation} - -\begin{table}[h] -\centering -\small -\begin{tabular}{lll} -\toprule -\textbf{Aspect} & \textbf{Standard Quadray} & \textbf{ARTexplorer Extended}\\ -\midrule -Negative coordinates & Prohibited (substitute) & Permitted (meaningful)\\ -At axis crossing & Replace with positive sum & Continue smoothly\\ -Zero-sum constraint & Enforced (3 DOF) & Optional (native 4 DOF)\\ -Janus Inversion & Not applicable & Core feature\\ -Compatible with XYZ & Isomorphic & Extended (4D$^\pm$)\\ -\bottomrule -\end{tabular} -\caption{Standard Quadray (Urner/Ace) vs.\ ARTexplorer's extended framework} -\end{table} - -The standard Quadray rules define a \textbf{coordinate language} for ordinary 3D space---elegant, sufficient, and isomorphic to Cartesian. Our extension uses Quadray as a \textbf{foundation} for exploring regions and operations that the all-positive constraint deliberately excludes. We honor Urner's framework by understanding exactly what it achieves and why; we extend it by asking what becomes visible when the constraint is relaxed. - -\subsection{Native Degrees of Freedom: 3, 4, or 5?} - -Quadray coordinates are conventionally described as providing 3 degrees of freedom. This arises from the \textbf{zero-sum constraint}: if $W + X + Y + Z = k$ for some constant $k$, then knowing any three coordinates determines the fourth. This constraint is imposed to ensure Quadray maps onto Cartesian 3D space---it is a \emph{compatibility requirement}, not an intrinsic property of tetrahedral coordinates. - -However, the tetrahedron---the minimum structural system capable of enclosing space---requires \textbf{four vertices} to define. You cannot specify a tetrahedron with three coordinates; the fourth is not redundant information but essential geometric content. - -\begin{observation}[The Deformed Tetrahedron] -Consider two points: $(1, 1, 1, 1)$ and $(1, 1, 1, 6)$. With the zero-sum constraint, these would be normalized to equivalent positions. But they describe fundamentally different geometric relationships: the first is symmetric (equidistant from all basis directions), the second represents a \textbf{deformed tetrahedron} stretched along the Z-axis. The fourth coordinate carries real information that the constraint destroys. -\end{observation} - -\begin{table}[h] -\centering -\begin{tabular}{ccp{7cm}} -\toprule -\textbf{Framing} & \textbf{DOF} & \textbf{What it describes}\\ -\midrule -Quadray as XYZ substitute & 3 & Zero-sum constraint enforced; equivalent to Cartesian\\ -Quadray as native system & \textbf{4} & Four independent coordinates; no external constraint\\ -Quadray with Janus extension & \textbf{4 + 1} & Four coordinates plus dimensional polarity ($\pm$)\\ -\bottomrule -\end{tabular} -\caption{Degrees of freedom under different interpretations} -\end{table} - -\textbf{The Fifth Degree: Dimensional Polarity (License Pending).} If we accept that positive and negative Quadray spaces (4D$^+$ and 4D$^-$) represent distinct dimensional realms separated by the Janus Point at origin, then a complete specification requires not only the four coordinates but also \emph{which side of origin} the point occupies. This dimensional polarity is not a continuous degree of freedom but a discrete binary state---yet it represents information that four unsigned coordinates cannot capture. - -Whether this constitutes a ``fifth dimension'' or merely a binary flag on a 4D system is a matter of interpretation. No dimensional licensing board exists to adjudicate the question. The framework is coherent either way.\footnote{Application submitted to the Universal Dimensional Licensing Board, 2026.January.Earthtime. Awaiting response.} - -The native Quadray system, without the zero-sum constraint, is a \textbf{4-dimensional coordinate system} that can describe tetrahedral deformations, asymmetries, and (with signed values) passage through the dimensional Janus Point. The 3 DOF interpretation is a projection onto Cartesian-compatible space---useful, but not the full picture. - -\subsection{Critical Clarification: Negative Coordinates $\neq$ Negative Dimensional Space} - -\textbf{For mathematicians and careful readers:} There is a crucial distinction between \emph{negative coordinate values} and \emph{negative dimensional space}. Conflating these would be a category error. - -When you translate an object along the $-W$ direction (past the origin on the W axis), the W coordinate becomes negative. But you have \textbf{not} changed dimensional state---you are still in positive dimensional space, just located in a region where one coordinate happens to be negative. This is exactly analogous to Cartesian coordinates: moving from $X = +5$ to $X = -5$ doesn't transport you to another dimension. - -\textbf{The 16 Regions of Full Signed Quadray Space.} In Cartesian XYZ, we have $2^3 = 8$ octants. In full signed WXYZ (without zero-sum constraint), we have $2^4 = 16$ regions: - -\begin{table}[h] -\centering -\begin{tabular}{cccp{4.5cm}} -\toprule -\textbf{Sign Pattern} & \textbf{\# Neg} & \textbf{Dimensional State} & \textbf{Notes}\\ -\midrule -$(+,+,+,+)$ & 0 & \textbf{4D$^+$} & Canonical positive space\\ -$(+,+,+,-)$ & 1 & 4D$^+$ & Ordinary space (one negative)\\ -$(+,+,-,-)$ & 2 & 4D$^+$ & Ordinary space (two negative)\\ -$(+,-,-,-)$ & 3 & 4D$^+$ & Ordinary space (three negative)\\ -$(-,-,-,-)$ & 4 & \textbf{4D$^-$} & Canonical negative space\\ -\bottomrule -\end{tabular} -\caption{The 16 regions of signed Quadray space (5 representative patterns shown; 11 additional permutations exist for mixed-sign cases)} -\end{table} - -\textbf{What Triggers Janus Inversion?} The Janus Point transition occurs \textbf{only} when passing between the two canonical regions: $(+,+,+,+) \longleftrightarrow (-,-,-,-)$. This happens through \textbf{scaling through zero}---the form itself collapses through the origin and re-emerges inverted---NOT through translation past zero on individual axes. - -\begin{table}[h] -\centering -\begin{tabular}{p{4cm}p{4.5cm}c} -\toprule -\textbf{Operation} & \textbf{Effect} & \textbf{State Change?}\\ -\midrule -Translate along $-W$ & W goes negative, others unchanged & \textbf{NO}\\ -Translate along $-W$, $-X$ & W and X go negative & \textbf{NO}\\ -Scale uniformly through zero & ALL coordinates pass through zero & \textbf{YES}\\ -\bottomrule -\end{tabular} -\caption{Translation vs. scaling: only uniform scaling through zero triggers dimensional inversion} -\end{table} - -The 14 mixed-sign regions are simply \textbf{ordinary navigable space}---they're ``over there'' relative to origin, but they're not dimensionally inverted. This is exactly how ARTexplorer behaves: translation allows negative coordinates without Janus effects; scaling through zero triggers the full transition. - -\subsection{Summary: The Full Dimensional Accounting} - -\begin{table}[h] -\centering -\begin{tabular}{cccp{5cm}} -\toprule -\textbf{Aspect} & \textbf{Count} & \textbf{Type} & \textbf{Notes}\\ -\midrule -Basis vectors & 4 & --- & W, X, Y, Z\\ -Axial directions & 8 & --- & $\pm$W, $\pm$X, $\pm$Y, $\pm$Z\\ -Spatial regions & 16 & --- & $2^4$ sign combinations\\ -Continuous DOF & \textbf{4} & Continuous & Position in tetrahedral space\\ -Dimensional polarity & \textbf{1} & Binary & 4D$^+$ or 4D$^-$\\ -\textbf{Total specification} & \textbf{4 + 1} & Mixed & 4 continuous + 1 binary\\ -\bottomrule -\end{tabular} -\caption{Complete dimensional accounting for the Quadray system} -\end{table} - -Whether to call this a ``5-dimensional system'' is a matter of convention. The dimensional polarity is not a continuous degree of freedom---it's a discrete binary state. We prefer the notation \textbf{4D$^\pm$} as the most precise description: a 4-dimensional continuous space with a discrete positive/negative dimensional state. - -\subsection{The Unit Tetrahedron} - -The fundamental unit in Quadray space has vertices at the four basis directions: - -\begin{table}[h] -\centering -\begin{tabular}{ccc} -\toprule -\textbf{Vertex} & \textbf{Quadray $(W, X, Y, Z)$} & \textbf{Description}\\ -\midrule -$V_0$ & $(1, 0, 0, 0)$ & W-axis vertex\\ -$V_1$ & $(0, 1, 0, 0)$ & X-axis vertex\\ -$V_2$ & $(0, 0, 1, 0)$ & Y-axis vertex\\ -$V_3$ & $(0, 0, 0, 1)$ & Z-axis vertex\\ -\bottomrule -\end{tabular} -\caption{Unit tetrahedron vertices in Quadray coordinates} -\end{table} - -The centroid lies at the origin $(0, 0, 0, 0)$. - -%============================================================================== -\section{The Dual Tetrahedron and Dimensional Inversion} -%============================================================================== - -\subsection{The Inversion Operation} - -The dual tetrahedron is obtained by inverting through the origin---a 180° rotation or, equivalently, multiplication by $-1$: - -\begin{table}[h] -\centering -\begin{tabular}{ccc} -\toprule -\textbf{Vertex} & \textbf{Raw Negative Form} & \textbf{Re-normalized (adding $(1,1,1,1)$)}\\ -\midrule -$V'_0$ & $(-1, 0, 0, 0)$ & $(0, 1, 1, 1)$\\ -$V'_1$ & $(0, -1, 0, 0)$ & $(1, 0, 1, 1)$\\ -$V'_2$ & $(0, 0, -1, 0)$ & $(1, 1, 0, 1)$\\ -$V'_3$ & $(0, 0, 0, -1)$ & $(1, 1, 1, 0)$\\ -\bottomrule -\end{tabular} -\caption{Dual tetrahedron vertices: raw negative and re-normalized forms} -\end{table} - -\subsection{The Topological Question} - -In classical topology, a closed genus-0 surface (sphere, tetrahedron) cannot be turned inside-out in 3D without tearing. Yet in Quadray space, we perform exactly this operation: the tetrahedron passes through the origin to become its dual. - -\begin{conjecture}[Dimensional Transition] -The origin in Quadray space is not merely ``empty space'' but a \textbf{transition point between positive and negative dimensional spaces}. The inversion operation does not require topological tearing because it occurs through dimensional transition---analogous to passing through zero on a number line. -\end{conjecture} - -This interpretation preserves the genus-0 nature of the tetrahedron while permitting ``inside-outing.'' The key insight is that Quadray coordinates, with their inherent 4-dimensionality (4 basis vectors constrained to 3 DOF), provide a natural higher-dimensional embedding in which such inversion becomes a rigid motion rather than a topological impossibility. - -%============================================================================== -\section{The Geometric Janus Point} -%============================================================================== - -\subsection{Analogy to Barbour's Temporal Janus Point} - -\begin{table}[h] -\centering -\begin{tabular}{p{6cm}p{6cm}} -\toprule -\textbf{Barbour's Janus Point} & \textbf{Geometric Janus Point}\\ -\midrule -The Big Bang is a central pivot from which time extends in two directions & The origin $(0,0,0,0)$ is a pivot from which positive and negative dimensional spaces extend\\ -\addlinespace -The universe passed through minimal size/complexity at the Janus Point & Forms pass through minimal extension (the origin) during inversion\\ -\addlinespace -Two arrows of time emerge, each with increasing complexity & Two dimensional spaces (4D$^+$ and 4D$^-$) emerge, each containing complete geometric structure\\ -\addlinespace -Observers on either side perceive their direction as ``forward'' & Observers in either dimensional space would perceive their forms as the ``base'' state\\ -\bottomrule -\end{tabular} -\caption{Correspondence between temporal and geometric Janus Points} -\end{table} - -\subsection{Fuller's IN/OUT Directionality} - -R. Buckminster Fuller criticized ``Up'' and ``Down'' as flat-earth artifacts. On a sphere, the only absolute directions are \textbf{IN} (toward center) and \textbf{OUT} (away from center). We map: - -\begin{align} -\text{Positive } (+) &\longleftrightarrow \text{OUT: expansion away from origin}\\ -\text{Negative } (-) &\longleftrightarrow \text{IN: collapse through origin} -\end{align} - -A form with all-negative Quadray coordinates has not merely ``moved to the other side''---it has passed \emph{through} the origin into what we term \textbf{negative dimensional space} (4D$^-$). - - -\begin{figure}[ht] - \centering - \includegraphics[width=0.75\linewidth]{Screenshot 2026-01-22 at 12.03.30 PM.png} - \caption{Quadray basis vectors with tetrahedral arrows pointing outward, indicating positive dimensional space (4D$^+$).} - \label{fig:positive-space} -\end{figure} - -\begin{figure}[ht] - \centering - \includegraphics[width=0.75\linewidth]{Screenshot 2026-01-22 at 12.03.17 PM.png} - \caption{After Janus Inversion: tetrahedral coordinates invert to dual form, background converts to white, basis vector arrows point toward origin indicating negative dimensional space (4D$^-$).} - \label{fig:negative-space} -\end{figure} -%============================================================================== -\section{The Cartesian Blind Spot} -%============================================================================== - -\subsection{Why This Remained Hidden} - -Human habituation to Cartesian coordinates may have long obscured the possibility of negative dimensional space. - -\begin{observation}[The Cartesian Blind Spot] -In Cartesian coordinates, the eight octants created by $\pm X$, $\pm Y$, $\pm Z$ all remain \emph{within} the same 3D reference frame. Negative coordinates simply point the other direction---there is no conceptual ``outside'' to Cartesian space. -\end{observation} - -Quadray coordinates operate differently. With four basis vectors spanning all of 3D space using \emph{non-negative values only}, the question immediately arises: \textbf{what is a negative position in this framework?} - -The answer cannot be ``the other direction''---the four basis vectors already cover all directions with positive values. The only coherent interpretation is that negative Quadray coordinates represent existence in a \textbf{complementary dimensional realm}. - -\subsection{Visual Demonstration} - -In our visualization software (ARTexplorer), when XYZ basis vectors invert through the origin, they merely flip from right-hand to left-hand orientation---remaining recognizably within the same spatial framework. But when Quadray-defined forms invert through the geometric Janus Point, something categorically different occurs: they pass into a space that positive Quadray coordinates cannot describe. - -The $\pm(1,1,1,1)$ normalization bridge between tetrahedron and dual tetrahedron hints at this hidden realm, but Cartesian thinking---with its symmetrical positive/negative axes---provided no reason to look for it. - -%============================================================================== -\section{Mathematical Formalization} -%============================================================================== - -\subsection{Dimensional State} - -For any point $P = (w, x, y, z)$ in Quadray space: - -\begin{definition}[Dimensional State] -\begin{align} -P \in 4D^+ &\quad \text{if all coordinates are non-negative (positive dimensional space)}\\ -P \in 4D^- &\quad \text{if all coordinates are non-positive (negative dimensional space)}\\ -P \in \partial &\quad \text{if coordinates have mixed signs (boundary/transition zone)} -\end{align} -\end{definition} - -\subsection{The Inversion Operator} - -The geometric Janus inversion is mathematically equivalent to: - -\begin{enumerate}[label=(\roman*)] -\item Multiplication by $-1$: $P' = -P$ -\item 180° rotation through the origin in 4D space -\item Application of the inversion matrix: $\text{diag}(-1, -1, -1, -1)$ -\end{enumerate} - -None of these operations require a topological ``hole''---only the existence of a higher-dimensional embedding, which Quadray coordinates inherently provide. - -\subsection{The Normalization Bridge} - -The dual tetrahedron can be re-expressed in positive coordinates by adding $(1,1,1,1)$: - -\begin{equation} -V'_i(\text{positive}) = V'_i(\text{negative}) + (1, 1, 1, 1) -\end{equation} - -This $\pm(1,1,1,1)$ translation serves as the \textbf{bridge between dimensional spaces}---a mathematical operation that projects negative-space forms into positive-space representation (and vice versa). - -%============================================================================== -\section{Speculative Extensions} -%============================================================================== - -The following conjectures emerge from this geometric framework. They are recorded not as claims but as directions for exploration: - -\subsection{Scale-Invariant Janus Points} - -If the geometric Janus Point operates at one scale, it may operate at all scales: - -\begin{itemize} -\item \textbf{Subatomic:} What we observe as ``particles'' may be local eddies of dimensional inversion---stable configurations oscillating through microscopic Janus Points -\item \textbf{Cosmic:} Black holes may be macro-scale Janus Points where spacetime itself inverts through the origin -\end{itemize} - -\subsection{Energy Twinning} - -Every manifestation of energy in positive space may have a paired ``twin'' in negative space. This is distinct from antimatter (which exists in positive space with opposite charge)---it represents a complementary existence across the dimensional boundary. Conservation laws may be shadows of a deeper bidimensional conservation. - -\subsection{The Dual as Shadow} - -The dual tetrahedron, rendered in positive space via $+(1,1,1,1)$ normalization, is already a \emph{projection} of negative space into our realm. Global inversion doesn't create something new---it reveals what was always there. - -\subsection{Cyclic Cosmology} - -Combining Barbour's Janus Point with ancient cosmological intuitions (the Vedic ``breath of Brahma,'' Gurdjieff's Trogoautoegocrat) suggests the universe may not pass through the Janus Point once but \emph{repeatedly}---an eternal oscillation between expansion and contraction, positive and negative dimensional states. - -%============================================================================== -\section{Visualization and Implementation} -%============================================================================== - -We have implemented this framework in ARTexplorer, an interactive 3D geometry visualization tool. Key behaviors: - -\begin{itemize} -\item Forms can be scaled through zero via direct manipulation -\item Crossing the origin triggers a visual ``Janus transition''---a golden flash at the geometric Janus Point -\item The background inverts from black to white when forms enter negative dimensional space, providing an unmistakable perceptual signal of dimensional state -\item Non-selected forms become translucent ``ghosts'' during the transition, emphasizing the dimensional boundary crossing -\end{itemize} - -The software is available at: \url{https://arossti.github.io/ARTexplorer/} - -%============================================================================== -\section{Anticipated Objections and Response} -%============================================================================== - -\subsection{The XYZ Rendering Objection} - -A mathematician could object: ``The current ARTexplorer implementation renders everything through THREE.js, which uses standard XYZ Cartesian coordinates. The `Janus Inversion' is simply \texttt{scale.set(-1, -1, -1)}---ordinary negative scaling that any 3D engine can perform. XYZ handles negative coordinates perfectly well. Nothing is `hidden'---the entire demonstration occurs in Cartesian space at the GPU level.'' - -This objection deserves a direct response. - -\subsection{What We Already Have} - -The mathematical foundations in ARTexplorer are substantial: - -\begin{enumerate} -\item \textbf{Quadray Basis Vectors}---Four tetrahedral basis vectors with precise Cartesian equivalents, maintaining the property that all positive combinations span 3D space -\item \textbf{Rational Trigonometry}---Quadrance ($Q = d^2$) and spread ($s = \sin^2\theta$) calculations that maintain algebraic exactness throughout geometric operations -\item \textbf{The $\pm(1,1,1,1)$ Normalization Bridge}---A mathematically defined translation between positive and negative Quadray representations -\item \textbf{Weierstrass Substitution}---Pure rational rotation without transcendental functions -\item \textbf{Algebraic Exactness}---XYZ conversion deferred to the GPU boundary; intermediate calculations remain in rational form -\end{enumerate} - -The current implementation performs geometry in Quadray/RT space before converting to XYZ for rendering. The question is not whether we have mathematics---we do---but whether the XYZ rendering layer undermines the conceptual claim about negative dimensionality. - -\subsection{The Subtler Claim} - -We do not claim that XYZ \emph{cannot} represent inverted geometry---it obviously can. The claim is that XYZ's symmetric $\pm$ axes make the \emph{question} of negative dimensionality structurally invisible. In XYZ, the point $(-1, -1, -1)$ is simply ``the opposite octant''---still conceptually within the same 3D space. The framework never prompts you to ask ``what \emph{is} negative space?'' - -In Quadray coordinates, where all positive values already span 3D, negative coordinates have no directional interpretation. This forces a categorically different question: negative \emph{what}? - -\subsection{Future Development: Native 4D Rendering} - -To fully realize the mathematical framework, future work should eliminate the XYZ conversion entirely: - -\begin{enumerate} -\item \textbf{Native 4D Rendering Engine}---A purpose-built renderer operating in tetrahedral coordinate space, where all transformations occur in WXYZ using rational algebra, with no Cartesian conversion until final pixel output. This would handle negative Quadray coordinates natively, not as XYZ proxies, and potentially achieve computational efficiency gains from tetrahedral symmetry. - -\item \textbf{Extended Signed Quadray Algebra}---Formalization of the mathematics of WXYZ where negative values are permitted, including quadrance calculations across the $\pm(1,1,1,1)$ boundary, transformation matrices for 4D$^\pm$ space, and topological characterization of the origin as dimensional transition point. -\end{enumerate} - -The current ARTexplorer demonstrates Janus Inversion through XYZ rendering, but the underlying Quadray mathematics is real and operational. The visual metaphor is built on genuine algebraic foundations---foundations that await only a native 4D renderer to be fully expressed. - -%============================================================================== -\section{Conclusion} -%============================================================================== - -We propose that Julian Barbour's Janus Point, originally conceived as a temporal pivot in cosmology, may have a geometric analog: the origin in tetrahedral (Quadray) coordinates serves as a transition point between positive and negative dimensional spaces. - -This possibility remained hidden because Cartesian coordinates, with their symmetric $\pm$ axes, made the question structurally irrelevant. Only by adopting a coordinate system where all of 3D space can be described entirely with non-negative values does the deeper question emerge: what then in this system is a \emph{negative} position? - -The answer points toward a complementary dimensional realm---4D$^-$---that may have implications for understanding phenomena from quantum mechanics to black holes. We offer this framework not as established physics but as a geometric intuition awaiting rigorous formalization, in the spirit of Barbour's own exploratory approach to fundamental questions about the nature of space and time. - -%============================================================================== -\section*{Acknowledgments} -%============================================================================== - -This work draws on the insights of Julian Barbour, R. Buckminster Fuller, and N.J. Wildberger. The geometric intuitions emerged from decades of contemplative practice and were developed in collaboration with AI assistance (Claude/Anthropic) for implementation and documentation. - -Special thanks to: -\begin{itemize} -\item Rudolf Dorenach, Bucky's German associate and my first Synergetics mentor -\item Kirby Urner, for introducing me to Quadray coordinates -\item Tom Ace, for his work on the basis vector conversion methodology -\item Gerald DeJong, for introducing me to Wildberger's Rational Trigonometry -\item Dawn Danby and David McConville, for their moral support and enthusiasm -\item Bonnie DeVarco, for her tireless preservation and active engagement with Fuller's original work -\item Mark Pavlidis, for teaching me Git discipline and the importance of clean code -\item Enzyme APD, for encouraging the pursuit of these ideas against all odds -\end{itemize} - -\subsection*{Note on the Janus Point (January 2026)} - -In correspondence with the author (22 January 2026, by email), Dr.\ Julian Barbour graciously responded to an early draft of this work. He indicated that he now favors \textbf{monodirectional} Big Bang solutions (as described in Chapter 16 of \emph{The Janus Point}) over bidirectional Janus Point solutions when Newtonian absolute elements are fully eliminated from the theory. - -Our geometric extension---applying the Janus Point concept to spatial rather than temporal structure---operates in a different domain and does not depend on the cosmological resolution of this question. The geometric Janus Point we describe is a property of tetrahedral coordinate systems, not a claim about the arrow of time. - -We are grateful for Dr.\ Barbour's engagement and particularly for his observation that ``science should be about shapes rather than dynamics''---a view that aligns naturally with our tetrahedral geometric approach and with Fuller's emphasis on structure over motion. - -%============================================================================== -\section*{References} -%============================================================================== - -\begin{enumerate} -\item Barbour, J. (2020). \emph{The Janus Point: A New Theory of Time}. Basic Books. -\item Barbour, J., Koslowski, T., \& Mercati, F. (2014). ``Identification of a Gravitational Arrow of Time.'' \emph{Physical Review Letters}, 113:181101. -\item Fuller, R.B. (1975). \emph{Synergetics: Explorations in the Geometry of Thinking}. Macmillan. -\item Wildberger, N.J. (2005). \emph{Divine Proportions: Rational Trigonometry to Universal Geometry}. Wild Egg Books. -\item CPT Symmetry. \emph{Wikipedia}. \url{https://en.wikipedia.org/wiki/CPT_symmetry} -\item Mandelbrot, B. ``Negative Fractal Dimensions and Multifractals.'' Yale University. \url{https://users.math.yale.edu/users/mandelbrot/web_pdfs/123negativeFractalDimensions.pdf} -\item Urner, K. ``Quadray Coordinates: A Logical Alternative.'' \url{http://www.grunch.net/synergetics/quadintro.html} -\item Ace, T. ``Quadray Coordinates.'' \url{http://minortriad.com/quadray.html} (C++ implementation) -\item DOI: 10.13140/RG.2.2.10492.19848 | CC BY-NC-ND 4.0 -\end{enumerate} - -\vfill -\begin{center} -\textit{``The question simply cannot arise within Cartesian assumptions.\\ -Only by adopting a coordinate system where `negative' has no directional meaning\\ -does the deeper question emerge: negative \textbf{what}, exactly?''} -\end{center} - -\end{document} diff --git a/Geometry documents/Geometry Archived/Universalize-Logs.md b/Geometry documents/Geometry Archived/Universalize-Logs.md new file mode 100644 index 00000000..577e82fc --- /dev/null +++ b/Geometry documents/Geometry Archived/Universalize-Logs.md @@ -0,0 +1,162 @@ +# Workplan: Universalize Polyhedra Console Logs + +## Problem + +Console logging across polyhedra is **wildly inconsistent**: + +| Form | Current Format | Issues | +|------|---------------|--------| +| Cube | `Cube: Expected Q=2.000, Max error=0.00e+0, Face spread S=1` | No title, no construction info, no Schlafli | +| Tetrahedron | One-liner + separate rendering.js block | Split across two files, but rendering.js block is gold standard format | +| Dual Tetrahedron | `Dual Tetrahedron: Expected Q=..., Max error=...` | No face spread, no construction info | +| Octahedron | One-liner with Q, error, spread | Same as cube — no title, no construction | +| Icosahedron | 10-line PurePhi symbolic block + validation | Massive, dumps internal algebra that's useful but overwhelming | +| Dodecahedron | 4-line `[ThreeRT]` header + validation | Different prefix convention, decent structure | +| Dual Icosahedron | 7-line `[ThreeRT]` header + validation | Heavy, includes rotation matrix details | +| Cuboctahedron | 4-line `[ThreeRT]` header + validation | Decent, shows √2 construction | +| Rhombic Dodecahedron | 5-line `[ThreeRT]` header + validation | Decent, shows dual construction | +| Truncated Tet | Conditional one-liners (varies by t) | Different format for t=0, t=0.5, general | +| Geodesics | Multi-line with PurePhi (varies by projection) | Good detail, but varies between geodesic types | +| Compounds | **No logging at all** | Silent by design (internal calls silenced) | +| Tetrahelices | 5-6 line blocks with RT validation | Reasonable, but different convention | + +### Key inconsistencies: +1. **No universal title** — some use `===`, some use `[ThreeRT]`, some use `[PurePhi]`, most use nothing +2. **Information varies** — cube gets 1 line, icosahedron gets 11 lines +3. **Missing data** — some skip V/E/F counts, Schlafli symbols, face spread, or construction method +4. **Split concerns** — tetrahedron construction info is in rt-rendering.js, not rt-polyhedra.js +5. **Prefix soup** — `[PurePhi]`, `[ThreeRT]`, `[RT]`, or no prefix at all + +## Gold Standard: Tetrahedron (rendering.js block) + +``` +=== TETRAHEDRON EDGE LENGTH 2 === +HalfSize (s): 0.7071067811865475 +Edge length (2s√2): 2.0000000000000000 +OutSphere radius (s√3): 1.2247448713915889 +Grid interval (√6/4): 0.6123724356957945 +``` + +What makes this good: +- **Clear `===` title** — scannable, grep-friendly, stands out in console +- **Labeled values with formulas** — `Edge length (2s√2): 2.000...` shows both the algebraic expression AND the numeric value +- **Full precision** — 16 decimal places, respects RT philosophy +- **Relevant derived metrics** — outsphere, grid interval (useful for IVM alignment) + +## Proposed Universal Format + +### Section 1: Identity (every form) +``` +=== TETRAHEDRON {3,3} === + Construction: Algebraic (vertices at ±s) + HalfSize (s): 0.707107 + V: 4, E: 6, F: 4 + Euler: V - E + F = 2 ✓ +``` + +### Section 2: RT Metrics (every form) +``` + Edge Q (8s²): 4.000000 + Edge length (2s√2): 2.000000 + Max Q error: 0.00e+0 + Face spread S: 0.888889 (8/9) +``` + +### Section 3: Construction Details (form-specific, opt-in) +For phi-based forms (icosahedron, dodecahedron, dual icosahedron): +``` + [PurePhi] φ = (1 + √5)/2 = 1.618033988749895 + [PurePhi] φ² = φ + 1 = 2.618033988749895 + Normalization: 1/√(1 + φ²) = 0.525731112119134 + Identity check: |φ² - (φ + 1)| = 0e+0 ✓ +``` + +For √2-based forms (cuboctahedron, rhombic dodecahedron): +``` + [PureRadicals] √2 = 1.414214 (cached) + Vertex distance: s/√2 = 0.500000 +``` + +For geodesic forms: +``` + Base: Icosahedron (V: 12, F: 20) + Subdivision: freq=3, F: 180 + Projection: OutSphere (Fuller geodesic) + Target Q: 0.001600, r: 0.040000 + Edge Q: avg=0.000251, max error=7.77e-5 +``` + +### Section 4: Sphere Metrics (every closed form) +``` + OutSphere Q (3s²): 1.500000, r: 1.224745 + MidSphere Q (s²): 0.500000, r: 0.707107 + InSphere Q (s²/3): 0.166667, r: 0.408248 +``` + +## Design Principles + +1. **Title is always `=== NAME {Schlafli} ===`** — one convention, grep-able +2. **Indented body** — 2-space indent for all detail lines under title +3. **Formulas shown** — `Edge Q (8s²): 4.000` not just `Edge Q: 4.000` +4. **Consistent sections** — Identity → RT Metrics → Construction → Spheres +5. **Opt-in verbosity** — Section 3 (construction) only for forms that need it (phi, √2, geodesic). Simple platonic solids (cube, tet, oct) skip it. +6. **Compounds log their result** — Even though internal calls are silent, the compound itself should log its hull identity +7. **Tetrahelices** — Same pattern: `=== TETRAHELIX-1 (TOROIDAL, 10 TET) ===` with V/E/F and Q validation + +## What Each Form Should Report + +| Form | Schlafli | Construction Method | Special Constants | Sphere Metrics | +|------|----------|-------------------|-------------------|----------------| +| Cube | {4,3} | Algebraic (±s) | — | Out/Mid/In | +| Tetrahedron | {3,3} | Algebraic (±s) | — | Out/Mid/In | +| Dual Tetrahedron | {3,3} | Algebraic (±s, dual parity) | — | Out/Mid/In | +| Octahedron | {3,4} | Algebraic (±s axis) | — | Out/Mid/In | +| Icosahedron | {3,5} | PurePhi normalization | φ, φ², 1/φ | Out/Mid/In | +| Dodecahedron | {5,3} | PurePhi (cube + phi vertices) | φ, 1/φ | Out/Mid/In | +| Dual Icosahedron | — | PurePhi (scaled icosa) | φ, rotation | Out/Mid/In | +| Cuboctahedron | — | PureRadicals | √2 | Out/Mid/In | +| Rhombic Dodecahedron | — | PureRadicals (dual cubocta) | √2 | Out/Mid/In | +| Truncated Tet | — | Parametric (t) | truncation ratio | — | +| Geodesic Icosa | — | Subdivide + Project | freq, projection | Target Q | +| Geodesic Tet | — | Subdivide + Project | freq, projection | Target Q | +| Geodesic Octa | — | Subdivide + Project | freq, projection | Target Q | +| Compound TT+Icosa | — | Hull of compound | — | — | +| Compound TT+Tet | — | Hull of compound | — | — | +| Tetrahelix 1 | — | Face-bonding chain | chirality, count | — | +| Tetrahelix 2 | — | Javelin chain | strands, bondMode | — | +| Tetrahelix 3 | — | Octahedral seed chain | strands, chirality | — | + +## Implementation + +### Files to modify: +- `modules/rt-polyhedra.js` — All polyhedra log blocks (~15 functions) +- `modules/rt-helices.js` — All 3 tetrahelix log blocks +- `modules/rt-rendering.js` — Move the tetrahedron `=== EDGE LENGTH ===` block to polyhedra.js (or remove the rendering.js version and let polyhedra.js own all construction logging) + +### Helper function (optional): +```javascript +// Could add a shared log formatter to reduce boilerplate: +function logPolyhedronIdentity(name, schlafli, data, options = {}) { + console.log(`=== ${name} ${schlafli} ===`); + console.log(` V: ${data.V}, E: ${data.E}, F: ${data.F}`); + console.log(` Euler: V - E + F = ${data.V - data.E + data.F} ${data.V - data.E + data.F === 2 ? '✓' : '✗'}`); + console.log(` Edge Q: ${data.edgeQ.toFixed(6)}, Max error: ${data.maxError.toExponential(2)}`); + if (data.faceSpread !== undefined) { + console.log(` Face spread S: ${data.faceSpread.toFixed(6)} (${data.spreadFraction})`); + } +} +``` + +### Migration strategy: +1. Create the helper function (or inline the format) +2. Update one form at a time, starting with Platonic solids (simplest) +3. Test each form's console output after modification +4. Move tetrahedron rendering.js block to polyhedra.js +5. Update phi-based forms (icosa, dodec, dual icosa) last (most complex) + +## Open Questions + +- Should sphere metrics (Out/Mid/In) be logged for every form, or only on request? +- Should construction details (PurePhi, PureRadicals) be a separate verbosity level? +- Should compounds log their component names + hull vertex count? +- Should the helper function live in rt-math.js (shared) or rt-polyhedra.js (local)? diff --git a/modules/rt-animate.js b/modules/rt-animate.js index b71e80d3..cd27f510 100644 --- a/modules/rt-animate.js +++ b/modules/rt-animate.js @@ -91,6 +91,8 @@ export const RTAnimate = { // Capture start state const startPos = camera.position.clone(); const startDist = startPos.length(); + const startDir = startPos.clone().normalize(); + const startUp = camera.up.clone(); const startZoom = camera.zoom; // Target state from saved view @@ -100,8 +102,24 @@ export const RTAnimate = { view.camera.position.z ); const endDist = endPos.length(); + const endDir = endPos.clone().normalize(); const endZoom = view.camera.zoom || 1; + // Compute end up vector — match setCameraPreset Z-pole handling + // When camera is near the Z axis, lookAt(origin) with up=(0,0,1) is degenerate + // because look direction becomes parallel to up vector. Use Y-up instead. + const zAxis = new THREE.Vector3(0, 0, 1); + const endUp = Math.abs(endDir.dot(zAxis)) > 0.95 + ? new THREE.Vector3(0, endDir.z > 0 ? 1 : -1, 0) + : new THREE.Vector3(0, 0, 1); + + // Pre-compute slerp parameters for direction interpolation + // Angle-based slerp handles all angular separations correctly (unlike lerp+normalize + // which breaks near antipodal directions where the midpoint approaches zero) + const dirDot = THREE.MathUtils.clamp(startDir.dot(endDir), -1, 1); + const omega = Math.acos(dirDot); + const sinOmega = Math.sin(omega); + const startTime = performance.now(); this.state.active = true; @@ -113,13 +131,25 @@ export const RTAnimate = { const rawT = Math.min(elapsed / durationMs, 1); const t = rawT * rawT * (3 - 2 * rawT); // smoothstep - // Slerp on sphere: lerp directions + interpolate distance - const pos = new THREE.Vector3().lerpVectors(startPos, endPos, t); + // Direction slerp on the unit sphere (robust for all angular separations) + let dir; + if (omega < 0.001) { + // Nearly identical directions — just use start + dir = startDir.clone(); + } else { + const a = Math.sin((1 - t) * omega) / sinOmega; + const b = Math.sin(t * omega) / sinOmega; + dir = startDir.clone().multiplyScalar(a).addScaledVector(endDir, b); + } + const dist = startDist + (endDist - startDist) * t; - pos.normalize().multiplyScalar(dist); + camera.position.copy(dir.multiplyScalar(dist)); - camera.position.copy(pos); - camera.up.set(0, 0, 1); // Z-up convention + // Interpolate up vector (avoids lookAt singularity at Z poles) + const up = new THREE.Vector3().lerpVectors(startUp, endUp, t); + if (up.lengthSq() < 0.001) up.set(0, 0, 1); // safety for antipodal ups + up.normalize(); + camera.up.copy(up); camera.lookAt(0, 0, 0); camera.zoom = startZoom + (endZoom - startZoom) * t; camera.updateProjectionMatrix(); diff --git a/modules/rt-delta.js b/modules/rt-delta.js index c2fec2c7..1c3ca326 100644 --- a/modules/rt-delta.js +++ b/modules/rt-delta.js @@ -38,7 +38,14 @@ export const RTDelta = { // Primitives "showPoint", "showLine", "showPolygon", "showPrism", "showCone", // Helices - "showTetrahelix1", "showTetrahelix2", + "showTetrahelix1", "showTetrahelix2", "showTetrahelix3", + // Tetrahelix 3 strand + chirality toggles + "tetrahelix3StrandA", "tetrahelix3StrandB", "tetrahelix3StrandC", + "tetrahelix3StrandD", "tetrahelix3StrandE", "tetrahelix3StrandF", + "tetrahelix3StrandG", "tetrahelix3StrandH", + "tetrahelix3ChiralA", "tetrahelix3ChiralB", "tetrahelix3ChiralC", + "tetrahelix3ChiralD", "tetrahelix3ChiralE", "tetrahelix3ChiralF", + "tetrahelix3ChiralG", "tetrahelix3ChiralH", // Regular polyhedra "showCube", "showTetrahedron", "showDualTetrahedron", "showOctahedron", "showIcosahedron", "showDodecahedron", @@ -54,10 +61,15 @@ export const RTDelta = { // Planar matrices "showCubeMatrix", "showTetMatrix", "showOctaMatrix", "showCuboctahedronMatrix", "showRhombicDodecMatrix", + // Matrix rotation toggles (45° grid alignment) + "cubeMatrixRotate45", "tetMatrixRotate45", "octaMatrixRotate45", + "cuboctaMatrixRotate45", "rhombicDodecMatrixRotate45", // Radial matrices "showRadialCubeMatrix", "showRadialRhombicDodecMatrix", "showRadialTetrahedronMatrix", "showRadialOctahedronMatrix", "showRadialCuboctahedronMatrix", + // Radial matrix mode toggles (Space Filling / IVM) + "radialCubeSpaceFill", "radialTetIVMMode", "radialOctIVMScale", // Basis vectors "showCartesianBasis", "showQuadray", // Penrose Tiling @@ -101,6 +113,29 @@ export const RTDelta = { geodesicDualIcosaFrequency: "geodesicDualIcosaFrequency", }, + /** + * Checkbox ID → sub-control panel ID mapping. + * Used by applyDelta() to show/hide sub-control panels when checkboxes change. + * Mirrors the checkbox-controls bindings in rt-ui-binding-defs.js. + * @private + */ + _subControlMap: { + // Planar matrices + showCubeMatrix: "cube-matrix-controls", + showTetMatrix: "tet-matrix-controls", + showOctaMatrix: "octa-matrix-controls", + showCuboctahedronMatrix: "cubocta-matrix-controls", + showRhombicDodecMatrix: "rhombic-dodec-matrix-controls", + // Radial matrices + showRadialCubeMatrix: "radial-cube-matrix-controls", + showRadialRhombicDodecMatrix: "radial-rhombic-dodec-matrix-controls", + showRadialTetrahedronMatrix: "radial-tetrahedron-matrix-controls", + showRadialOctahedronMatrix: "radial-octahedron-matrix-controls", + showRadialCuboctahedronMatrix: "radial-cuboctahedron-matrix-controls", + // Tetrahelix 3 + showTetrahelix3: "tetrahelix3-controls", + }, + /** @private */ _captureSliders() { const result = {}; @@ -231,6 +266,16 @@ export const RTDelta = { const el = document.getElementById(id); if (el) el.checked = checked; } + + // Show/hide sub-control panels for checkbox-with-controls forms + for (const [checkboxId, controlsId] of Object.entries(this._subControlMap)) { + if (delta.polyhedraCheckboxes[checkboxId] !== undefined) { + const panel = document.getElementById(controlsId); + if (panel) { + panel.style.display = delta.polyhedraCheckboxes[checkboxId] ? "block" : "none"; + } + } + } } // 4. Trigger geometry rebuild (single call, like importState) @@ -358,6 +403,7 @@ export const RTDelta = { } // Snap projections at midpoint + let projSnapped = false; for (const snap of projSnaps) { if (!snap.applied && t >= 0.5) { const radio = document.querySelector( @@ -365,12 +411,14 @@ export const RTDelta = { ); if (radio) radio.checked = true; snap.applied = true; - needsRebuild = true; + projSnapped = true; } } - // Single geometry rebuild if anything changed this tick - if (needsRebuild && window.renderingAPI?.updateGeometry) { + // Geometry rebuild: _setSlider() dispatches 'input' events which trigger + // updateGeometry() via the UI binding system. Only call explicitly for + // projection snaps (which don't dispatch events). + if (projSnapped && window.renderingAPI?.updateGeometry) { window.renderingAPI.updateGeometry(); } @@ -385,9 +433,11 @@ export const RTDelta = { `input[name="${snap.name}"][value="${snap.to}"]` ); if (radio) radio.checked = true; + projSnapped = true; } } - if (window.renderingAPI?.updateGeometry) { + // Only explicit rebuild for remaining projection snaps + if (projSnapped && window.renderingAPI?.updateGeometry) { window.renderingAPI.updateGeometry(); } } diff --git a/modules/rt-filehandler.js b/modules/rt-filehandler.js index a07fc7b3..d1bfec06 100644 --- a/modules/rt-filehandler.js +++ b/modules/rt-filehandler.js @@ -371,6 +371,24 @@ export const RTFileHandler = { rhombicDodecMatrixSizeSlider: parseInt( document.getElementById("rhombicDodecMatrixSizeSlider")?.value || "1" ), + // Planar matrix 45° rotation toggles + cubeMatrixRotate45: + document.getElementById("cubeMatrixRotate45")?.checked || false, + tetMatrixRotate45: + document.getElementById("tetMatrixRotate45")?.checked || false, + octaMatrixRotate45: + document.getElementById("octaMatrixRotate45")?.checked || false, + cuboctaMatrixRotate45: + document.getElementById("cuboctaMatrixRotate45")?.checked || false, + rhombicDodecMatrixRotate45: + document.getElementById("rhombicDodecMatrixRotate45")?.checked || false, + // Radial matrix mode toggles (Space Filling / IVM) + radialCubeSpaceFill: + document.getElementById("radialCubeSpaceFill")?.checked ?? true, + radialTetIVMMode: + document.getElementById("radialTetIVMMode")?.checked || false, + radialOctIVMScale: + document.getElementById("radialOctIVMScale")?.checked || false, // Radial matrix frequency sliders radialCubeFreqSlider: parseInt( document.getElementById("radialCubeFreqSlider")?.value || "1" @@ -839,47 +857,61 @@ export const RTFileHandler = { if (checkbox) checkbox.checked = sliders[chiralKey]; } } - // Planar matrix size sliders - if (sliders.cubeMatrixSizeSlider !== undefined) { - const el = document.getElementById("cubeMatrixSizeSlider"); - if (el) el.value = sliders.cubeMatrixSizeSlider; - } - if (sliders.tetMatrixSizeSlider !== undefined) { - const el = document.getElementById("tetMatrixSizeSlider"); - if (el) el.value = sliders.tetMatrixSizeSlider; - } - if (sliders.octaMatrixSizeSlider !== undefined) { - const el = document.getElementById("octaMatrixSizeSlider"); - if (el) el.value = sliders.octaMatrixSizeSlider; - } - if (sliders.cuboctaMatrixSizeSlider !== undefined) { - const el = document.getElementById("cuboctaMatrixSizeSlider"); - if (el) el.value = sliders.cuboctaMatrixSizeSlider; - } - if (sliders.rhombicDodecMatrixSizeSlider !== undefined) { - const el = document.getElementById("rhombicDodecMatrixSizeSlider"); - if (el) el.value = sliders.rhombicDodecMatrixSizeSlider; - } - // Radial matrix frequency sliders - if (sliders.radialCubeFreqSlider !== undefined) { - const el = document.getElementById("radialCubeFreqSlider"); - if (el) el.value = sliders.radialCubeFreqSlider; - } - if (sliders.radialRhombicDodecFreqSlider !== undefined) { - const el = document.getElementById("radialRhombicDodecFreqSlider"); - if (el) el.value = sliders.radialRhombicDodecFreqSlider; + // Planar matrix size sliders (set value + update N×N display text) + const matrixSizeSliders = [ + ["cubeMatrixSizeSlider", "cubeMatrixSizeValue"], + ["tetMatrixSizeSlider", "tetMatrixSizeValue"], + ["octaMatrixSizeSlider", "octaMatrixSizeValue"], + ["cuboctaMatrixSizeSlider", "cuboctaMatrixSizeValue"], + ["rhombicDodecMatrixSizeSlider", "rhombicDodecMatrixSizeValue"], + ]; + for (const [sliderId, displayId] of matrixSizeSliders) { + if (sliders[sliderId] !== undefined) { + const el = document.getElementById(sliderId); + if (el) el.value = sliders[sliderId]; + const display = document.getElementById(displayId); + if (display) { + const v = sliders[sliderId]; + display.textContent = `${v}×${v}`; + } + } } - if (sliders.radialTetFreqSlider !== undefined) { - const el = document.getElementById("radialTetFreqSlider"); - if (el) el.value = sliders.radialTetFreqSlider; + // Planar matrix 45° rotation toggles + const rotate45Ids = [ + "cubeMatrixRotate45", "tetMatrixRotate45", "octaMatrixRotate45", + "cuboctaMatrixRotate45", "rhombicDodecMatrixRotate45", + ]; + for (const id of rotate45Ids) { + if (sliders[id] !== undefined) { + const el = document.getElementById(id); + if (el) el.checked = sliders[id]; + } } - if (sliders.radialOctFreqSlider !== undefined) { - const el = document.getElementById("radialOctFreqSlider"); - if (el) el.value = sliders.radialOctFreqSlider; + // Radial matrix mode toggles (Space Filling / IVM) + const radialModeIds = [ + "radialCubeSpaceFill", "radialTetIVMMode", "radialOctIVMScale", + ]; + for (const id of radialModeIds) { + if (sliders[id] !== undefined) { + const el = document.getElementById(id); + if (el) el.checked = sliders[id]; + } } - if (sliders.radialVEFreqSlider !== undefined) { - const el = document.getElementById("radialVEFreqSlider"); - if (el) el.value = sliders.radialVEFreqSlider; + // Radial matrix frequency sliders (set value + update Fn display text) + const radialFreqSliders = [ + ["radialCubeFreqSlider", "radialCubeFreqDisplay", v => `F${2 * v - 1}`], + ["radialRhombicDodecFreqSlider", "radialRhombicDodecFreqDisplay", v => `F${2 * v - 1}`], + ["radialTetFreqSlider", "radialTetFreqDisplay", v => `F${v}`], + ["radialOctFreqSlider", "radialOctFreqDisplay", v => `F${v}`], + ["radialVEFreqSlider", "radialVEFreqDisplay", v => `F${v}`], + ]; + for (const [sliderId, displayId, formatFn] of radialFreqSliders) { + if (sliders[sliderId] !== undefined) { + const el = document.getElementById(sliderId); + if (el) el.value = sliders[sliderId]; + const display = document.getElementById(displayId); + if (display) display.textContent = formatFn(sliders[sliderId]); + } } // Geodesic frequency sliders if (sliders.geodesicTetraFrequency !== undefined) { @@ -1051,6 +1083,26 @@ export const RTFileHandler = { quadrayTruncTetControls.style.display = "block"; } + // Show/hide planar matrix controls based on checkbox state + const matrixControlMappings = [ + ["showCubeMatrix", "cube-matrix-controls"], + ["showTetMatrix", "tet-matrix-controls"], + ["showOctaMatrix", "octa-matrix-controls"], + ["showCuboctahedronMatrix", "cubocta-matrix-controls"], + ["showRhombicDodecMatrix", "rhombic-dodec-matrix-controls"], + ["showRadialCubeMatrix", "radial-cube-matrix-controls"], + ["showRadialRhombicDodecMatrix", "radial-rhombic-dodec-matrix-controls"], + ["showRadialTetrahedronMatrix", "radial-tetrahedron-matrix-controls"], + ["showRadialOctahedronMatrix", "radial-octahedron-matrix-controls"], + ["showRadialCuboctahedronMatrix", "radial-cuboctahedron-matrix-controls"], + ]; + for (const [checkboxId, controlsId] of matrixControlMappings) { + if (checkboxes[checkboxId]) { + const panel = document.getElementById(controlsId); + if (panel) panel.style.display = "block"; + } + } + // Trigger updateGeometry to render the restored forms if (window.renderingAPI?.updateGeometry) { window.renderingAPI.updateGeometry(); diff --git a/modules/rt-rendering.js b/modules/rt-rendering.js index c0dcfa14..354fecfa 100644 --- a/modules/rt-rendering.js +++ b/modules/rt-rendering.js @@ -2027,6 +2027,10 @@ export function initScene(THREE, OrbitControls, RT) { cubeMatrixGroup.remove(cubeMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = cubeMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export cubeMatrixGroup.userData.parameters = { matrixSize: matrixSize, @@ -2042,7 +2046,7 @@ export function initScene(THREE, OrbitControls, RT) { matrixSize, scale, rotate45, - opacity, + effectiveOpacity, colorPalette.cubeMatrix, THREE ); @@ -2202,6 +2206,10 @@ export function initScene(THREE, OrbitControls, RT) { tetMatrixGroup.remove(tetMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = tetMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export tetMatrixGroup.userData.parameters = { matrixSize: matrixSize, @@ -2217,7 +2225,7 @@ export function initScene(THREE, OrbitControls, RT) { matrixSize, scale, rotate45, - opacity, + effectiveOpacity, colorPalette.tetrahedron, THREE ); @@ -2280,6 +2288,10 @@ export function initScene(THREE, OrbitControls, RT) { octaMatrixGroup.remove(octaMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = octaMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export octaMatrixGroup.userData.parameters = { matrixSize: matrixSize, @@ -2297,7 +2309,7 @@ export function initScene(THREE, OrbitControls, RT) { scale, rotate45, colinearEdges, - opacity, + effectiveOpacity, colorPalette.octahedronMatrix, THREE ); @@ -2506,6 +2518,10 @@ export function initScene(THREE, OrbitControls, RT) { cuboctaMatrixGroup.remove(cuboctaMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = cuboctaMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export cuboctaMatrixGroup.userData.parameters = { matrixSize: matrixSize, @@ -2521,7 +2537,7 @@ export function initScene(THREE, OrbitControls, RT) { matrixSize, scale, rotate45, - opacity, + effectiveOpacity, colorPalette.cuboctahedron, // Lime-cyan (Vector Equilibrium color) THREE ); @@ -2894,6 +2910,10 @@ export function initScene(THREE, OrbitControls, RT) { rhombicDodecMatrixGroup.remove(rhombicDodecMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = rhombicDodecMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export rhombicDodecMatrixGroup.userData.parameters = { matrixSize: matrixSize, @@ -2911,7 +2931,7 @@ export function initScene(THREE, OrbitControls, RT) { scale, rotate45, faceCoplanar, - opacity, + effectiveOpacity, colorPalette.rhombicDodecahedron, // Golden orange (Rhombic Dodecahedron color) THREE ); @@ -2954,6 +2974,10 @@ export function initScene(THREE, OrbitControls, RT) { radialCubeMatrixGroup.remove(radialCubeMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = radialCubeMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export radialCubeMatrixGroup.userData.parameters = { frequency: frequency, @@ -2969,7 +2993,7 @@ export function initScene(THREE, OrbitControls, RT) { frequency, scale, spaceFilling, - opacity, + effectiveOpacity, colorPalette.cube, // Use cube color THREE ); @@ -3016,6 +3040,10 @@ export function initScene(THREE, OrbitControls, RT) { ); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = radialRhombicDodecMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export radialRhombicDodecMatrixGroup.userData.parameters = { frequency: frequency, @@ -3032,7 +3060,7 @@ export function initScene(THREE, OrbitControls, RT) { frequency, scale, spaceFilling, - opacity, + effectiveOpacity, colorPalette.rhombicDodecahedron, // Use rhombic dodec color THREE ); @@ -3079,6 +3107,10 @@ export function initScene(THREE, OrbitControls, RT) { const ivmMode = document.getElementById("radialTetIVMMode")?.checked || false; + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = radialTetMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export radialTetMatrixGroup.userData.parameters = { frequency: frequency, @@ -3093,7 +3125,7 @@ export function initScene(THREE, OrbitControls, RT) { const radialTetMatrix = RTRadialMatrix.createRadialTetrahedronMatrix( frequency, scale, - opacity, + effectiveOpacity, colorPalette.radialTetrahedron, THREE, ivmMode @@ -3144,6 +3176,10 @@ export function initScene(THREE, OrbitControls, RT) { const ivmScaleOnly = document.getElementById("radialOctIVMScale")?.checked || false; + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = radialOctMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export radialOctMatrixGroup.userData.parameters = { frequency: frequency, @@ -3158,7 +3194,7 @@ export function initScene(THREE, OrbitControls, RT) { const radialOctMatrix = RTRadialMatrix.createRadialOctahedronMatrix( frequency, scale, - opacity, + effectiveOpacity, colorPalette.radialOctahedron, THREE, false, // ivmScale = false (no FCC lattice) @@ -3216,6 +3252,10 @@ export function initScene(THREE, OrbitControls, RT) { radialVEMatrixGroup.remove(radialVEMatrixGroup.children[0]); } + // Apply dissolve opacity for smooth fade transitions + const dissolveOpacity = radialVEMatrixGroup.userData.dissolveOpacity ?? 1.0; + const effectiveOpacity = opacity * dissolveOpacity; + // Store parameters for instance creation/export radialVEMatrixGroup.userData.parameters = { frequency: frequency, @@ -3229,7 +3269,7 @@ export function initScene(THREE, OrbitControls, RT) { const radialVEMatrix = RTRadialMatrix.createRadialCuboctahedronMatrix( frequency, scale, - opacity, + effectiveOpacity, colorPalette.radialCuboctahedron, THREE );