Owner's ruling: "there should be no cap on the proportion of a corridor, especially for big buildings, the crinkliness rule is there to prevent these becoming unpleasant spaces." proportion_circulation was [1.5, 0.5], failing a corridor above aspect 2.57 -- at the 1.97 m minimum width the width factor allows, a corridor 5.1 m long; at the 2.4 m target width, 6.2 m. The corpus shows the consequence: median circulation leaf 14.3 m2 at aspect 1.67, a stubby room rather than a corridor. The justification checks out arithmetically. The unpleasant space the cap was standing in for is a long BURIED corridor -- and crinkliness already sends that to zero, since a buried leaf has crink == 0. A long corridor along a facade scores 0.90. Aspect cannot tell those two apart; crinkliness can, so the cap was duplicating a rule that already exists and does the job better. Shipped: proportion_circulation = None (no aspect requirement). quality_proportion returns 1.0 for circulation, and shapecurve.leaf_constraints yields rmax = inf so the DP agrees with the fitness instead of pruning topologies the objective would accept. A habitable room's aspect target is untouched. The narrow side still holds -- width_circulation keeps a corridor >= 1.97 m, and "edge too long" still caps a single wall at 8 m. Unlike 39.14/39.18/39.19 this DOES change the fail set, which is the point. Across the twelve baseline artefacts it removes exactly 7 corridor proportion fails and adds none: harbor 33/43/42 -> 33/42/40, maple 54/73/55 -> 54/71/54, health-centre 4/9/5 -> 3/9/5, programme-house unchanged. Also recorded in 39.22, and a retraction: hxi was titled "search is rewarded for deleting the circulation spine", which 39.8 had already measured and refuted -- 0 of the 7 connectivity-breaking deletions sampled were rewarded. My own earlier comment on the bead restated that retracted claim; corrected, and the bead retitled. What binds next: removing the cap roughly doubles a corridor leaf's reach, from proportion at ~6.2 m to size_circulation at 12.5 m (2.4 m wide hits the 30 m2 fail edge there). size_circulation's target area is ZERO, the other half of the double-charge -- circulation priced as overhead once in value_circulation = 50 and again in a size factor whose optimum is non-existence. Not changed: it is a distinct parameter with its own rationale, unruled, and 39.16 is a standing reminder about inherited constants. 415 passed. Refs homemaker-py-hxi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
96 lines
3.8 KiB
Python
96 lines
3.8 KiB
Python
"""Corridors have no aspect requirement (homemaker-py-hxi, DESIGN.md §39.22).
|
|
|
|
Owner's ruling: "there should be no cap on the proportion of a corridor,
|
|
especially for big buildings, the crinkliness rule is there to prevent these
|
|
becoming unpleasant spaces."
|
|
|
|
The tests below pin both halves of that — the cap is gone, AND crinkliness
|
|
still does the job the cap was wrongly doing, so removing it did not leave
|
|
long buried corridors unpunished.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import math
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from homemaker_layout import dom as dom_mod
|
|
from homemaker_layout.fitness import (
|
|
CONF_DEFAULTS, FAIL_THRESHOLD, Fitness, gaussian, load_config,
|
|
)
|
|
|
|
EXAMPLES = Path(__file__).resolve().parent.parent / "examples"
|
|
pytestmark = pytest.mark.skipif(not (EXAMPLES / "harbor-house").is_dir(),
|
|
reason="examples absent")
|
|
|
|
|
|
def _fit(**ov):
|
|
conf, cost = load_config(EXAMPLES / "harbor-house", overrides=ov)
|
|
return Fitness(conf, cost)
|
|
|
|
|
|
def test_there_is_no_corridor_aspect_requirement():
|
|
assert CONF_DEFAULTS["proportion_circulation"] is None
|
|
fit = _fit()
|
|
assert dom_mod.is_circulation(dom_mod.Node(type="C"))
|
|
# any aspect at all scores 1.0 -- checked through the real code path
|
|
for aspect in (1.0, 3.0, 12.0, 40.0):
|
|
fit_leaf = dom_mod.Node(
|
|
type="C", node=[[0.0, 0.0], [aspect, 0.0], [aspect, 1.0], [0.0, 1.0]])
|
|
assert fit.quality_proportion(fit_leaf) == 1.0, aspect
|
|
|
|
|
|
def test_a_room_still_has_one():
|
|
"""The ruling is about corridors. A habitable room's aspect target stands."""
|
|
fit = _fit()
|
|
room = dom_mod.Node(type="r", node=[[0.0, 0.0], [12.0, 0.0], [12.0, 1.0], [0.0, 1.0]])
|
|
assert fit.quality_proportion(room) < FAIL_THRESHOLD
|
|
|
|
|
|
def test_crinkliness_is_what_punishes_an_unpleasant_corridor():
|
|
"""The ruling's own justification, as arithmetic.
|
|
|
|
A long BURIED corridor is exactly the unpleasant space the aspect cap was
|
|
being used to prevent; crinkliness sends it to zero. A long corridor along
|
|
a facade is a perfectly good corridor, and crinkliness likes it.
|
|
"""
|
|
target, sigma = CONF_DEFAULTS["uncrinkliness_circulation"]
|
|
h, width, length = 3.0, 2.4, 30.0
|
|
area = width * length
|
|
|
|
buried = 0.0 # no illuminated wall at all
|
|
assert buried == 0.0
|
|
|
|
lit_one_side = (length * h) / area # the long wall is a facade
|
|
q = gaussian(1.0 / lit_one_side, 1.0, target, sigma)
|
|
assert q > 0.85, "a daylit corridor along a facade should score well"
|
|
|
|
|
|
def test_removing_the_cap_only_ever_removes_proportion_fails():
|
|
"""It cannot introduce a failure, and it touches nothing but corridors."""
|
|
old = {"proportion_circulation": [1.5, 0.5]}
|
|
seen = 0
|
|
for name in ("harbor-house", "maple-court", "health-centre", "programme-house"):
|
|
d = EXAMPLES / name
|
|
for p in sorted(d.glob("coldstart-500000-s*.dom")):
|
|
root = dom_mod.load(str(p))
|
|
c_old, cost = load_config(d, overrides=old)
|
|
c_new, _ = load_config(d)
|
|
_, f_old = Fitness(c_old, cost).score_with_fails(copy.deepcopy(root))
|
|
_, f_new = Fitness(c_new, cost).score_with_fails(copy.deepcopy(root))
|
|
assert not (set(f_new) - set(f_old)), "must not add a failure"
|
|
assert all(f.endswith(" proportion") for f in set(f_old) - set(f_new))
|
|
seen += 1
|
|
assert seen >= 4
|
|
|
|
|
|
def test_the_shape_curve_dp_accepts_an_unbounded_aspect():
|
|
"""`leaf_constraints` feeds rmax into the DP; None must become inf, not crash
|
|
and not silently fall back to a finite cap."""
|
|
from homemaker_layout import shapecurve
|
|
fit = _fit()
|
|
leaf = dom_mod.Node(type="C", node=[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]])
|
|
assert math.isinf(shapecurve.leaf_constraints(fit, leaf).rmax)
|