COMPO · 2.03 · DIAGONAL DIRECTION

Диагональ.
Направление.

Модуль описывает диагональную линию как измеримый вектор: угол, знак наклона, силу отклонения от осей, направление source→target и допустимую роль в локальной композиции. Никаких абстрактных “динамичнее” — только вычислимые признаки и candidate rules.

Контракт модуля

01 · machine interface

CONSUMES

Line.role Line.directed Compo2.01.angle360 Compo2.01.angle180 Compo2.01.unitVector Compo2.01.lengthDiagN Compo2.02.axisClass Compo2.02.axisErrorDeg Canvas.aspect StylePreset?

PROVIDES

Line.diagonalClass Line.axisDistanceDeg Line.diagonalStrength Line.directionQuadrant Line.directionSign Line.rotationCandidates[]

DEFERRED INPUTS

eye_flow_score global_balance_score negative_space_score hierarchy_score grid_global_score

Модуль экспортирует направление, но не решает глобально, полезно ли оно композиции.

Когда активируется диагональный модуль

02 · consume 2.02, do not recompute

H/V уже решены

if Line.axisClass in [H,V]: Compo2.03.status = NOT_APPLICABLE do not recompute axis distance do not rotate as diagonal

Канонический владелец axis-classification — Compo 2.02.

NONE → диагональ

if Line.axisClass == NONE: axisDistanceDeg = Line.axisErrorDeg diagonalStrength = clamp( axisDistanceDeg / 45, 0, 1 )

axisErrorDeg приходит из 2.02 и повторно здесь не вычисляется.

OWNERSHIP: 2.03 классифицирует характер уже подтверждённой диагонали (SHALLOW/MODERATE/STEEP), но не владеет решением H/V/NONE.

Сила диагонали

03 · normalized metric
0
axis aligned

Горизонталь или вертикаль.

0.5
moderate

Заметное, но не экстремальное отклонение.

1
max diagonal

Наибольшее отклонение от H/V: около 45° к ближайшей оси.

axisDistanceDeg = min distance to {0°, 90°, 180°} // because max possible distance to nearest axis = 45° diagonalStrength = clamp(axisDistanceDeg / 45, 0, 1)
DERIVED Эта нормализация строго следует из геометрии. Но то, какой диапазон preferred для конкретной роли, — уже HEURISTIC/STYLE.

Направление source → target

04 · directed descriptor

↘ SE

ux > 0 uy > 0

↗ NE

ux > 0 uy < 0

↙ SW

ux < 0 uy > 0

↖ NW

ux < 0 uy < 0
directionSign = { horizontal: sign(unit.x), vertical: sign(unit.y) } if directed == false: preserve geometric orientation but do not assign semantic source/target meaning
Для недиректированной линии нельзя автоматически утверждать, что взгляд идёт “слева направо” или “снизу вверх”. Это решает будущий eye-flow module, учитывая текст, объекты и культурное направление чтения.

Candidate generation

05 · rotation proposals

Сохранить направление

candidateAngles = { currentAngle, currentAngle ± 3°, currentAngle ± 6° }

Используется для локального поиска без смены характера наклона.

Смена диагонального класса

SHALLOW targets ≈ 6°..12° from axis MODERATE targets ≈ 18°..30° STEEP targets ≈ 32°..45°

STYLE/HEURISTIC Модуль генерирует варианты, но не выбирает победителя глобально.

Любая ротация меняет endpoints. После применения кандидата геометрия полностью пересчитывается через Compo 2.01.

Визуальная лаборатория

06 · exact examples
axisDistance ≈ 2.6°
НЕ диагональ по default tau=3°. Передать в Compo 2.02.
axisDistance ≈ 14.0°
MODERATE. Намеренный наклон уже ясно отделён от горизонтали.
axisDistance ≈ 34.3°
STEEP. Высокая diagonalStrength.
directed connector: SW → NE
Направление определяется source/target и unit vector. Eye-flow пока DEFERRED.
accent diagonal / no semantic target
Accent может быть диагональю без connector semantics. Нельзя заставлять её вести от A к B.

Локальный scoring

07 · metric ownership

Собственные метрики

diagonalStrength // descriptor, not a second vote diagonalRoleFit // owned fit against role/style directionConsistency rotationCost

Только эти характеристики принадлежат Compo 2.03.

Не вычислять здесь

eye_flow_score // DEFERRED global_balance_score // DEFERRED hierarchy_score // DEFERRED negative_space_score // DEFERRED grid_global_score // DEFERRED
diagonalRoleFit = roleDiagonalFit(role, diagonalStrength) localScore = w_diag * diagonalRoleFit + w_dir * directionConsistency - w_rot * rotationCost // weights = STYLE_PRESET / engine defaults // localScore != final composition score

Context interactions

08 · override graph
МодульМожет изменитьПричина
Grid / alignmentугол или endpointsможет предпочесть H/V либо конкретную диагональную ось сетки
Eye-flowнаправление source→targetесли линия должна вести между семантическими объектами
Negative spaceдлину / позициюесли диагональ слишком агрессивно режет свободное поле
Hierarchyтолщину / контрастчтобы accent не стал случайной доминантой
Protected zonesendpoints / routeлиния не должна пересекать запрещённый контент
PASS ORDER 1. Compo 2.01 computes geometry 2. Compo 2.02 rejects/handles near-axis lines 3. Compo 2.03 classifies true diagonal 4. generate angle candidates 5. local score 6. request DEFERRED global context 7. global solver accepts / rotates / moves / rejects 8. recompute through Compo 2.01 9. validate again

DATA FOR LAYOUT ENGINE

09 · implementation block
{ "module": "Compo 2.03", "name": "diagonal_lines_and_direction", "metric_owner": [ "line.diagonalRoleFit", "line.directionConsistency", "line.rotationCost" ], "consumes": [ "Compo0.Canvas", "Compo2.00.Line.role", "Compo2.01.Line.angle360", "Compo2.01.Line.angle180", "Compo2.01.Line.unitVector", "Compo2.01.Line.lengthDiagN", "Compo2.02.Line.axisClass", "Compo2.02.Line.axisErrorDeg" ], "provides": [ "Line.diagonalClass", "Line.axisDistanceDeg", "Line.diagonalStrength", "Line.directionQuadrant", "Line.directionSign", "Line.rotationCandidates" ], "defaults": { "shallow_max_deg": {"value":12,"source":"INITIAL_HEURISTIC"}, "moderate_max_deg": {"value":28,"source":"INITIAL_HEURISTIC"}, "candidate_rotation_step_deg": {"value":3,"source":"STYLE_PRESET"} }, "derived": { "diagonalStrength": "axisDistanceDeg / 45" }, "deferred_inputs": [ "eye_flow_score", "global_balance_score", "hierarchy_score", "negative_space_score", "grid_global_score" ], "hard_rules": [ "axisClass H/V is NOT_APPLICABLE here; only axisClass NONE enters diagonal classification", "directed endpoint order must be preserved", "after rotation geometry must be recomputed via Compo 2.01" ], "finality": "LOCAL_PROPOSAL_ONLY" }

Тесты

10 · deterministic cases

TEST 01

axisClass = H axisErrorDeg = 2.0° expected status = NOT_APPLICABLE no diagonal scoring

TEST 02

axisClass = NONE axisErrorDeg = 18° axisDistance = 18° diagonalStrength = 0.4 expected class = MODERATE

TEST 03

unit = (0.74,-0.67) directed = true expected quadrant = NE expected directionSign = (+,-)
MIGRATION 2.00–2.07 / pre-2.08: canonical LinePathGeometry, role/subrole/rigidity, metric ownership cleanup, path-length vs spatial-span separation.