Модуль формализует момент, когда точка оказывается слишком близко к другой точке, линии, кругу, эллипсу, прямоугольнику, текстовому или защищённому объекту. Его задача — отличить намеренное соединение от случайного «почти касания», вычислить clearance и выбрать корректирующее действие без участия AI.
Metric ownership. Модуль не владеет global balance, hierarchy, negative space, crop quality или typography. Он сообщает геометрическую проблему и варианты исправления. Финальный solver решает, какой из допустимых вариантов лучше для композиции целиком.
Weight dependency. Геометрия контакта не зависит от visual weight. visualWeightContextual используется только как необязательный сигнал при ранжировании correction candidates; если PASS B ещё не выполнен, разрешён fallback на visualWeightBase. Модуль 1.09 не пересчитывает вес.
Точка для машины — это диск
01 / geometric model
P
point center
P=(x,y) в нормализованных координатах или pixels.
r
visual radius
Фактический визуальный радиус маркера из Compo 1.01.
g
clearance
Подписанное расстояние от границы диска до ближайшей границы другого объекта.
Visual point geometry:
PointDisk(P, r)
Signed clearance:
g(P, O) = signedDistance(center=P, obstacle=O) - r
Interpretation:
g > 0 → separated
g = 0 → exact geometric tangency
g < 0 → overlap / penetration
Normalized clearance for scale-independent rules:
clearancePointN = g / Dp
where Dp = 2r
Primary normalizations:
clearancePointN = gPx / (2*rPx)
clearanceShortN = gPx / min(W,H)
Use clearancePointN for point-centered contact states.
Use clearanceShortN when comparing absolute spacing across modules.
Каноническое пространство расстояний = pixels (или эквивалентное isotropic metric space). Нельзя считать Euclidean distance напрямую по xN,yN∈[0,1] на прямоугольном холсте: при W≠H оси имеют разный физический масштаб. Сначала: xPx=xN·W, yPx=yN·H, затем все SDF / distance операции.
Не использовать математическую точку нулевого размера. Визуальный конфликт определяется границей видимого маркера, поэтому collision geometry обязана учитывать r, outline и фактическую толщину stroke.
Distance-функции и точность
02 / geometry + precision
Точка ↔ точка
d = length(P1 - P2)
g = d - (r1 + r2)
DERIVED — exact for two disks.
Точка ↔ круг
d = length(P - C)
g = d - (R + r)
DERIVED — exact for circle obstacle.
Точка ↔ line segment
t = clamp(dot(P-A,B-A)/|B-A|²,0,1)
Q = A + t(B-A)
d = length(P-Q)
g = d - (r + lineWidth/2)
DERIVED — exact for ROUND-capped stroked segment
(capsule geometry).
BUTT/SQUARE caps require their real stroke geometry.
q = abs(P-C) - halfSize + cornerR
sdRoundBox = length(max(q,0)) + min(max(q.x,q.y),0) - cornerR
g = sdRoundBox - r
DERIVED — exact for uniform corner radius.
Точка ↔ ellipse
Do NOT use circle approximation.
Use closest-point-on-ellipse numerical solver:
1. Transform P into ellipse local space.
2. Solve nearest boundary point Q by Newton iteration.
3. d = |P-Q|.
4. sign = insideEllipse(P) ? -1 : +1.
5. g = sign*d - r.
NUMERICAL_DERIVED / deterministic to solver tolerance.
Not symbolic-exact; export solverErrorPx.
Broad phase перед exact distance. Использовать spatial hash / quadtree / R-tree / BVH для отбора препятствий, чьи расширенные AABB пересекают contactSearchBounds. Число соседей не фиксируется как эстетическое правило. Ограничение kMax, если оно нужно реализации, маркируется как IMPLEMENTATION_HEURISTIC.
DERIVED_EXACT
disk↔disk, disk↔circle, AABB, uniform rounded-rect и capsule при совпадающей модели stroke.
NUMERICAL_DERIVED
ellipse и другие формы, где closest point решается итерационно. Всегда сохранять solverErrorPx.
APPROXIMATION
Допустима только как broad-phase или style shortcut. Нельзя маркировать approximation как exact metric.
Классификация контакта
03 / contact states
State
Условие по clearancePointN
Интерпретация
Default action
Источник
DEEP_OVERLAP
clearancePointN ≤ -0.30
точка явно вошла в объект
разрешить только если relation intent = overlap/anchor
HEURISTIC
SHALLOW_OVERLAP
-0.30 < clearancePointN < -0.05
неуверенное частичное пересечение
обычно исправить: углубить или разнести
HEURISTIC
TANGENT
|clearancePointN| ≤ 0.05
границы практически касаются
высокий tangency penalty
HEURISTIC
NEAR_TANGENT
0.05 < clearancePointN < 0.50
слишком узкая щель; связь выглядит случайной
увеличить gap или сделать контакт намеренным
HEURISTIC
SEPARATED
0.50 ≤ clearancePointN ≤ 2.50
читаемая дистанция
допустимо
HEURISTIC
DETACHED
clearancePointN > 2.50
геометрически конфликт отсутствует
контактный модуль нейтрален
HEURISTIC
Диапазоны — INITIAL_HEURISTIC, а не закон дизайна. Для крупных кругов, сверхмалых служебных точек и специальных style presets пороги могут быть переопределены. Геометрический факт g — DERIVED; интерпретация clearancePointN — HEURISTIC.
Сначала определить намерение
04 / relation intent
independent
Объекты не должны выглядеть связанными.
prefer clearancePointN ≥ 0.50
anchor
Точка обозначает узел линии/границы.
target clearancePointN ≈ 0
BUT snap is intentional
→ no tangency penalty
overlap
Точка должна явно пересекать форму.
target clearancePointN ≤ -0.30
satellite
Точка находится рядом, но не касается.
target clearancePointN = 0.65..1.60
Rule:
accidental tangency = geometry says "almost connected"
AND semantic/structural intent says "independent"
intentional tangency = geometry says "connected"
AND relation.intent ∈ {anchor, boundary_marker}
Therefore:
NEVER score contact only from distance.
Score = geometry × intended relation.
Матрица конфликтов по ролям
05 / role matrix
Point role
Obstacle
Near tangent
Overlap
Default policy
noise
text / protected semantic block
REJECT
HARD REJECT
точка удаляется либо пересэмплируется
noise
dominant geometry
penalty
обычно reject
обходить доминанту, если preset не разрешает texture-overlap
anchor
line endpoint/intersection
нежелательно
точный snap разрешён
перевести в exact anchor state
support
circle/rect/ellipse
penalty
soft
либо clear separation, либо committed overlap
focus
support geometry
penalty
soft
выбор по global hierarchy
marker
boundary
allowed only with intent
allowed
snap to boundary normal / defined anchor point
Корректирующие операторы
06 / corrective pass
A · Separate
Развести точку и препятствие по нормали ближайшего расстояния.
Δ = targetGap - g
P' = P + n_closest × Δ
B · Commit overlap
Если связь должна быть явной — увеличить проникновение, чтобы убрать «почти касание».
target clearancePointN = -0.30..-0.70
P' = move toward obstacle
C · Snap anchor
При role=anchor точка ставится точно на вычисленную геометрическую опору.
P' = closestBoundaryPoint(O,P)
relation.intent = anchor
D · Resize request
Модуль не меняет размер сам, а отправляет запрос владельцу размера.
request Compo 1.01:
r_target = r × k
k = 0.75..0.95
E · Resample
Для декоративной точки дешевле удалить конфликтный candidate и взять новый.
if role=noise
and correctionCost > threshold:
reject candidate
F · Escalate
Если локальное исправление ухудшает глобальные метрики, передать варианты global solver.
PROVIDE correctionCandidates[]
DO NOT choose final globally.
Correction candidate generation:
C0 = keep original
C1 = separate to preferred gap
C2 = commit overlap
C3 = snap to anchor (only if allowed)
C4 = request radius reduction
C5 = delete/resample (for low-importance roles)
Hard-filter invalid candidates.
Compute local contact cost.
Return top K candidates to global solver.
Recommended K: 2..4 [INITIAL_HEURISTIC]
Интерактивная лаборатория
07 / live classifier
Изменяйте тип препятствия, радиус и clearance. Классификация строится только по геометрии; semantic intent задаётся отдельно.
Tangent ≠ always badIntent changes scoreclearancePointN = g / 2r
Local scoring
08 / owned metrics
Clearance score
For independent relation:
target band = [0.50, 2.50] in clearancePointN
clearance_score =
1.0 inside preferred band
smooth falloff outside band
For anchor relation:
target clearancePointN = 0
For overlap relation:
target clearancePointN ≤ -0.30
Если overlap повышает выразительность доминанты, global solver может предпочесть его separation-кандидату.
global.hierarchy_score
[DEFERRED_INPUT]
Negative space
Разведение объектов может съесть ценную пустоту; поэтому локально лучший gap не всегда глобально лучший.
global.negative_space_score
[DEFERRED_INPUT]
Правило приоритета: hard semantic protection > explicit anchor relation > local contact preference > global aesthetic optimization. При конфликте hard constraints вариант удаляется ещё до global scoring.
Детерминированные тесты
10 / test cases
Case
Input
Expected derived
Expected state
Resolution
T01
r=20, circle R=80, center distance=100
g=0; clearancePointN=0
TANGENT
independent → correct; anchor → accept
T02
r1=10,r2=10,d=25
g=5; clearancePointN=0.25
NEAR_TANGENT
increase gap
T03
r1=15,r2=20,d=27
g=-8; clearancePointN=-0.267 using point1 diameter
Implementation requirement: одинаковый scene state + одинаковый seed + одинаковые входные параметры должны давать тот же набор contact states и correction candidates. Никаких LLM-вызовов внутри этого модуля.