Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { convertUnits } from "./units_convert.ts"; ← from units.convert ^1.0.0 · built alongside by fune
import { type FloorArea, type FloorAreaUnit, type RoomDimensions } from "./property_floor_area_types.ts";
const DIMENSION = /^[0-9]{1,3}(\.[0-9]{1,3})?$/;
const MAX_ROOMS = 1000;
/** A dimension as an integer number of thousandths. */
function thousandths(room: string, field: string, text: string): number {
if (typeof text !== "string" || !DIMENSION.test(text)) {
throw new RangeError(`room "${room}": ${field} must be a decimal below 1000 with at most 3 decimal places, received "${text}"`);
}
const [whole, fraction = ""] = text.split(".");
const value = Number(whole) * 1000 + Number(fraction.padEnd(3, "0"));
if (value === 0) {
throw new RangeError(`room "${room}": ${field} must be greater than zero`);
}
return value;
}
/** An integer number of millionths as the shortest exact decimal. */
function decimal(millionths: number): string {
const whole = Math.floor(millionths / 1_000_000);
const fraction = String(millionths % 1_000_000).padStart(6, "0").replace(/0+$/, "");
return fraction === "" ? String(whole) : `${whole}.${fraction}`;
}
/**
* Room areas and their total, exact, from length × width of each room, and
* the total in square metres and square feet rounded once at the end.
*/
export function floorArea(rooms: readonly RoomDimensions[], lengthUnit: FloorAreaUnit, decimals: number): FloorArea {
if (lengthUnit !== "m" && lengthUnit !== "ft") {
throw new RangeError(`unknown length unit "${lengthUnit}": use m or ft`);
}
if (rooms.length === 0) {
throw new RangeError("rooms must not be empty");
}
if (rooms.length > MAX_ROOMS) {
throw new RangeError(`at most ${MAX_ROOMS} rooms, received ${rooms.length}`);
}
// Each area is below 1e12 millionths and the total below 1e15: exact numbers.
const areas = rooms.map((r) => thousandths(r.name, "length", r.length) * thousandths(r.name, "width", r.width));
const total = decimal(areas.reduce((a, b) => a + b, 0));
const from = lengthUnit === "m" ? "m2" : "ft2";
return {
roomAreas: areas.map(decimal),
total,
squareMetres: convertUnits(total, from, "m2", decimals),
squareFeet: convertUnits(total, from, "ft2", decimals),
};
}