COMPO · 1.09 · POINT SYSTEM

Пересечения,
касания
и конфликты

Модуль формализует момент, когда точка оказывается слишком близко к другой точке, линии, кругу, эллипсу, прямоугольнику, текстовому или защищённому объекту. Его задача — отличить намеренное соединение от случайного «почти касания», вычислить clearance и выбрать корректирующее действие без участия AI.

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

00 / module api

CONSUMES

Point.radiusShortN ← Compo 1.01 Point.visualWeightBase ← Compo 1.01 Point.visualWeightContextual? ← Compo 1.01 PASS B (optional) Point.positionCandidate ← Compo 1.02 Point.role ← Compo 1.00 Scene.nearbyGeometry[] Scene.protectedZones[] Relation.intent(optional)

PROVIDES

ContactRelation[] clearancePx clearancePointN clearanceShortN clearanceScore contactConflictPenalty contactState correctionCandidates[] contactResolution

OWNS METRICS

point.clearance_score point.contact_conflict_penalty point.tangency_penalty point.contact_resolution_cost
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.

Точка ↔ axis-aligned rectangle

q = abs(P-C) - halfSize outside = length(max(q,0)) inside = min(max(q.x,q.y),0) sdBox = outside + inside g = sdBox - r DERIVED — exact SDF.

Точка ↔ rounded rectangle

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_OVERLAPclearancePointN ≤ -0.30точка явно вошла в объектразрешить только если relation intent = overlap/anchorHEURISTIC
SHALLOW_OVERLAP-0.30 < clearancePointN < -0.05неуверенное частичное пересечениеобычно исправить: углубить или разнестиHEURISTIC
TANGENT|clearancePointN| ≤ 0.05границы практически касаютсявысокий tangency penaltyHEURISTIC
NEAR_TANGENT0.05 < clearancePointN < 0.50слишком узкая щель; связь выглядит случайнойувеличить gap или сделать контакт намереннымHEURISTIC
SEPARATED0.50 ≤ clearancePointN ≤ 2.50читаемая дистанциядопустимоHEURISTIC
DETACHEDclearancePointN > 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 roleObstacleNear tangentOverlapDefault policy
noisetext / protected semantic blockREJECTHARD REJECTточка удаляется либо пересэмплируется
noisedominant geometrypenaltyобычно rejectобходить доминанту, если preset не разрешает texture-overlap
anchorline endpoint/intersectionнежелательноточный snap разрешёнперевести в exact anchor state
supportcircle/rect/ellipsepenaltysoftлибо clear separation, либо committed overlap
focussupport geometrypenaltysoftвыбор по global hierarchy
markerboundaryallowed only with intentallowedsnap 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
STATE g = …
Изменяйте тип препятствия, радиус и 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

Tangency penalty

if intent = independent: tangency_penalty = exp(-(clearancePointN/σ)²) INITIAL_HEURISTIC: σ = 0.18 if intent = anchor: tangency_penalty = 0 anchor_error = abs(clearancePointN)
contact_conflict_penalty = w_tangent × tangencyPenalty + w_overlap × illegalOverlapPenalty + w_protect × protectedZoneViolation + w_multi × multiContactAmbiguity INITIAL_HEURISTIC weights: w_tangent = 0.30 w_overlap = 0.35 w_protect = 1.00 // hard semantic protection may reject before scoring w_multi = 0.20 IMPORTANT: global.balance_score = DEFERRED_INPUT global.hierarchy_score = DEFERRED_INPUT global.negative_space_score = DEFERRED_INPUT global.eye_flow_score = DEFERRED_INPUT This module does not recompute them.

Контекст и переопределения

09 / interactions

Typography

Текст может объявить enlarged protected zone. Точка не имеет права сама уменьшать эту зону.

Text.protectedGeometry ← future typography module [DEFERRED_INPUT]

Hierarchy

Если 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
CaseInputExpected derivedExpected stateResolution
T01r=20, circle R=80, center distance=100g=0; clearancePointN=0TANGENTindependent → correct; anchor → accept
T02r1=10,r2=10,d=25g=5; clearancePointN=0.25NEAR_TANGENTincrease gap
T03r1=15,r2=20,d=27g=-8; clearancePointN=-0.267 using point1 diameterSHALLOW_OVERLAPseparate or commit
T04r=12, line width=4, center-to-segment d=14g=0TANGENTanchor allowed; independent penalized
T05noise point intersects protected text zoneprotectedViolation=trueHARD_CONFLICTreject/resample
T06anchor point boundary error 0.7 pxclosest point knownANCHOR_MISALIGNsnap exactly to boundary

Data for layout engine

11 / machine contract
{ "module": "Compo 1.09", "name": "point_contact_conflict", "version": "2.0", "consumes": [ "Point.radiusShortN", "Point.visualWeightBase", "Point.visualWeightContextual?", "Point.positionCandidate", "Point.role", "Scene.nearbyGeometry[]", "Scene.protectedZones[]", "Relation.intent?" ], "provides": [ "ContactRelation[]", "clearancePx", "clearancePointN", "clearanceShortN", "clearanceScore", "contactConflictPenalty", "contactState", "correctionCandidates[]" ], "owns_metrics": [ "point.clearance_score", "point.contact_conflict_penalty", "point.tangency_penalty", "point.contact_resolution_cost" ], "deferred_inputs": [ "global.balance_score", "global.hierarchy_score", "global.negative_space_score", "global.eye_flow_score", "typography.protected_geometry" ], "metric_space": { "canonical": "PIXEL_ISOTROPIC", "forbidden": "raw normalized Euclidean distance when W != H", "conversion": "xPx=xN*W; yPx=yN*H" }, "derived": { "disk_disk_clearance": {"formula":"length(P1-P2)-(r1+r2)","precision":"DERIVED_EXACT"}, "disk_circle_clearance": {"formula":"length(P-C)-(r+R)","precision":"DERIVED_EXACT"}, "disk_round_segment_clearance": {"formula":"distancePointSegment(P,A,B)-(r+lineWidth/2)","precision":"DERIVED_EXACT_FOR_CAPSULE"}, "disk_box_clearance": {"formula":"sdBox(P,box)-r","precision":"DERIVED_EXACT"}, "disk_ellipse_clearance": {"method":"closest-point numerical solver","precision":"NUMERICAL_DERIVED","exports":"solverErrorPx"}, "clearancePointN": "clearancePx/(2*rPx)", "clearanceShortN": "clearancePx/min(W,H)" }, "initial_heuristics": { "deep_overlap_max_clearancePointN": -0.30, "shallow_overlap_max_clearancePointN": -0.05, "tangent_abs_clearancePointN": 0.05, "near_tangent_max_clearancePointN": 0.50, "preferred_separation_max_clearancePointN": 2.50, "tangency_sigma": 0.18, "correction_top_k": [2,4] }, "correction_operators": [ "separate_along_closest_normal", "commit_overlap", "snap_anchor", "request_resize_from_Compo_1.01", "reject_or_resample", "escalate_candidates_to_global_solver" ], "pipeline": [ "broad_phase_nearby_geometry", "compute_signed_clearance_in_pixel_space", "normalize_clearance", "infer_or_read_relation_intent", "classify_contact_state", "apply_hard_constraints", "generate_corrections", "score_local_candidates", "return_top_k_to_global_solver" ] }
Implementation requirement: одинаковый scene state + одинаковый seed + одинаковые входные параметры должны давать тот же набор contact states и correction candidates. Никаких LLM-вызовов внутри этого модуля.