UNPKG

primitive-geometry

Version:

Geometries for 3D rendering, including normals, UVs and cell indices (faces). Perfect if you want to supercharge your dependency folder... with 30KB of geometries.

216 lines (196 loc) 6.81 kB
/** @module mappings */ import { HALF_PI, SQRT2, TAU } from "./utils.js"; const safeSqrt = (x) => Math.sqrt(Math.max(x, 0)); const safeDivide = (x, y) => x / (y + Number.EPSILON); const isNegligeable = (x) => Math.abs(x) < Number.EPSILON * 2; const remapRectangular = (x, radius) => (x / radius + 1) / 2; const remap = (x) => (x + 1) / 2; // From [-1, 1] to [0, 1] export function rectangular({ uvs, index, x, y, radius, sx = 1, sy = 1 }) { uvs[index] = remapRectangular(x, radius * sx); uvs[index + 1] = remapRectangular(y, radius * sy); } export function polar({ uvs, index, radiusRatio, thetaRatio }) { uvs[index] = radiusRatio; uvs[index + 1] = thetaRatio; } // Basic export function radial({ uvs, index, u, v, radius }) { const x = safeDivide( Math.sqrt(u ** 2 + v ** 2), Math.max(Math.abs(u), Math.abs(v)), ); uvs[index] = remap(x * u, radius); uvs[index + 1] = remap(x * v, radius); } const FOUR_OVER_PI = 4 / Math.PI; export function concentric({ uvs, index, u, v, radius }) { const u2 = u ** 2; const v2 = v ** 2; const x = Math.sqrt(u2 + v2); if (u2 > v2) { uvs[index] = remap(x * Math.sign(u), radius); uvs[index + 1] = remap( x * (FOUR_OVER_PI * Math.atan(safeDivide(v, Math.abs(u)))), radius, ); } else { uvs[index] = remap( x * (FOUR_OVER_PI * Math.atan(safeDivide(u, Math.abs(v)))), radius, ); uvs[index + 1] = remap(x * Math.sign(v), radius); } } export function lamé({ uvs, index, u, v, radius }) { const u2 = u ** 2; const v2 = v ** 2; uvs[index] = remap(Math.sign(u) * Math.abs(u) ** (1 - u2 - v2), radius); uvs[index + 1] = remap(Math.sign(v) * Math.abs(v) ** (1 - u2 - v2), radius); } export function elliptical({ uvs, index, u, v, radius }) { const t = u ** 2 - v ** 2; const pu1 = 0.5 * safeSqrt(2 + t + 2 * SQRT2 * u); const pu2 = 0.5 * safeSqrt(2 + t - 2 * SQRT2 * u); const pv1 = 0.5 * safeSqrt(2 - t + 2 * SQRT2 * v); const pv2 = 0.5 * safeSqrt(2 - t - 2 * SQRT2 * v); uvs[index] = remap(pu1 - pu2, radius); uvs[index + 1] = remap(pv1 - pv2, radius); } // Radial, all variations of FG squircular: function fixFGSingularities(uvs, index, u, v, radius) { if (isNegligeable(u) || isNegligeable(v)) { uvs[index] = remap(u, radius); uvs[index + 1] = remap(v, radius); } else { return true; } } export function fgSquircular({ uvs, index, u, v, radius }) { const ok = fixFGSingularities(uvs, index, u, v, radius); if (ok) { const u2 = u ** 2; const v2 = v ** 2; const sign = Math.sign(u * v); const uv2Sum = u2 + v2; const sqrtUV = Math.sqrt( uv2Sum - safeSqrt(uv2Sum * (uv2Sum - 4 * u2 * v2)), ); uvs[index] = remap((sign / (v * SQRT2)) * sqrtUV, radius); uvs[index + 1] = remap((sign / (u * SQRT2)) * sqrtUV, radius); } } export function twoSquircular({ uvs, index, u, v, radius }) { const ok = fixFGSingularities(uvs, index, u, v, radius); if (ok) { const sign = Math.sign(u * v); const sqrtUV = Math.sqrt(1 - safeSqrt(1 - 4 * u ** 2 * v ** 2)); uvs[index] = remap((sign / (v * SQRT2)) * sqrtUV, radius); uvs[index + 1] = remap((sign / (u * SQRT2)) * sqrtUV, radius); } } export function threeSquircular({ uvs, index, u, v, radius }) { const ok = fixFGSingularities(uvs, index, u, v, radius); if (ok) { const u2 = u ** 2; const v2 = v ** 2; const sign = Math.sign(u * v); const sqrtUV = Math.sqrt( (1 - safeSqrt(1 - 4 * u ** 4 * v2 - 4 * u2 * v ** 4)) / (2 * (u2 + v2)), ); uvs[index] = remap((sign / v) * sqrtUV, radius); uvs[index + 1] = remap((sign / u) * sqrtUV, radius); } } export function cornerificTapered2({ uvs, index, u, v, radius }) { const ok = fixFGSingularities(uvs, index, u, v, radius); if (ok) { const u2 = u ** 2; const v2 = v ** 2; const sign = Math.sign(u * v); const uv2Sum = u2 + v2; const sqrtUV = Math.sqrt( (uv2Sum - Math.sqrt(uv2Sum * (uv2Sum - 4 * u2 * v2 * (2 - u2 - v2)))) / (2 * (2 - u2 - v2)), ); uvs[index] = remap((sign / v) * sqrtUV, radius); uvs[index + 1] = remap((sign / u) * sqrtUV, radius); } } export function tapered4({ uvs, index, u, v, radius }) { const ok = fixFGSingularities(uvs, index, u, v, radius); if (ok) { const u2 = u ** 2; const v2 = v ** 2; const sign = Math.sign(u * v); const uv2Sum = u2 + v2; const divider = 3 - u ** 4 - 2 * u2 * v2 - v ** 4; const sqrtUV = Math.sqrt( (uv2Sum - safeSqrt(uv2Sum * (uv2Sum - 2 * u2 * v2 * divider))) / divider, ); uvs[index] = remap((sign / v) * sqrtUV, radius); uvs[index + 1] = remap((sign / u) * sqrtUV, radius); } } // Non-axial const FOURTH_SQRT2 = 2 ** (1 / 4); export function nonAxial2Pinch({ uvs, index, u, v, radius }) { const u2 = u ** 2; const v2 = v ** 2; const sign = Math.sign(u * v); const uv2Sum = u2 + v2; const sqrtUV = (uv2Sum - 2 * u2 * v2 - safeSqrt((uv2Sum - 4 * u2 * v2) * uv2Sum)) ** (1 / 4); if (isNegligeable(v)) { uvs[index] = remap(Math.sign(u) * Math.sqrt(Math.abs(u)), radius); uvs[index + 1] = remap(safeDivide(sign, u * FOURTH_SQRT2) * sqrtUV, radius); } else { uvs[index] = remap(safeDivide(sign, v * FOURTH_SQRT2) * sqrtUV, radius); uvs[index + 1] = remap( isNegligeable(u) ? Math.sign(v) * Math.sqrt(Math.abs(v)) : safeDivide(sign, u * FOURTH_SQRT2) * sqrtUV, radius, ); } } export function nonAxialHalfPinch({ uvs, index, u, v, radius }) { const u2 = u ** 2; const v2 = v ** 2; const sign = Math.sign(u * v); const uv2Sum = u2 + v2; const sqrtUV = Math.sqrt( safeDivide(1 - safeSqrt(1 - 4 * u2 * v2 * uv2Sum ** 2), 2 * uv2Sum), ); if (isNegligeable(v)) { uvs[index] = remap(Math.sign(u) * u2, radius); uvs[index + 1] = remap(safeDivide(sign, u) * sqrtUV, radius); } else { uvs[index] = remap(safeDivide(sign, v) * sqrtUV, radius); uvs[index + 1] = remap( isNegligeable(u) ? Math.sign(v) * v2 : safeDivide(sign, u) * sqrtUV, radius, ); } } // Variations of elliptical export function squelched({ uvs, index, u, v, radius, t }) { uvs[index] = [HALF_PI, TAU - HALF_PI].includes(t) ? 0.5 : remap(u / Math.sqrt(1 - v ** 2), radius); uvs[index + 1] = [0, TAU, Math.PI].includes(t) ? 0.5 : remap(v / Math.sqrt(1 - u ** 2), radius); } export function squelchedVertical({ uvs, index, u, v, radius, t }) { uvs[index] = remap(u, radius); uvs[index + 1] = [0, TAU, Math.PI].includes(t) ? 0.5 : remap(v / Math.sqrt(1 - u ** 2), radius); } export function squelchedHorizontal({ uvs, index, u, v, radius, t }) { uvs[index] = [HALF_PI, TAU - HALF_PI].includes(t) ? 0.5 : remap(u / Math.sqrt(1 - v ** 2), radius); uvs[index + 1] = remap(v, radius); }