Files
klz-cables.com/components/home/hero-webgl/math.ts
Marc Mintel 82bb7240d5
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Failing after 3m48s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
chore(workspace): add gitea repository url to all packages
2026-02-27 11:27:22 +01:00

99 lines
2.7 KiB
TypeScript

import { createNoise2D } from 'simplex-noise';
// Seeded random number generator
export function LCG(seed: number) {
return function () {
seed = Math.imul(48271, seed) | 0 % 2147483647;
return (seed & 2147483647) / 2147483648;
};
}
export type Vec2 = [number, number];
export type Vec3 = [number, number, number];
export function distance2D(a: Vec2, b: Vec2) {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
return Math.sqrt(dx * dx + dy * dy);
}
export function distance3D(a: Vec3, b: Vec3) {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
const dz = a[2] - b[2];
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
// Generate points for a hanging cable between two points (catenary/sag)
export function generateCatenaryCurve(
p1: Vec3,
p2: Vec3,
sag: number,
segments: number = 10
): Vec3[] {
const points: Vec3[] = [];
for (let i = 0; i <= segments; i++) {
const t = i / segments;
const x = p1[0] + (p2[0] - p1[0]) * t;
const z = p1[2] + (p2[2] - p1[2]) * t;
// Linear interpolation of height
const baseHeight = p1[1] + (p2[1] - p1[1]) * t;
// Add parabolic sag
// t ranges from 0 to 1. Parabola that is 0 at ends and 1 in middle is 4 * t * (1 - t)
const sagOffset = 4 * t * (1 - t) * sag;
const y = baseHeight - sagOffset;
points.push([x, y, z]);
}
return points;
}
// Simple terrain height function combining multiple frequencies
export function getTerrainHeight(noise2D: ReturnType<typeof createNoise2D>, x: number, z: number): number {
// We want a gigantic, hilly planet. We'll use low frequency noise.
const n1 = noise2D(x * 0.002, z * 0.002) * 40; // Main hills
const n2 = noise2D(x * 0.01, z * 0.01) * 10; // Smaller details
return n1 + n2;
}
// Helper: Place points within a radius using rejection sampling to avoid overlap
export function generateClusteredPoints(
rng: () => number,
center: Vec2,
count: number,
radius: number,
minDist: number
): Vec2[] {
const points: Vec2[] = [];
let attempts = 0;
const maxAttempts = count * 50;
while (points.length < count && attempts < maxAttempts) {
attempts++;
const angle = rng() * Math.PI * 2;
// Square root for uniform distribution in a circle
const r = Math.sqrt(rng()) * radius;
const p: Vec2 = [
center[0] + Math.cos(angle) * r,
center[1] + Math.sin(angle) * r
];
let valid = true;
for (const other of points) {
if (distance2D(p, other) < minDist) {
valid = false;
break;
}
}
if (valid) {
points.push(p);
}
}
return points;
}