Functional Weave
Code in Rust

construction.roof-pitch@1.0.0

impl/python.py

1,916 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import math

from .construction_roof_pitch_types import RoofPitch, RoofType
from .math_atan import atan  ← from math.atan ^1.0.0 · built alongside by fune
from .math_round_float import round_float  ← from math.round-float ^1.0.0 · built alongside by fune

# 180 / pi as a double, written out so no language computes it differently.
DEGREES_PER_RADIAN = 57.29577951308232
LIMIT = 10000


def _check_length(name: str, value: float, allow_zero: bool) -> None:
    ok = (
        not isinstance(value, bool)
        and isinstance(value, (int, float))
        and math.isfinite(value)
        and value <= LIMIT
        and (value >= 0 if allow_zero else value > 0)
    )
    if not ok:
        want = "zero or more" if allow_zero else "greater than zero"
        raise ValueError(
            "%s must be a finite number of metres, %s and at most %d, received %r" % (name, want, LIMIT, value)
        )


def roof_pitch(span: float, rise: float, roof_length: float, roof_type: RoofType) -> RoofPitch:
    """Pitch, rafter length and slope area of a duo- or mono-pitch roof.

    The area is worked from the unrounded rafter, not the one rounded to a
    millimetre for display: over a long roof the difference shows in the
    second decimal place.
    """
    _check_length("span", span, False)
    _check_length("rise", rise, True)
    _check_length("roofLength", roof_length, False)
    if roof_type == "duo":
        run = span / 2
        slopes = 2
    elif roof_type == "mono":
        run = float(span)
        slopes = 1
    else:
        raise ValueError('unknown roof type "%s"' % (roof_type,))
    radians = atan(rise / run)
    # sqrt is one of the operations IEEE 754 requires to be correctly rounded,
    # so math.sqrt is the same double in every language.
    rafter = math.sqrt(run * run + rise * rise)
    return RoofPitch(
        pitch_degrees=round_float(radians * DEGREES_PER_RADIAN, 2),
        rafter_length=round_float(rafter, 3),
        roof_area=round_float(rafter * roof_length * slopes, 2),
    )