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),
)