mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
301 lines
11 KiB
HTML
301 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>PIST Obelisk Manifold — Composite Coordinate Viewer</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
overflow: hidden;
|
|
background-color: #050505;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
canvas { display: block; }
|
|
#ui {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 20px;
|
|
color: #00ffcc;
|
|
pointer-events: none;
|
|
text-shadow: 0 0 8px rgba(0, 255, 204, 0.8);
|
|
}
|
|
h1 { margin: 0 0 5px 0; font-size: 1.1rem; letter-spacing: 2px; }
|
|
p { margin: 0; font-size: 0.85rem; opacity: 0.8; }
|
|
#coord-panel {
|
|
position: absolute;
|
|
top: 20px;
|
|
right: 20px;
|
|
color: #ffaa44;
|
|
pointer-events: none;
|
|
text-align: right;
|
|
text-shadow: 0 0 8px rgba(255, 170, 68, 0.6);
|
|
font-size: 0.8rem;
|
|
line-height: 1.6;
|
|
}
|
|
#coord-panel span { color: #00ffcc; }
|
|
#legend {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
color: #888;
|
|
font-size: 0.75rem;
|
|
pointer-events: none;
|
|
}
|
|
.legend-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
|
|
</style>
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
|
|
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="ui">
|
|
<h1>OBLISK MANIFOLD: ACTIVE</h1>
|
|
<p>Nodes Mapped: <span id="nodeCount">0</span> / 2000</p>
|
|
<p>Current Shell: <span id="shellDisplay">k=0</span></p>
|
|
</div>
|
|
|
|
<div id="coord-panel">
|
|
<div>n = <span id="n-val">0</span></div>
|
|
<div>k = <span id="k-val">0</span>, t = <span id="t-val">0</span></div>
|
|
<div>mass = <span id="m-val">0</span></div>
|
|
<div style="margin-top:8px; color:#666; font-size:0.7rem;">
|
|
tree:<br><span id="tree-val">[]</span><br>
|
|
surface: <span id="surf-val">x=0.00 y=0.00</span><br>
|
|
torus: <span id="torus-val">θ=0.00 φ=0.00</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="legend">
|
|
<div><span class="legend-dot" style="background:#00ffcc"></span> tree address (base-20 path)</div>
|
|
<div><span class="legend-dot" style="background:#ff6644"></span> surface coordinate (1/x curve)</div>
|
|
<div><span class="legend-dot" style="background:#4488ff"></span> torus angle (Φ-irrational rotation)</div>
|
|
<div><span class="legend-dot" style="background:#ffaa00"></span> PIST shell position (k, t)</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import * as THREE from 'three';
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
|
// ── PIST Coordinate Primitives ──
|
|
const PHI = (1 + Math.sqrt(5)) / 2;
|
|
|
|
function pistK(n) { return Math.floor(Math.sqrt(n)); }
|
|
function pistT(n) { return n - pistK(n) * pistK(n); }
|
|
function pistMass(k, t) {
|
|
if (k === 0) return 0;
|
|
const tf = Math.min(t, 2 * k + 1 - t);
|
|
return tf * (2 * k + 1 - tf);
|
|
}
|
|
function treeAddress(n, depth) {
|
|
const path = [];
|
|
let rem = n;
|
|
for (let level = 0; level < depth; level++) {
|
|
path.push({ level, branch: rem % 20 });
|
|
rem = Math.floor(rem / 20);
|
|
}
|
|
return path;
|
|
}
|
|
function surfaceCoord(n) {
|
|
const x = 1.0 + (n % 255) * (255.0 / 255.0);
|
|
return { x: x, y: 1.0 / x, theta: (n * PHI) % (2 * Math.PI) };
|
|
}
|
|
function torusAngles(n) {
|
|
return {
|
|
theta: (n * PHI) % (2 * Math.PI),
|
|
phi: (n * PHI * PHI) % (2 * Math.PI),
|
|
psi: (n * PHI * PHI * PHI) % (2 * Math.PI)
|
|
};
|
|
}
|
|
|
|
// ── Scene ──
|
|
const scene = new THREE.Scene();
|
|
scene.fog = new THREE.FogExp2(0x050505, 0.025);
|
|
|
|
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
camera.position.set(0, 6, 22);
|
|
|
|
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
document.body.appendChild(renderer.domElement);
|
|
|
|
const controls = new OrbitControls(camera, renderer.domElement);
|
|
controls.enableDamping = true;
|
|
controls.dampingFactor = 0.05;
|
|
controls.autoRotate = false;
|
|
|
|
// ── The Obelisk (Torus Knot p=3, q=7) ──
|
|
const radius = 3.5, tube = 1.0;
|
|
const tubularSegments = 400, radialSegments = 64;
|
|
const p = 3, q = 7;
|
|
|
|
const obeliskGeo = new THREE.TorusKnotGeometry(radius, tube, tubularSegments, radialSegments, p, q);
|
|
const obeliskMat = new THREE.MeshBasicMaterial({
|
|
color: 0x2a2a2a,
|
|
wireframe: true,
|
|
transparent: true,
|
|
opacity: 0.25
|
|
});
|
|
const obelisk = new THREE.Mesh(obeliskGeo, obeliskMat);
|
|
obelisk.scale.set(1, 2.2, 1);
|
|
scene.add(obelisk);
|
|
|
|
const nodeGroup = new THREE.Group();
|
|
obelisk.add(nodeGroup);
|
|
|
|
// ── Node Mapping: Real PIST Coordinates ──
|
|
const vertices = obeliskGeo.attributes.position.array;
|
|
const vertexCount = vertices.length / 3;
|
|
let mappedNodes = 0;
|
|
const maxNodes = 2000;
|
|
const mappedIndices = new Set();
|
|
|
|
const nodeCounterUI = document.getElementById('nodeCount');
|
|
const shellDisplay = document.getElementById('shellDisplay');
|
|
|
|
// Color by coordinate type
|
|
function nodeColor(n) {
|
|
const mod = n % 4;
|
|
if (mod === 0) return 0x00ffcc; // tree: cyan
|
|
if (mod === 1) return 0xff6644; // surface: orange-red
|
|
if (mod === 2) return 0x4488ff; // torus: blue
|
|
return 0xffaa00; // shell: amber
|
|
}
|
|
|
|
function mapNode() {
|
|
if (mappedNodes >= maxNodes) return;
|
|
|
|
// Pick an unmapped vertex index
|
|
let idx;
|
|
let attempts = 0;
|
|
do {
|
|
idx = Math.floor(Math.random() * vertexCount);
|
|
attempts++;
|
|
} while (mappedIndices.has(idx) && attempts < 50);
|
|
if (mappedIndices.has(idx)) return;
|
|
mappedIndices.add(idx);
|
|
|
|
const vi = idx * 3;
|
|
const x = vertices[vi];
|
|
const y = vertices[vi + 1];
|
|
const z = vertices[vi + 2];
|
|
|
|
// Compute real PIST coordinates for this node index
|
|
const k = pistK(mappedNodes);
|
|
const t = pistT(mappedNodes);
|
|
const mass = pistMass(k, t);
|
|
const tree = treeAddress(mappedNodes, 3);
|
|
const surf = surfaceCoord(mappedNodes);
|
|
const torus = torusAngles(mappedNodes);
|
|
|
|
const color = nodeColor(mappedNodes);
|
|
const size = 0.04 + (mass / 5000) * 0.08; // mass = bigger
|
|
|
|
const nodeGeo = new THREE.SphereGeometry(size, 8, 8);
|
|
const nodeMat = new THREE.MeshBasicMaterial({ color });
|
|
const node = new THREE.Mesh(nodeGeo, nodeMat);
|
|
node.position.set(x, y, z);
|
|
|
|
// Store PIST data on the mesh for hover/click
|
|
node.userData = { n: mappedNodes, k, t, mass, tree, surf, torus };
|
|
nodeGroup.add(node);
|
|
|
|
mappedNodes++;
|
|
nodeCounterUI.innerText = mappedNodes;
|
|
shellDisplay.innerText = `k=${k}, t=${t}, mass=${mass}`;
|
|
}
|
|
|
|
const intervalId = setInterval(mapNode, 30);
|
|
|
|
// ── Active Indicator (follows current n) ──
|
|
const activeGeo = new THREE.SphereGeometry(0.25, 16, 16);
|
|
const activeMat = new THREE.MeshBasicMaterial({
|
|
color: 0xffffff, transparent: true, opacity: 0.9
|
|
});
|
|
const activeMesh = new THREE.Mesh(activeGeo, activeMat);
|
|
scene.add(activeMesh);
|
|
activeMesh.visible = false;
|
|
|
|
// ── Raycaster for hover ──
|
|
const raycaster = new THREE.Raycaster();
|
|
const mouse = new THREE.Vector2();
|
|
|
|
function onMouseMove(event) {
|
|
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
|
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
|
}
|
|
window.addEventListener('mousemove', onMouseMove, false);
|
|
|
|
// ── UI update ──
|
|
const uiN = document.getElementById('n-val');
|
|
const uiK = document.getElementById('k-val');
|
|
const uiT = document.getElementById('t-val');
|
|
const uiM = document.getElementById('m-val');
|
|
const uiTree = document.getElementById('tree-val');
|
|
const uiSurf = document.getElementById('surf-val');
|
|
const uiTorus = document.getElementById('torus-val');
|
|
|
|
function updateUIPanel(data) {
|
|
uiN.textContent = data.n;
|
|
uiK.textContent = data.k;
|
|
uiT.textContent = data.t;
|
|
uiM.textContent = data.mass;
|
|
uiTree.textContent = JSON.stringify(data.tree.map(n => n.branch));
|
|
uiSurf.textContent = `x=${data.surf.x.toFixed(2)} y=${data.surf.y.toFixed(4)}`;
|
|
uiTorus.textContent = `θ=${data.torus.theta.toFixed(2)} φ=${data.torus.phi.toFixed(2)}`;
|
|
}
|
|
|
|
// ── Animation ──
|
|
const clock = new THREE.Clock();
|
|
let currentN = 0;
|
|
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
const delta = clock.getDelta();
|
|
|
|
obelisk.rotation.y += 0.15 * delta;
|
|
|
|
// Cycle active indicator through mapped nodes
|
|
if (mappedNodes > 0) {
|
|
currentN = (currentN + 1) % mappedNodes;
|
|
const children = nodeGroup.children;
|
|
if (children[currentN]) {
|
|
const pos = children[currentN].position.clone();
|
|
pos.applyMatrix4(obelisk.matrixWorld);
|
|
activeMesh.position.copy(pos);
|
|
activeMesh.visible = true;
|
|
updateUIPanel(children[currentN].userData);
|
|
}
|
|
}
|
|
|
|
// Raycaster hover
|
|
raycaster.setFromCamera(mouse, camera);
|
|
const intersects = raycaster.intersectObjects(nodeGroup.children);
|
|
if (intersects.length > 0) {
|
|
const hit = intersects[0].object;
|
|
if (hit.userData && hit.userData.n !== undefined) {
|
|
updateUIPanel(hit.userData);
|
|
activeMesh.position.copy(hit.position.clone().applyMatrix4(obelisk.matrixWorld));
|
|
}
|
|
}
|
|
|
|
controls.update();
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
|
|
animate();
|
|
</script>
|
|
</body>
|
|
</html>
|