refraction almost completed
This commit is contained in:
331
app/ui-cube-math.js
Normal file
331
app/ui-cube-math.js
Normal file
@@ -0,0 +1,331 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function createCubeMathHelpers(dependencies) {
|
||||
const {
|
||||
state,
|
||||
CUBE_VERTICES,
|
||||
FACE_GEOMETRY,
|
||||
EDGE_GEOMETRY,
|
||||
EDGE_GEOMETRY_KEYS,
|
||||
CUBE_VIEW_CENTER,
|
||||
WALL_FRONT_ROTATIONS,
|
||||
LOCAL_DIRECTION_VIEW_MAP,
|
||||
normalizeId,
|
||||
normalizeLetterKey,
|
||||
normalizeEdgeId,
|
||||
formatDirectionName,
|
||||
getEdgesForWall,
|
||||
getEdgePathEntry,
|
||||
getEdgeLetterId,
|
||||
getCubeCenterData
|
||||
} = dependencies || {};
|
||||
|
||||
function normalizeAngle(angle) {
|
||||
let next = angle;
|
||||
while (next > 180) {
|
||||
next -= 360;
|
||||
}
|
||||
while (next <= -180) {
|
||||
next += 360;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function setRotation(nextX, nextY) {
|
||||
state.rotationX = normalizeAngle(nextX);
|
||||
state.rotationY = normalizeAngle(nextY);
|
||||
}
|
||||
|
||||
function snapRotationToWall(wallId) {
|
||||
const target = WALL_FRONT_ROTATIONS[normalizeId(wallId)];
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
setRotation(target.x, target.y);
|
||||
}
|
||||
|
||||
function facePoint(quad, u, v) {
|
||||
const weight0 = ((1 - u) * (1 - v)) / 4;
|
||||
const weight1 = ((1 + u) * (1 - v)) / 4;
|
||||
const weight2 = ((1 + u) * (1 + v)) / 4;
|
||||
const weight3 = ((1 - u) * (1 + v)) / 4;
|
||||
|
||||
return {
|
||||
x: quad[0].x * weight0 + quad[1].x * weight1 + quad[2].x * weight2 + quad[3].x * weight3,
|
||||
y: quad[0].y * weight0 + quad[1].y * weight1 + quad[2].y * weight2 + quad[3].y * weight3
|
||||
};
|
||||
}
|
||||
|
||||
function projectVerticesForRotation(rotationX, rotationY) {
|
||||
const yaw = (rotationY * Math.PI) / 180;
|
||||
const pitch = (rotationX * Math.PI) / 180;
|
||||
|
||||
const cosY = Math.cos(yaw);
|
||||
const sinY = Math.sin(yaw);
|
||||
const cosX = Math.cos(pitch);
|
||||
const sinX = Math.sin(pitch);
|
||||
|
||||
const centerX = CUBE_VIEW_CENTER.x;
|
||||
const centerY = CUBE_VIEW_CENTER.y;
|
||||
const scale = 54;
|
||||
const camera = 4.6;
|
||||
|
||||
return CUBE_VERTICES.map(([x, y, z]) => {
|
||||
const x1 = x * cosY + z * sinY;
|
||||
const z1 = -x * sinY + z * cosY;
|
||||
|
||||
const y2 = y * cosX - z1 * sinX;
|
||||
const z2 = y * sinX + z1 * cosX;
|
||||
|
||||
const perspective = camera / (camera - z2);
|
||||
|
||||
return {
|
||||
x: centerX + x1 * scale * perspective,
|
||||
y: centerY + y2 * scale * perspective,
|
||||
z: z2
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function projectVertices() {
|
||||
return projectVerticesForRotation(state.rotationX, state.rotationY);
|
||||
}
|
||||
|
||||
function getEdgeGeometryById(edgeId) {
|
||||
const canonicalId = normalizeEdgeId(edgeId);
|
||||
const geometryIndex = EDGE_GEOMETRY_KEYS.indexOf(canonicalId);
|
||||
if (geometryIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
return EDGE_GEOMETRY[geometryIndex] || null;
|
||||
}
|
||||
|
||||
function getWallEdgeDirections(wallOrWallId) {
|
||||
const wallId = normalizeId(typeof wallOrWallId === "string" ? wallOrWallId : wallOrWallId?.id);
|
||||
const faceIndices = FACE_GEOMETRY[wallId];
|
||||
if (!Array.isArray(faceIndices) || faceIndices.length !== 4) {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
const frontRotation = WALL_FRONT_ROTATIONS[wallId] || {
|
||||
x: state.rotationX,
|
||||
y: state.rotationY
|
||||
};
|
||||
const projectedVertices = projectVerticesForRotation(frontRotation.x, frontRotation.y);
|
||||
const quad = faceIndices.map((index) => projectedVertices[index]);
|
||||
const center = facePoint(quad, 0, 0);
|
||||
const directionsByEdgeId = new Map();
|
||||
|
||||
getEdgesForWall(wallId).forEach((edge) => {
|
||||
const geometry = getEdgeGeometryById(edge?.id);
|
||||
if (!geometry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [fromIndex, toIndex] = geometry;
|
||||
const from = projectedVertices[fromIndex];
|
||||
const to = projectedVertices[toIndex];
|
||||
if (!from || !to) {
|
||||
return;
|
||||
}
|
||||
|
||||
const midpointX = (from.x + to.x) / 2;
|
||||
const midpointY = (from.y + to.y) / 2;
|
||||
const dx = midpointX - center.x;
|
||||
const dy = midpointY - center.y;
|
||||
|
||||
const directionByPosition = Math.abs(dx) >= Math.abs(dy)
|
||||
? (dx >= 0 ? "east" : "west")
|
||||
: (dy >= 0 ? "south" : "north");
|
||||
const direction = LOCAL_DIRECTION_VIEW_MAP[directionByPosition] || directionByPosition;
|
||||
|
||||
directionsByEdgeId.set(normalizeEdgeId(edge?.id), direction);
|
||||
});
|
||||
|
||||
return directionsByEdgeId;
|
||||
}
|
||||
|
||||
function getEdgeDirectionForWall(wallId, edgeId) {
|
||||
const wallKey = normalizeId(wallId);
|
||||
const edgeKey = normalizeEdgeId(edgeId);
|
||||
if (!wallKey || !edgeKey) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const directions = getWallEdgeDirections(wallKey);
|
||||
return directions.get(edgeKey) || "";
|
||||
}
|
||||
|
||||
function getEdgeDirectionLabelForWall(wallId, edgeId) {
|
||||
return formatDirectionName(getEdgeDirectionForWall(wallId, edgeId));
|
||||
}
|
||||
|
||||
function getHebrewLetterSymbol(hebrewLetterId) {
|
||||
const id = normalizeLetterKey(hebrewLetterId);
|
||||
if (!id || !state.hebrewLetters) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const entry = state.hebrewLetters[id];
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return "";
|
||||
}
|
||||
|
||||
const symbol = String(
|
||||
entry?.letter?.he || entry?.he || entry?.glyph || entry?.symbol || ""
|
||||
).trim();
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
function getHebrewLetterName(hebrewLetterId) {
|
||||
const id = normalizeLetterKey(hebrewLetterId);
|
||||
if (!id || !state.hebrewLetters) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const entry = state.hebrewLetters[id];
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return "";
|
||||
}
|
||||
|
||||
const name = String(entry?.letter?.name || entry?.name || "").trim();
|
||||
return name;
|
||||
}
|
||||
|
||||
function getAstrologySymbol(type, name) {
|
||||
const normalizedType = normalizeId(type);
|
||||
const normalizedName = normalizeId(name);
|
||||
|
||||
const planetSymbols = {
|
||||
mercury: "☿︎",
|
||||
venus: "♀︎",
|
||||
mars: "♂︎",
|
||||
jupiter: "♃︎",
|
||||
saturn: "♄︎",
|
||||
sol: "☉︎",
|
||||
sun: "☉︎",
|
||||
luna: "☾︎",
|
||||
moon: "☾︎",
|
||||
earth: "⊕",
|
||||
uranus: "♅︎",
|
||||
neptune: "♆︎",
|
||||
pluto: "♇︎"
|
||||
};
|
||||
|
||||
const zodiacSymbols = {
|
||||
aries: "♈︎",
|
||||
taurus: "♉︎",
|
||||
gemini: "♊︎",
|
||||
cancer: "♋︎",
|
||||
leo: "♌︎",
|
||||
virgo: "♍︎",
|
||||
libra: "♎︎",
|
||||
scorpio: "♏︎",
|
||||
sagittarius: "♐︎",
|
||||
capricorn: "♑︎",
|
||||
aquarius: "♒︎",
|
||||
pisces: "♓︎"
|
||||
};
|
||||
|
||||
const elementSymbols = {
|
||||
fire: "🜂",
|
||||
water: "🜄",
|
||||
air: "🜁",
|
||||
earth: "🜃",
|
||||
spirit: "🜀"
|
||||
};
|
||||
|
||||
if (normalizedType === "planet") {
|
||||
return planetSymbols[normalizedName] || "";
|
||||
}
|
||||
|
||||
if (normalizedType === "zodiac") {
|
||||
return zodiacSymbols[normalizedName] || "";
|
||||
}
|
||||
|
||||
if (normalizedType === "element") {
|
||||
return elementSymbols[normalizedName] || "";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function getCenterLetterId(center = null) {
|
||||
const entry = center || getCubeCenterData();
|
||||
return normalizeLetterKey(entry?.hebrewLetterId || entry?.associations?.hebrewLetterId || entry?.letter);
|
||||
}
|
||||
|
||||
function getCenterLetterSymbol(center = null) {
|
||||
const centerLetterId = getCenterLetterId(center);
|
||||
if (!centerLetterId) {
|
||||
return "";
|
||||
}
|
||||
return getHebrewLetterSymbol(centerLetterId);
|
||||
}
|
||||
|
||||
function getEdgeAstrologySymbol(edge) {
|
||||
const pathEntry = getEdgePathEntry(edge);
|
||||
const astrology = pathEntry?.astrology || {};
|
||||
return getAstrologySymbol(astrology.type, astrology.name);
|
||||
}
|
||||
|
||||
function getEdgeLetter(edge) {
|
||||
const hebrewLetterId = getEdgeLetterId(edge);
|
||||
if (!hebrewLetterId) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return getHebrewLetterSymbol(hebrewLetterId);
|
||||
}
|
||||
|
||||
function getEdgeMarkerDisplay(edge) {
|
||||
const letter = getEdgeLetter(edge);
|
||||
const astro = getEdgeAstrologySymbol(edge);
|
||||
|
||||
if (state.markerDisplayMode === "letter") {
|
||||
return letter
|
||||
? { text: letter, isMissing: false }
|
||||
: { text: "!", isMissing: true };
|
||||
}
|
||||
|
||||
if (state.markerDisplayMode === "astro") {
|
||||
return astro
|
||||
? { text: astro, isMissing: false }
|
||||
: { text: "!", isMissing: true };
|
||||
}
|
||||
|
||||
if (letter && astro) {
|
||||
return { text: `${letter} ${astro}`, isMissing: false };
|
||||
}
|
||||
|
||||
return { text: "!", isMissing: true };
|
||||
}
|
||||
|
||||
return {
|
||||
normalizeAngle,
|
||||
setRotation,
|
||||
snapRotationToWall,
|
||||
facePoint,
|
||||
projectVerticesForRotation,
|
||||
projectVertices,
|
||||
getEdgeGeometryById,
|
||||
getWallEdgeDirections,
|
||||
getEdgeDirectionForWall,
|
||||
getEdgeDirectionLabelForWall,
|
||||
getHebrewLetterSymbol,
|
||||
getHebrewLetterName,
|
||||
getAstrologySymbol,
|
||||
getCenterLetterId,
|
||||
getCenterLetterSymbol,
|
||||
getEdgeAstrologySymbol,
|
||||
getEdgeMarkerDisplay,
|
||||
getEdgeLetter
|
||||
};
|
||||
}
|
||||
|
||||
window.CubeMathHelpers = {
|
||||
createCubeMathHelpers
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user