COMPO · 1.08 · POINT NOISE FIELD · v2

Декоративный
и вычислительный
шум

Локальный генератор микроточек. Модуль не «сыпет точки на глаз», а строит воспроизводимое поле: точная единица плотности, разрешающие маски, minimum-distance sampling, локальная проверка соседей и обязательное подчинение более высоким композиционным решениям.

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

00 / Compo 0.00 protocol

CONSUMES

Compo0.Canvas Compo0.ProtectedZones[] Scene.occupiedGeometry[] Point.functionalRole Point.visualRoleCandidate Point.roleRigidity Point.visualWeightBase Point.visualWeightContextual?@fresh Compo1.06.Grid? / gridCandidates[]? StylePreset.noise Seed

PROVIDES

NoiseField NoisePoint[] NoiseField.targetCount NoiseField.actualCount NoiseField.allowedAreaN NoiseField.diagnostics NoiseField.localScore

OWNS METRICS

noise.count_fit noise.spacing_distribution noise.field_match noise.soft_intrusion_penalty noise.local_neighbor_pressure noise.local_score

DEFERRED INPUTS

global.balance_score global.negative_space_score global.hierarchy_score global.eye_flow_score typography.protectedZones image_semantics.protectedZones
Граница ответственности: Compo 1.08 не владеет глобальным clutter, hierarchy, negative space или balance. Он вычисляет только локальные свойства микроточечного поля. Если более высокий модуль требует чистой пустоты, итоговая плотность может быть снижена до нуля даже при идеальном локальном score.

Единицы плотности

01 / исправленная математика
AN
allowed normalized area

Разрешённая площадь в нормализованных координатах. Для всего холста A_N=1.

ρN
points / normalized area

Количество точек на одну полную нормализованную площадь холста.

N
target count

Целое число микроточек после учёта разрешённой площади и detail scale.

W,H = canvas size in px A_canvas_px = W * H A_allowed_px = area(usableCanvas - hardProtectedZones) A_allowed_N = A_allowed_px / A_canvas_px // dimensionless, range 0..1 rhoN = requested point count per normalized full-canvas area // unit: points / normalized-area-unit N_target = round(rhoN * A_allowed_N * detailScale) where: detailScale = STYLE_PRESET or output-context multiplier default detailScale = 1.0 EXAMPLE: W=1080, H=1350 A_allowed_px = 1,166,400 px² A_canvas_px = 1,458,000 px² A_allowed_N = 0.80 rhoN = 75 detailScale = 1.0 N_target = round(75 * 0.80) = 60
Исправление v2: нельзя смешивать px² и normalized area. Плотность хранится либо как rhoN в points / normalized-area-unit, либо отдельно как pointsPerMP. Внутри одного расчёта единицы не смешиваются.
PresetrhoNНазначениеСтатус
quiet8–24единичные технические маркерыSTYLE PRESET
editorial20–60лёгкое микрополеSTYLE PRESET
technical45–120вычислительная / схемная средаSTYLE PRESET
dense_field100–260активная точечная текстураSTYLE PRESET

Диапазоны — стартовые пресеты, а не универсальные законы. Они должны калиброваться на корпусе реальных постеров.

Поле вероятности

02 / target intensity field
MH
hard mask

0 = запрещено; 1 = разрешено.

MS
soft mask

0..1 — плавное ослабление около защищённых зон.

Z
zone weight

Локальный коэффициент активности из текущего preset/scenario.

G
style field

Полоса, градиент, кластер, grid-jitter или другой стилевой паттерн.

All field values are dimensionless in [0,1]. F_raw(x,y) = M_H(x,y) * M_S(x,y) * Z(x,y) * G(x,y) F_norm(x,y) = F_raw(x,y) / integralAllowed(F_raw) Expected local count in region R: E[N_R] = N_target * integral_R(F_norm) IMPORTANT: F_norm defines WHERE points should appear. rhoN / N_target defines HOW MANY points should appear. Do not use rhoN directly as an acceptance probability.
Исправление v2: старая схема P_accept = clamp(rho_base × masks, 0,1) смешивала «количество точек» и «вероятность». Теперь количество и пространственное распределение разделены.

Генерация кандидатов

03 / deterministic sampling

Pure random

Допустим только как намеренный chaos preset.

p = RNG(seed) no distance control clumps expected

Poisson-like

Базовый режим для свободного микрошумового поля.

accept p iff M_H(p)=1 and d(p,q) ≥ rMin(p,q) for accepted q nearby

Grid jitter

Для technical preset. Позиции наследуют grid, но получают bounded deviation.

p = gridNode + jitter |jx| ≤ Jx |jy| ≤ Jy Compo1.06 owns grid-fit
DEFAULT PIPELINE 1. Build hard masks. 2. Build soft masks. 3. Compute A_allowed_N. 4. Compute N_target from rhoN. 5. Build normalized field F_norm(x,y). 6. Initialize seeded PRNG. 7. Generate weighted candidate pool from F_norm. 8. Hard reject protected candidates. 9. Apply minimum-neighbor-distance filter. 10. Stop when N_target reached OR attempt budget exhausted. 11. Assign radius / opacity / visual role constraints. 12. Compute LOCAL metrics only. 13. Correct underfilled / overfilled zones. 14. Send proposal to global solver. 15. Freeze only after global acceptance.
Performance: neighbor queries should use spatial hashing / uniform grid. Full O(N²) pair checks are unnecessary for production.

Зоны запрета и мягкого отвода

04 / protected semantics
PROTECTED CONTENTsoft/hard exclusion envelope
GOOD: шум остаётся за пределами смыслового блока.
PROTECTED CONTENTHARD FAIL
BAD: шум атакует защищённую семантику.
Hard mask: M_H(p) ∈ {0,1} if M_H(p)=0 → candidate REJECT Soft exclusion around protected geometry: d = signedDistance(p, expandedProtectedGeometry) M_S(p) = smoothstep(0, softMarginShortN, dShortN) Suggested INITIAL_HEURISTIC margins: text / microcopy: 0.012..0.035 of short side dominant geometry: 0.010..0.030 of short side identity-critical image zones: DEFERRED to image-semantics owner

Подчинённый визуальный вес

05 / role constraint, not hierarchy ownership
r
radiusShortN

0.0012–0.0045 как стартовый STYLE/HEURISTIC диапазон.

α
opacity

0.25–0.72 — STYLE_PRESET, не закон.

WB
base weight

Берётся из Compo 1.01; здесь не пересчитывается.

WC
contextual weight

Использовать только при @fresh; иначе базовый вес.

referenceWeight = minEffectiveWeight(semanticPointsNearby) noiseWeight = Point.visualWeightContextual @fresh else Point.visualWeightBase roleWeightRatio = noiseWeight / max(referenceWeight, EPS) INITIAL_HEURISTIC: preferred roleWeightRatio ≤ 0.45 soft violation 0.45..0.70 above 0.70 → reduce radius/opacity OR reclassify visualRoleCandidate This is a LOCAL role-consistency constraint. It is NOT global.hierarchy_score.
Compo 1.08 не имеет права вычислять собственный вариант global.hierarchy_score. Он лишь проверяет, не перестал ли объект, заявленный как noise, быть микрошумом по собственным локальным ограничениям роли.

Интерактивная лаборатория

06 / seeded field

Лаборатория иллюстрирует разделение target count и spatial field. Красная зона жёстко запрещена.

Локальный scoring

07 / owned metrics only

Count + field match

noise.count_fit = 1 - clamp(abs(N_actual-N_target)/max(1,N_target), 0, 1) For K spatial bins: expected_k = N_target * integral_bin_k(F_norm) observed_k = count(points in bin_k) field_error = sum(abs(observed_k-expected_k)) / max(1, 2*N_target) noise.field_match = 1-clamp(field_error,0,1)

Spacing distribution

d_i = nearestNeighborDistance(point_i) hard condition: d_i ≥ minGap_i cv = std(d_i) / max(mean(d_i), EPS) spacing_distribution = scoreAgainstPreset(cv, StylePreset.noise.spacingProfile) // exact target depends on style

Soft intrusion

soft_intrusion_penalty = mean(1 - M_S(point_i)) hard violations are NOT a score: if any M_H(point_i)==0: candidate invalid

Neighbor pressure

pressure_i = sum_j kernel(d_ij / minGap_i) for d_ij < pressureRadius noise.local_neighbor_pressure = clamp(mean(pressure_i),0,1)
noise.local_score = w_count * noise.count_fit + w_field * noise.field_match + w_spacing * noise.spacing_distribution - w_soft * noise.soft_intrusion_penalty - w_press * noise.local_neighbor_pressure Weights = ENGINE_DEFAULT or STYLE_PRESET. ANTI-DOUBLE-COUNTING: Global solver may consume noise.local_score OR selected component metrics. It must not sum local_score + all of its components again.
Corrective pass: underfilled zone → add candidates where observed < expected; overfilled zone → remove lowest-contribution points; excessive pressure → remove points from densest neighbor cells; protected-zone violation → reject candidate, never compensate with score.

Context interactions

08 / override graph
OwnerМожет изменитьЧто Compo 1.08 делает после override
Global negative spacerhoN, active zones, вплоть до 0перегенерирует target count и поле
Global hierarchyдопустимый visual-weight envelopeуменьшает radius/opacity или reclassifies candidate
Typographyhard/soft protected zonesпересобирает masks
Image semanticsлица, логотипы, товарные protected zonesпересобирает masks
Compo 1.06 gridдопустимые grid-jitter candidatesчитает grid evidence, но не пересчитывает grid-fit
Global balancezone activity mapможет перераспределить шум, не считая его полноценной балансирующей массой
OVERRIDE PRIORITY 1. protected semantic HARD constraints 2. global composition 3. global hierarchy / negative-space intent 4. local noise constraints 5. style preset 6. seeded random variation Randomness never overrides 1..5.

Статус чисел

09 / provenance
ПараметрКлассКомментарий
area normalization, Euclidean distance, countsDERIVEDГеометрия/арифметика.
rhoN preset rangesSTYLE PRESETКалибруется по визуальному языку.
minGap, soft margins, role weight ratiosINITIAL HEURISTICСтартовые инженерные ограничения.
balance / negative-space / hierarchy / eye-flowDEFERRED INPUTПриходят от владельцев глобальных метрик.

Детерминированные тесты

10 / regression suite

T01 · Density units

A_allowed_N = 0.80 rhoN = 75 detailScale = 1 EXPECTED: N_target = 60

T02 · Seed repeatability

same input same seed same preset EXPECTED: NoisePoint[] identical max coordinate delta = 0

T03 · Hard mask

candidate p inside hard zone EXPECTED: p rejected NOT scored surviving hard violations = 0

T04 · Global override

localScore = 0.91 global negative-space intent = CLEAN EXPECTED: rhoN may become 0 local evidence remains unchanged

T05 · Role mismatch

roleWeightRatio = 0.82 EXPECTED: reduce radius/opacity OR visualRole reclassify NO global hierarchy recomputation

T06 · Anti double count

global receives local_score and component metrics EXPECTED: validator requires aggregate OR components never both

T07 · Unit invariance

same aspect + same masks 1080×1350 vs 2160×2700 same rhoN, detailScale=1 EXPECTED: same N_target normalized distribution equivalent

T08 · Grid owner

sampling=grid_jitter EXPECTED: Compo1.06.grid_fit consumed NOT redefined here

T09 · Attempt budget

N_target impossible under minGap EXPECTED: return underfilled diagnostic no infinite loop no hard-rule violation

DATA FOR LAYOUT ENGINE

11 / machine contract · v2
{ "document_id": "Compo 1.08", "version": "2.0", "module": "decorative_and_computational_point_noise", "scope": "micro-point field generation only", "finality": "LOCAL_PROPOSAL_ONLY", "consumes": [ "Compo0.Canvas", "Compo0.ProtectedZones[]", "Scene.occupiedGeometry[]", "Point.functionalRole", "Point.visualRoleCandidate", "Point.roleRigidity", "Point.visualWeightBase", "Point.visualWeightContextual?@fresh", "Compo1.06.Grid?", "Compo1.06.GridCandidate[]?", "StylePreset.noise", "Seed" ], "provides": [ "NoiseField", "NoisePoint[]", "NoiseField.targetCount", "NoiseField.actualCount", "NoiseField.allowedAreaN", "NoiseField.localScore", "NoiseField.diagnostics" ], "owns_metrics": [ "noise.count_fit", "noise.spacing_distribution", "noise.field_match", "noise.soft_intrusion_penalty", "noise.local_neighbor_pressure", "noise.local_score" ], "metric_groups": { "noise.local_score": { "components": [ "noise.count_fit", "noise.spacing_distribution", "noise.field_match", "noise.soft_intrusion_penalty", "noise.local_neighbor_pressure" ], "aggregation_rule": "consume aggregate OR components, never both" } }, "density_model": { "allowedAreaN": "A_allowed_px / (W*H)", "rhoN_unit": "points_per_normalized_canvas_area", "targetCount": "round(rhoN * allowedAreaN * detailScale)", "detailScale": {"default":1.0,"source":"STYLE_PRESET"} }, "style_density_presets_rhoN": { "quiet": [8,24], "editorial": [20,60], "technical": [45,120], "dense_field": [100,260] }, "initial_heuristics": { "radiusShortN": [0.0012,0.0045], "softMarginShortN": [0.010,0.035], "preferredNoiseToSemanticWeightRatioMax": 0.45, "softNoiseToSemanticWeightRatioMax": 0.70, "attemptBudgetMultiplier": [20,80] }, "hard_constraints": [ "no surviving noise point inside hard protected zone", "seeded repeatability for identical inputs", "finite generation attempt budget", "no local ownership of global metrics" ], "soft_constraints": [ "minimum_neighbor_distance", "target_count_fit", "target_field_match", "soft_zone_avoidance", "noise_role_weight_consistency" ], "deferred_inputs": [ "global.balance_score", "global.negative_space_score", "global.hierarchy_score", "global.eye_flow_score", "typography.protectedZones", "image_semantics.protectedZones" ], "forbidden_metric_ownership": [ "global.balance_score", "global.negative_space_score", "global.hierarchy_score", "global.eye_flow_score", "grid.fit_score", "point.visualWeightBase", "point.visualWeightContextual" ], "pipeline": [ "build_masks", "compute_allowed_area", "compute_target_count", "build_normalized_field", "generate_seeded_candidates", "hard_filter", "neighbor_distance_filter", "assign_visual_parameters", "compute_owned_metrics", "corrective_pass", "request_deferred_global_context", "apply_global_override", "freeze_after_acceptance" ], "recompute_when": [ "protected zones change", "rhoN or detailScale changes", "style noise field changes", "grid-jitter source changes", "semantic point effective weight changes materially", "global solver changes active zones" ] }

Финальный чек-лист

12 / validation
  • Плотность имеет одну явную единицу измерения.
  • A_allowed_N вычисляется через реальную площадь, затем нормализуется.
  • Количество точек отделено от пространственного field.
  • Hard protected zone не превращается в штраф — кандидат просто недопустим.
  • Random воспроизводим через seed.
  • Neighbor search имеет ограниченный attempt budget.
  • Grid-fit читается из Compo 1.06 и не пересчитывается.
  • Visual weight читается из Compo 1.01 и не пересчитывается.
  • Global hierarchy / balance / negative-space не принадлежат этому модулю.
  • Local score не суммируется повторно вместе со всеми компонентами.
  • Если global solver требует чистого пространства, rhoN=0 допустим.
  • Все негеометрические пороги имеют HEURISTIC или STYLE_PRESET статус.