77 lines
2.2 KiB
TypeScript
77 lines
2.2 KiB
TypeScript
export type LinearUnit = 'metric' | 'imperial'
|
|
|
|
const METERS_PER_FOOT = 0.3048
|
|
const FEET_PER_METER = 1 / METERS_PER_FOOT
|
|
|
|
type LinearControlValueOptions = {
|
|
minMeters?: number
|
|
maxMeters?: number
|
|
}
|
|
|
|
export function metersToLinearUnit(meters: number, unit: LinearUnit): number {
|
|
return unit === 'imperial' ? meters * FEET_PER_METER : meters
|
|
}
|
|
|
|
export function linearUnitToMeters(value: number, unit: LinearUnit): number {
|
|
return unit === 'imperial' ? value * METERS_PER_FOOT : value
|
|
}
|
|
|
|
export function linearControlValueToMeters(
|
|
value: number,
|
|
unit: LinearUnit,
|
|
options: LinearControlValueOptions = {},
|
|
): number {
|
|
const meters = linearUnitToMeters(value, unit)
|
|
const minMeters = options.minMeters ?? Number.NEGATIVE_INFINITY
|
|
const maxMeters = options.maxMeters ?? Number.POSITIVE_INFINITY
|
|
|
|
return Math.min(Math.max(meters, minMeters), maxMeters)
|
|
}
|
|
|
|
export function getLinearUnitLabel(unit: LinearUnit): string {
|
|
return unit === 'imperial' ? 'ft' : 'm'
|
|
}
|
|
|
|
const SQUARE_FEET_PER_SQUARE_METER = FEET_PER_METER * FEET_PER_METER
|
|
|
|
export function squareMetersToAreaUnit(squareMeters: number, unit: LinearUnit): number {
|
|
return unit === 'imperial' ? squareMeters * SQUARE_FEET_PER_SQUARE_METER : squareMeters
|
|
}
|
|
|
|
export function getAreaUnitLabel(unit: LinearUnit): string {
|
|
return unit === 'imperial' ? 'ft²' : 'm²'
|
|
}
|
|
|
|
export function formatAreaLabel(
|
|
squareMeters: number,
|
|
unit: LinearUnit,
|
|
fractionDigits = 1,
|
|
): string {
|
|
return `${squareMetersToAreaUnit(squareMeters, unit).toFixed(fractionDigits)}${getAreaUnitLabel(unit)}`
|
|
}
|
|
|
|
export function formatLinearMeasurement(meters: number, unit: LinearUnit): string {
|
|
if (!Number.isFinite(meters)) return '--'
|
|
|
|
const absoluteMeters = Math.abs(meters)
|
|
|
|
if (unit === 'imperial') {
|
|
const feet = metersToLinearUnit(absoluteMeters, unit)
|
|
let wholeFeet = Math.floor(feet)
|
|
let inches = Math.round((feet - wholeFeet) * 12)
|
|
if (inches === 12) {
|
|
wholeFeet += 1
|
|
inches = 0
|
|
}
|
|
|
|
const sign = meters < 0 && (wholeFeet !== 0 || inches !== 0) ? '-' : ''
|
|
|
|
return `${sign}${wholeFeet}'${inches}"`
|
|
}
|
|
|
|
const roundedMeters = Number.parseFloat(absoluteMeters.toFixed(2))
|
|
const sign = meters < 0 && roundedMeters !== 0 ? '-' : ''
|
|
|
|
return `${sign}${roundedMeters}m`
|
|
}
|