92 lines
2.9 KiB
JavaScript
92 lines
2.9 KiB
JavaScript
export const TAU = Math.PI * 2;
|
|
|
|
/**
|
|
* Ceil a sample position while keeping an analytically integral boundary on
|
|
* that integer. IEEE-754 roundoff from expressions such as (0.1 + 0.2) * 1000
|
|
* must not add a physically nonexistent sample of latency.
|
|
*/
|
|
export function ceilSamplePosition(samplePosition) {
|
|
if (!Number.isFinite(samplePosition)) {
|
|
throw new TypeError("sample position must be finite.");
|
|
}
|
|
const nearestInteger = Math.round(samplePosition);
|
|
const tolerance =
|
|
16 * Number.EPSILON * Math.max(1, Math.abs(samplePosition));
|
|
const result = Math.abs(samplePosition - nearestInteger) <= tolerance
|
|
? nearestInteger
|
|
: Math.ceil(samplePosition);
|
|
if (!Number.isSafeInteger(result)) {
|
|
throw new RangeError("sample position exceeds the safe integer range.");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function ceilSampleIndex(timeSeconds, sampleRate) {
|
|
if (!Number.isFinite(timeSeconds)) throw new TypeError("time must be finite.");
|
|
if (!(sampleRate > 0) || !Number.isFinite(sampleRate)) {
|
|
throw new TypeError("sampleRate must be finite and positive.");
|
|
}
|
|
return ceilSamplePosition(timeSeconds * sampleRate);
|
|
}
|
|
|
|
export function distance3(a, b) {
|
|
return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
|
}
|
|
|
|
export function dbToLinear(decibels) {
|
|
return 10 ** (decibels / 20);
|
|
}
|
|
|
|
export function powerToDb(power, floorDb = -120) {
|
|
if (!(power > 0)) return floorDb;
|
|
return Math.max(floorDb, 10 * Math.log10(power));
|
|
}
|
|
|
|
export function clamp(value, minimum, maximum) {
|
|
return Math.min(maximum, Math.max(minimum, value));
|
|
}
|
|
|
|
export function lerp(start, end, amount) {
|
|
return start + (end - start) * amount;
|
|
}
|
|
|
|
export function createScanPoints(scan) {
|
|
const points = new Float32Array(scan.columns * scan.rows * 4);
|
|
let offset = 0;
|
|
for (let row = 0; row < scan.rows; row += 1) {
|
|
const y = scan.rows === 1 ? scan.yMax : lerp(scan.yMax, scan.yMin, row / (scan.rows - 1));
|
|
for (let column = 0; column < scan.columns; column += 1) {
|
|
const x = scan.columns === 1 ? scan.xMin : lerp(scan.xMin, scan.xMax, column / (scan.columns - 1));
|
|
points[offset] = x;
|
|
points[offset + 1] = y;
|
|
points[offset + 2] = scan.z;
|
|
points[offset + 3] = 0;
|
|
offset += 4;
|
|
}
|
|
}
|
|
return points;
|
|
}
|
|
|
|
export function scanIndexToPosition(index, scan) {
|
|
const row = Math.floor(index / scan.columns);
|
|
const column = index % scan.columns;
|
|
return [
|
|
scan.columns === 1 ? scan.xMin : lerp(scan.xMin, scan.xMax, column / (scan.columns - 1)),
|
|
scan.rows === 1 ? scan.yMax : lerp(scan.yMax, scan.yMin, row / (scan.rows - 1)),
|
|
scan.z,
|
|
];
|
|
}
|
|
|
|
export function findPeak(values) {
|
|
if (values.length === 0) throw new Error("빈 map에서는 peak를 찾을 수 없습니다.");
|
|
let peakIndex = 0;
|
|
let peakValue = values[0];
|
|
for (let index = 1; index < values.length; index += 1) {
|
|
if (values[index] > peakValue) {
|
|
peakValue = values[index];
|
|
peakIndex = index;
|
|
}
|
|
}
|
|
return { index: peakIndex, value: peakValue };
|
|
}
|