Контракт модуля
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
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
Локальный 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 zones | endpoints / 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.