Модуль определяет, когда точка должна привязаться к узлу сетки, когда допустимо смещение, как учитывать занятость модулей и как не превращать композицию в механическую таблицу. Все правила заданы как вычислимые функции.
Назначение
00 / scope
Что решает этот модуль
Сетка превращает свободные координаты в систему опор. Для каждой точки модуль решает:
нужно ли привязывать её к узлу;
какой узел предпочтителен;
какое отклонение допустимо;
не перегружен ли выбранный модуль;
не создаётся ли слишком механичная раскладка;
можно ли нарушить сетку ради иерархии или баланса.
Ключевой принцип
Сетка не является абсолютным законом. Она задаёт дискретное пространство кандидатов, а итоговая позиция определяется контекстом.
base_position = nearest_allowed_grid_node(point)
final_position = base_position + controlled_deviation
where deviation may be changed by:
hierarchy
balance
negative_space
collision
eye_flow
typography
Для: декоративных точек и живой editorial-композиции.
Snap candidate
Сетка только предлагает несколько узлов, а solver выбирает лучший.
C = k nearest allowed nodes
best = argmax(score(node))
Для: доминантных точек и конфликтных областей.
Snap decision:
if role in [anchor, connector_node, measurement_marker]:
mode = HARD
elif role in [support, rhythm, micro_marker]:
mode = SOFT
elif role == focus:
mode = CANDIDATE
else:
mode = SOFT
Hard constraints may force a different mode.
Контролируемое отклонение
04 / deviation
Почему отклонение нужно
Если каждая точка строго сидит в узле, композиция быстро становится механической. Поэтому вторичным элементам разрешается небольшое смещение.
Запрет: отклонение не должно использоваться для маскировки плохого выбора узла. Если точке требуется сдвиг более 0.30 размера ячейки, алгоритм должен выбрать другой узел или отказаться от snap.
Occupancy / занятость
05 / density control
Занятость ячейки
Каждый объект увеличивает нагрузку на окружающие ячейки. Точка не должна автоматически размещаться в уже перегруженной зоне.
Node occupancy score:
occupancy_score = 1 - clamp(O_norm(node), 0, 1)
If role == focus:
prefer O_norm ≈ 0.10..0.55
If role == support:
prefer O_norm ≈ 0.20..0.70
If role == micro_noise:
allow O_norm ≈ 0.35..0.85
Кандидаты + внешний контекст
06 / local vs deferred
Focus
Получает несколько grid-кандидатов. Compo 1.06 ранжирует их только по собственным grid-метрикам и не решает, где глобально должна находиться доминанта.
LOCAL CANDIDATES
Support
Может получить ближайшие допустимые узлы и bounded deviation. Выбор между ними после локального ранжирования может быть изменён global solver.
LOCAL PROPOSAL
Noise / marker
Допускает быстрый локальный выбор, если роль разрешает это и все hard constraints уже подтверждены владельцами соответствующих проверок.
FAST PATH
Compo 1.06 DOES NOT OWN:
free_space_score
hierarchy_score
balance_score
alignment_score
eye_flow_score
negative_space_score
These are DEFERRED_INPUTS.
They may be attached to GridCandidate later by their owners.
Local candidate result:
GridCandidate {
nodeId,
snappedPosition,
deviation,
components: {
snap_distance_score,
occupancy_fit,
deviation_score,
mechanicality_penalty
},
grid_fit_score,
deferredContext: null | ExternalMetricMap
}
Архитектурное правило: Compo 1.06 не смешивает grid-fit с hierarchy/balance/eye-flow. Он отдаёт чистую локальную grid-оценку и список кандидатов; глобальный solver добавляет внешние метрики отдельно.
Контекстные взаимодействия
07 / interactions
Условие
Коррекция
Причина
Рядом крупная доминанта
использовать узлы, усиливающие связь, но не касание
подчинение и группировка
Рядом текстовый блок
предпочитать общий baseline / column edge
связь с типографикой
Высокая локальная плотность
снизить deviation и/или выбрать соседний модуль
контроль clutter
Пустая зона композиции
разрешить более крупную deviation
избежать чрезмерной механики
Точка является anchor линии
hard snap
геометрическая целостность
Grid node создаёт плохое касание
запретить узел даже при минимальном расстоянии
tangency penalty выше snap reward
Глобальный баланс плох
разрешить переход в соседнюю ячейку
global rule overrides local snap
Правила приоритетов и overrides
08 / rule priority
Hard constraints — выше сетки
collision с защищённым объектом;
опасное касание;
выход за safe-area, если запрещён;
закрытие текста/лица/ключевой детали;
невыполнимый минимальный gap.
Global rules могут отменить snap
Compo 05 — negative space;
Compo 13 — dominance;
Compo 19 — hierarchy;
Compo 22 — asymmetrical balance;
Compo 27 — eye movement;
Compo 49 — global scoring.
Priority order:
1. hard safety / protected content
2. semantic constraints
3. global composition constraints
4. hierarchy / balance / eye flow
5. grid alignment
6. local aesthetic variation
7. randomization
Therefore:
GRID MUST NEVER OVERRIDE A BAD COMPOSITION.
Все три значения принадлежат Compo 1.06. Их диапазон после нормализации: [0..1].
Mechanicality penalty
For a set of comparable points:
exactSnapRatio = count(δ≈0) / N
repeatedOffsetRatio = dominant_offset_pattern / N
mechanicality_raw =
max(exactSnapRatio, repeatedOffsetRatio)
mechanicality_penalty =
style_requires_precision
? 0
: smoothstep(M0, M1, mechanicality_raw)
M0,M1 = STYLE_PRESET / HEURISTIC
Это групповая grid-метрика. Она не заменяет rhythm/hierarchy metrics из других модулей.
GRID FIT AGGREGATE
active = {
snap_distance_score,
occupancy_fit,
deviation_score
}
w = normalizeActiveWeights(stylePreset.gridWeights)
grid_fit_score =
w.snap * snap_distance_score
+ w.occupancy * occupancy_fit
+ w.deviation * deviation_score
- w.mechanicality * mechanicality_penalty
grid_fit_score = clamp(grid_fit_score, 0, 1)
IMPORTANT ANTI-DOUBLE-COUNTING:
Global solver may consume EITHER:
A) grid_fit_score
OR
B) its component metrics
but MUST NOT sum A + B together.
Удалено из локальной формулы: hierarchy, balance, eye-flow, free-space, alignment, tangency и collision. Эти оценки принадлежат другим владельцам. Compo 1.06 может получить их как DEFERRED_INPUT либо запросить hard-validation, но не пересчитывает сам.
Примеры
10 / visual tests
focus = nodesupport = nodesoft candidate shown
mechanicality highhierarchy low
Лаборатория
11 / interactive
Пошаговый алгоритм
12 / deterministic pipeline
01 Build grid Сформировать узлы, линии и допустимую область.
02 Map occupancy Рассчитать только grid occupancy от уже размещённых объектов.
03 Generate candidates Получить k ближайших разрешённых узлов.
04 Request hard validation Protected zones / collision / tangency проверяют их owner-модули.
Hard anchor Input: 1080×1350, 6×8 grid, role=anchor, preferred=(0.48,0.52). Expected: candidate at exact allowed node, δ=0, snap_distance_score=1. Final global placement is not asserted here.
T02
Soft support Input: support point, editorial style. Expected: exact-node candidate + bounded offset candidates |δ|≤0.18 cell; Compo 1.06 ranks them only by grid metrics. Alignment/hierarchy are absent until owner modules attach them.
T03
Occupied node Input: nearest node has O_norm=1.12 and support role. Expected: low occupancy_fit; candidate may remain locally valid unless an external hard rule rejects it.
T04
Bad tangency Input: node would place point in forbidden tangency band. Expected: Compo 1.06 requests owner validation (Compo 1.09 / geometry validator); if REJECT is returned, candidate is removed. 1.06 does not compute tangency penalty itself.
T05
Mechanicality Input: 12 comparable support points, editorial style, all hard-snapped. Expected: mechanicality_penalty > 0; local ranking favors bounded variation where available.
T06
Global override Input: locally best grid candidate later receives poor global.balance_score. Expected: Compo 1.06 output remains unchanged as local evidence; global solver may select another candidate. Audit log records the override owner.
T07
Anti-double-counting Input: global solver receives grid_fit_score and its components. Expected: aggregation uses either the aggregate or the components, never both. Duplicate aggregation must fail validation.
effectiveExistingWeight(object):
if contextualWeight exists
AND contextualWeight.version is fresh:
return contextualWeight
else:
return visualWeightBase
Candidate point itself is NOT added to occupancy map
until candidate evaluation requests a hypothetical map.
DIRTY / RECOMPUTE
recompute 1.06 when:
grid changes
candidate point position/role changes
occupied object geometry changes
relevant effective visual weight changes
protected/allowed grid nodes change
Do NOT recompute because a deferred global score changed;
only global ranking changes unless solver moves geometry.
Anti-double-counting:grid.fit_score является агрегатом собственных grid-компонентов. Если глобальный solver использует агрегат, компоненты не добавляются повторно. Tangency/collision и global metrics принадлежат другим owner-модулям.