Органический contour = geometry contract
3.02 строит и валидирует один локальный contour формы. Он не решает читаемость силуэта и не превращает геометрию кривой в aesthetic score.
Single-contour path: segment graph, closure, bounds, point/tangent evaluation и raw intersections.
Topology/readability → 3.03; holes/compound → 3.04; transform → 3.05; scoring → 3.06.
LOCAL SINGLE-CONTOUR GEOMETRY ONLY.
Segment graph
Organic contour хранится как последовательность явных segment primitives. Необязательные spline-форматы нормализуются в canonical segments с provenance.
| Segment | Fields | Contract |
|---|---|---|
LINE | p0,p1 | exact straight boundary segment |
QUAD | p0,c,p1 | quadratic Bézier; canonical or normalized to cubic by backend |
CUBIC | p0,c1,c2,p1 | canonical organic contour workhorse |
ARC | center,rx,ry,startAngle,sweepAngle,axisRotationRad? | analytic elliptical arc; no implicit full ellipse alias |
PathShapeGeometry
Как и в 3.01, local path geometry не содержит transform. World contour вычисляется позже через 3.05.
Canonical path
PathShapeGeometry {
kind: "PATH",
closed: true|false,
segments: ContourSegment[],
localBounds,
exactness:
"EXACT|BOUNDED_APPROXIMATION",
source: OrganicContourSource,
sourcePathHash,
version,
provenance
}Organic source
OrganicContourSource = "EXPLICIT_SEGMENTS" | "ANCHORS_AND_HANDLES" | "SMOOTHED_POLYLINE" | "PRIMITIVE_CONVERSION" | "IMPORTED_PATH" source != quality controlPointCount != beauty
Bézier evaluation
Точки и касательные — чистая geometry. Они нужны bounds, conversion, intersections и downstream topology, но не голосуют эстетически.
Quadratic
Cubic
Local bounds без occupancy
Для Bézier bounds учитывают внутренние экстремумы, а не только anchors/control box. Но bounds всё равно не становятся silhouette area или occupancy.
localBounds — geometry support box. Silhouette area/occupancy/density принадлежат теме 4.Closure и continuity
Форма-region требует замкнутого contour. При этом маленький numerical gap и настоящий semantic open contour — разные состояния.
| Case | Status | Policy |
|---|---|---|
| exact endpoint match | CLOSED | valid |
| gap ≤ owner tolerance | CLOSED_WITH_TOLERANCE | snap only under explicit normalization policy; retain provenance |
| gap > tolerance | OPEN / UNRESOLVED | do not invent closing segment |
| adjacent segment break | INVALID_CHAIN | correction request |
| zero-length segment | DEGENERATE_SEGMENT | remove only if topology unchanged and provenance retained |
Reversal и winding
Traversal order — часть path serialization, но геометрический locus сохраняется при reversal. Winding direction нужен downstream fill/topology, а величина signed area здесь не публикуется.
CW, отрицательный — CCW. Направление нельзя копировать из math +Y-up conventions без инверсии.Reversal
reverse(path): reverse segment order swap segment endpoints CUBIC: c1 ↔ c2 ARC: sweep sign flips geometry locus = SAME winding = FLIPS start semantic may change
Winding descriptor
windingDirection = "CW" | "CCW" | "DEGENERATE" implementation may use signed shoelace/integration internally, but area magnitude is NOT published as 3.02 owner metric.
Self-intersection: факт, не topology verdict
3.02 может определить, где contour пересекает сам себя. Но classification вроде simple/non-simple silhouette и её readability принадлежит 3.03.
ContourIntersectionEvent {
segmentA,
segmentB,
point,
tA,
tB,
kind:
"PROPER_CROSS" |
"TANGENT_TOUCH" |
"OVERLAP" |
"ENDPOINT_CONTACT",
exactness,
confidence,
provenance
}
Organic path → polyline
Renderer или intersection backend может потребовать flattening. Approximation должна быть budgeted и иметь error bound, иначе sampling density начнёт менять topology.
Bound
Каждая flattened curve несёт maxErrorBound в локальных/нормализованных единицах.
Segments
Есть maxSegments; бесконечное adaptive refinement запрещено.
Fallback
Approximate intersection/bounds evidence получает соответствующий exactness/confidence.
Когда нужен другой owner
3.02 не должен разрастаться в universal shape engine.
| Request | Owner / route |
|---|---|
| parameteric rectangle/ellipse/star | 3.01 |
| single freeform contour edit | 3.02 |
| silhouette readability / topology interpretation | 3.03 |
| multiple contours / holes / boolean / fill rule | 3.04 |
| position / rotation / scale / reflection / skew | Core ObjectTransform / 3.05 |
| candidate search / shape target score | 3.06 |
| silhouette area / visual mass | Topic 4 |
| scene free-space / negative space | Topic 5 |
Визуальная лаборатория
Один simple organic contour, reversed traversal, self-intersecting path и bounded flattened representation. Цвет не кодирует quality.
Reference contour tests
Проверяем continuity, Bézier endpoints, reversal, winding, closure, intersections и no-owner-theft.
DATA FOR LAYOUT ENGINE
Machine-readable contract 3.02.
{
"module": "Compo 3.02",
"name": "organic_shapes_and_path_contours",
"version": "1.1-corrective",
"scope": "LOCAL_SINGLE_CONTOUR_PATH_GEOMETRY_FOR_SHAPES",
"consumes": [
"Compo3.00.Shape",
"Compo3.00.PathShapeGeometry interface",
"Compo3.01.PrimitiveToPathResult? as optional derived source",
"GeometryToleranceContext?"
],
"provides": [
"PathShapeGeometry",
"ContourSegment[]",
"ContourPathEvaluator",
"ContourBoundsResult",
"ContourClosureResult",
"ContourIntersectionEvent[]",
"ContourWindingDirection",
"OrganicContourSource",
"PathGeometryCorrectionRequest[]"
],
"owns_contracts": [
"single-contour segment graph",
"LINE|QUAD|CUBIC|ARC segment schema",
"segment continuity and closure validation",
"local point/tangent evaluation along contour",
"exact or bounded local contour bounds",
"raw self-intersection event detection",
"winding direction descriptor without publishing area magnitude"
],
"owns_metrics": [],
"descriptors_only": [
"closed",
"segmentCount",
"segmentKinds[]",
"localBounds",
"startPoint",
"endPoint",
"closureGap",
"windingDirection",
"intersectionEvents[]",
"exactness",
"approximationErrorBound",
"sourcePathHash"
],
"segment_types": {
"LINE": {
"exact": true
},
"QUAD": {
"exact": true
},
"CUBIC": {
"exact": true
},
"ARC": {
"exact": true
}
},
"path_schema": {
"kind": "PATH",
"closed": "boolean",
"segments": "ContourSegment[]",
"localBounds": "Rect",
"exactness": "EXACT|BOUNDED_APPROXIMATION",
"source": "OrganicContourSource",
"version": "string|integer",
"provenance": "object"
},
"organic_source_types": [
"EXPLICIT_SEGMENTS",
"ANCHORS_AND_HANDLES",
"SMOOTHED_POLYLINE",
"PRIMITIVE_CONVERSION",
"IMPORTED_PATH"
],
"canonicalization": {
"adjacent_segment_endpoints": "must match within owner tolerance or be INVALID/UNRESOLVED; never silently bridge large gaps",
"closed_path": "last endpoint must equal first endpoint within tolerance; closure is explicit",
"phase_or_start_index": "canonical serialization may rotate start index only when semantic anchor is absent",
"reversal": "changes traversal/winding but must preserve unordered geometric locus",
"duplicate_zero_length_segments": "remove only when provenance is retained and topology is unchanged",
"catmull_rom_or_spline_inputs": "normalize to canonical CUBIC segments with explicit conversion provenance"
},
"boundary_interface": {
"pointAt": "(q01) -> Point; q is normalized arc-length fraction when available",
"tangentAt": "(q01) -> Vector|N/A",
"localBounds": "() -> Rect",
"intersections": "() -> ContourIntersectionEvent[]",
"windingDirection": "() -> CW|CCW|DEGENERATE",
"toPolyline": "(errorPolicy) -> BoundedApproximation"
},
"hard_constraints": [
"final filled shape contour requires closed=true; open contour is unresolved/non-region geometry unless explicitly used as an intermediate source",
"segment chain continuity is required",
"NaN/Infinity coordinates are invalid",
"local geometry must not contain object position/rotation/scale/reflection/skew",
"multiple outer contours or holes are outside 3.02 and route to 3.04",
"raw self-intersection events may be reported here, but topology classification/readability belongs to 3.03",
"winding direction may be exported; signed/absolute area magnitude is not a 3.02 owner metric",
"curvature/tension scoring from line topic 2.04/2.18 must not be copied as shape-contour quality metrics",
"visual mass / silhouette area / occupancy are not owned here",
"no beautyScore",
"winding direction must follow canonical screen +Y-down convention",
"ARC endpoints are derived from analytic arc parameters; runtime must not require undeclared p0/p1 fields"
],
"initial_heuristics": {
"closure_tolerance_shortN": {
"value": 1e-05,
"provenance": "INITIAL_HEURISTIC"
},
"polyline_approx_max_error_shortN": {
"value": 0.0005,
"provenance": "INITIAL_HEURISTIC"
},
"polyline_approx_max_segments": {
"value": 512,
"provenance": "IMPLEMENTATION_HEURISTIC"
},
"derivative_zero_epsilon": {
"value": 1e-10,
"provenance": "IMPLEMENTATION_HEURISTIC"
}
},
"correction_policy": {
"small_closure_gap": "may propose close endpoint within declared tolerance only if caller policy allows snap",
"large_closure_gap": "request path owner/generator correction; do not auto-close",
"broken_chain": "request segment endpoint correction",
"self_intersection": "report event; interpretation/correction target may involve 3.03/3.06",
"multiple_subpaths_or_holes": "route to Compo3.04",
"transform_change": "route to Compo3.05/Core ObjectTransform"
},
"deferred_inputs": [
"Compo3.03 silhouette readability and topology interpretation",
"Compo3.04 fill rule / holes / convexity / concavity",
"Compo3.05 transformed world contour",
"Compo3.06 candidate generation / local shape scoring",
"Topic4 silhouette area / occupancy / visual mass",
"Topic5 scene free space",
"global balance/hierarchy/eye-flow"
],
"recompute_when": [
"segment type or control point changes",
"closure state changes",
"geometry tolerance changes",
"path normalization/conversion policy changes"
],
"finality": "LOCAL_SINGLE_CONTOUR_GEOMETRY_ONLY",
"coordinate_convention": {
"screen_axes": "+X right, +Y down",
"winding": "positive shoelace signed area in screen coordinates => CW; negative => CCW",
"phase": "angles follow the inherited screen-coordinate convention"
},
"arc_contract": {
"fields": [
"center",
"rx",
"ry",
"startAngle",
"sweepAngle",
"axisRotationRad?"
],
"startPoint": "derived analytically at startAngle",
"endPoint": "derived analytically at startAngle+sweepAngle",
"reversal": "startAngle += sweepAngle; sweepAngle = -sweepAngle; geometric locus preserved",
"redundant_p0_p1_storage": "NOT_REQUIRED"
}
}