From 076a8c90f102ac97e06417b8caa515bba6999f8e Mon Sep 17 00:00:00 2001 From: nan Date: Thu, 17 Sep 2026 17:19:05 +0800 Subject: [PATCH] Add specifications for two-times upscale and unified depth plane features - Introduced the two-times upscale specification detailing requirements for unified output, fixed model presets, arbitrary dimensions, and consistent job behavior. - Added scenarios for existing models, repeated upscale, and handling of alpha preservation. - Created the unified depth plane specification outlining geometry modes, stable interface, and asset evaluation. - Defined scenarios for mode switching, relief and camera preservation, and validation of delivered assets. --- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/hdr-background-removal/spec.md | 0 .../2026-09-17-add-ben2-hdr-support}/tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../2026-09-17-add-cutout-polyline}/design.md | 0 .../proposal.md | 0 .../specs/cutout-polyline/spec.md | 0 .../specs/mask-mode-options/spec.md | 0 .../2026-09-17-add-cutout-polyline}/tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../image-layer-alpha-adjustment/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/mask-editing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../2026-09-17-add-moge3-model}/design.md | 0 .../2026-09-17-add-moge3-model}/proposal.md | 0 .../specs/moge3-model/spec.md | 0 .../2026-09-17-add-moge3-model}/tasks.md | 0 .../2026-09-17-add-moge3-model}/validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/panorama-conversion/spec.md | 0 .../panorama-geometry-generation/spec.md | 0 .../specs/panorama-surface/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/two-times-upscale/spec.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-lasso-entry/spec.md | 0 .../specs/projected-depth-surface/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/image-depth-symmetry/spec.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/texture-node-image-actions/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/panorama-input-cropping/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/depth-surface-strip-cleanup/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/compact-repeat-arrangement/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/post-cutout-symmetry/spec.md | 0 .../specs/shell-normal-smoothing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/material-view-adaptation/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/coreml-inference/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/frame-edge-antialiasing/spec.md | 0 .../specs/image-alpha-presentation/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-edge-depth/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-mesh-sampling/spec.md | 0 .../specs/image-edit-results/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/image-tool-ai-availability/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../experiment-cases.md | 0 .../experiment.md | 0 .../implementation.md | 0 .../proposal.md | 0 .../specs/cutout-adaptive-mesh/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/relief-plane-depth-controls/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/empty-image-edit-isolation/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/joint-geometry-node-layout/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../polyline-gesture-interaction/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/moge-depth-artifacts/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-attribute-lifecycle/spec.md | 0 .../cutout-geometry-node-efficiency/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../2026-09-17-organize-tool-cli}/design.md | 0 .../2026-09-17-organize-tool-cli}/proposal.md | 0 .../specs/developer-tool-cli/spec.md | 0 .../2026-09-17-organize-tool-cli}/tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-boundary-padding/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/crop-tool-options/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/pinned-geometry-smoothing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/frame-transparent-rgb/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/image-tool-interaction/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/image-selection-processing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/panorama-surface-prototype/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../cutout-split-boundary-shaping/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-symmetry-seam/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/geometry-depth-controls/spec.md | 0 .../specs/image-result-materials/spec.md | 0 .../selection-content-processing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/model-inference-memory/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../maintainable-project-structure/spec.md | 0 .../production-only-ai-workflows/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/geometry-field-flow/spec.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/geometry-node-smoothing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-normal-policy/spec.md | 0 .../specs/cutout-shape-presets/spec.md | 0 .../specs/depth-cutout-volume/spec.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/brush-stroke-processing/spec.md | 0 .../specs/frame-projection-clipping/spec.md | 0 .../specs/image-pixel-read-integrity/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/frame-tool/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/image-edit-tools/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../depth-surface-face-separation/spec.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-depth-extension/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../depth-surface-stretch-culling/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/conversion-source-images/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/plane-conversion/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/depth-split-selection/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../depth-scaled-tangent-normals/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/depth-alpha-semantics/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/panorama-mask-regions/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../selection-content-processing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/server-cache-display/spec.md | 0 .../specs/server-lifecycle-controls/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../mirror-validation.json | 0 .../proposal.md | 0 .../retirement-results.json | 0 .../retirement.json | 0 .../specs/ai-model-tiers/spec.md | 0 .../specs/background-removal-models/spec.md | 0 .../specs/hat-upscale/spec.md | 0 .../specs/model-asset-retirement/spec.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/depth-node-options/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../tasks.md | 0 .../validation.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/selection-rasterization/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/depth-plane-alpha-culling/spec.md | 0 .../separate-depth-relief-planes/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/canonical-object-frame/spec.md | 0 .../specs/depth-symmetry-orientation/spec.md | 0 .../object-space-normal-alignment/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-axis-convention/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/frame-tool/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/ai-input-validation/spec.md | 0 .../ai-operator-failure-handling/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/boundary-depth-smoothing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/depth-artifact-contract/spec.md | 0 .../specs/unified-depth-plane/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/mask-cut-depth-sampling/spec.md | 0 .../2026-09-17-unify-mask-cut-depth}/tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/clipboard-image-cache/spec.md | 0 .../specs/material-color-images/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/reference-depth-calibration/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/image-selection/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/pasted-brush-preview/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-quality-meshing/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-edge-length/spec.md | 0 .../tasks.md | 0 .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cutout-boundary-smoothing/spec.md | 0 .../tasks.md | 0 openspec/specs/ai-input-validation/spec.md | 65 +++++ openspec/specs/ai-model-tiers/spec.md | 42 +++ .../ai-operator-failure-handling/spec.md | 37 +++ .../specs/background-removal-models/spec.md | 59 +++++ .../specs/boundary-depth-smoothing/spec.md | 123 +++++++++ .../specs/brush-stroke-processing/spec.md | 52 ++++ openspec/specs/canonical-object-frame/spec.md | 67 +++++ openspec/specs/clipboard-image-cache/spec.md | 43 +++ .../specs/compact-repeat-arrangement/spec.md | 68 +++++ .../specs/conversion-source-images/spec.md | 93 +++++++ openspec/specs/coreml-inference/spec.md | 73 ++++++ openspec/specs/crop-tool-options/spec.md | 118 +++++++++ openspec/specs/cutout-adaptive-mesh/spec.md | 101 ++++++++ .../specs/cutout-attribute-lifecycle/spec.md | 47 ++++ openspec/specs/cutout-axis-convention/spec.md | 65 +++++ .../specs/cutout-boundary-padding/spec.md | 64 +++++ .../specs/cutout-boundary-smoothing/spec.md | 34 +++ openspec/specs/cutout-depth-extension/spec.md | 53 ++++ openspec/specs/cutout-edge-depth/spec.md | 69 +++++ openspec/specs/cutout-edge-length/spec.md | 69 +++++ .../cutout-geometry-node-efficiency/spec.md | 75 ++++++ openspec/specs/cutout-lasso-entry/spec.md | 27 ++ openspec/specs/cutout-mesh-sampling/spec.md | 27 ++ openspec/specs/cutout-normal-policy/spec.md | 49 ++++ openspec/specs/cutout-polyline/spec.md | 61 +++++ openspec/specs/cutout-quality-meshing/spec.md | 75 ++++++ openspec/specs/cutout-shape-presets/spec.md | 60 +++++ .../cutout-split-boundary-shaping/spec.md | 73 ++++++ openspec/specs/cutout-symmetry-seam/spec.md | 108 ++++++++ openspec/specs/depth-alpha-semantics/spec.md | 65 +++++ .../specs/depth-artifact-contract/spec.md | 42 +++ openspec/specs/depth-cutout-volume/spec.md | 49 ++++ openspec/specs/depth-node-options/spec.md | 51 ++++ .../specs/depth-plane-alpha-culling/spec.md | 40 +++ .../depth-scaled-tangent-normals/spec.md | 31 +++ openspec/specs/depth-split-selection/spec.md | 27 ++ .../depth-surface-face-separation/spec.md | 131 ++++++++++ .../depth-surface-stretch-culling/spec.md | 57 ++++ .../specs/depth-surface-strip-cleanup/spec.md | 73 ++++++ .../specs/depth-symmetry-orientation/spec.md | 47 ++++ openspec/specs/developer-tool-cli/spec.md | 81 ++++++ .../specs/empty-image-edit-isolation/spec.md | 76 ++++++ .../specs/frame-edge-antialiasing/spec.md | 85 ++++++ .../specs/frame-projection-clipping/spec.md | 36 +++ openspec/specs/frame-tool/spec.md | 244 ++++++++++++++++++ openspec/specs/frame-transparent-rgb/spec.md | 95 +++++++ .../specs/geometry-depth-controls/spec.md | 27 ++ openspec/specs/geometry-field-flow/spec.md | 61 +++++ .../specs/geometry-node-smoothing/spec.md | 38 +++ openspec/specs/hat-upscale/spec.md | 49 ++++ openspec/specs/hdr-background-removal/spec.md | 93 +++++++ .../specs/image-alpha-presentation/spec.md | 98 +++++++ openspec/specs/image-depth-symmetry/spec.md | 78 ++++++ openspec/specs/image-edit-results/spec.md | 42 +++ openspec/specs/image-edit-tools/spec.md | 128 +++++++++ .../image-layer-alpha-adjustment/spec.md | 60 +++++ .../specs/image-pixel-read-integrity/spec.md | 37 +++ openspec/specs/image-result-materials/spec.md | 20 ++ .../specs/image-selection-processing/spec.md | 75 ++++++ openspec/specs/image-selection/spec.md | 37 +++ .../specs/image-tool-ai-availability/spec.md | 57 ++++ openspec/specs/image-tool-interaction/spec.md | 64 +++++ .../specs/joint-geometry-node-layout/spec.md | 49 ++++ .../maintainable-project-structure/spec.md | 41 +++ .../specs/mask-cut-depth-sampling/spec.md | 103 ++++++++ openspec/specs/mask-editing/spec.md | 98 +++++++ openspec/specs/mask-mode-options/spec.md | 29 +++ openspec/specs/material-color-images/spec.md | 82 ++++++ .../specs/material-view-adaptation/spec.md | 112 ++++++++ openspec/specs/model-asset-retirement/spec.md | 38 +++ openspec/specs/model-inference-memory/spec.md | 61 +++++ openspec/specs/moge-depth-artifacts/spec.md | 113 ++++++++ openspec/specs/moge3-model/spec.md | 73 ++++++ .../object-space-normal-alignment/spec.md | 39 +++ openspec/specs/panorama-conversion/spec.md | 37 +++ .../panorama-geometry-generation/spec.md | 37 +++ .../specs/panorama-input-cropping/spec.md | 46 ++++ openspec/specs/panorama-mask-regions/spec.md | 95 +++++++ .../specs/panorama-surface-prototype/spec.md | 37 +++ openspec/specs/panorama-surface/spec.md | 45 ++++ openspec/specs/pasted-brush-preview/spec.md | 45 ++++ .../specs/pinned-geometry-smoothing/spec.md | 109 ++++++++ openspec/specs/plane-conversion/spec.md | 113 ++++++++ .../polyline-gesture-interaction/spec.md | 65 +++++ openspec/specs/post-cutout-symmetry/spec.md | 78 ++++++ .../production-only-ai-workflows/spec.md | 49 ++++ .../specs/projected-depth-surface/spec.md | 89 +++++++ .../specs/reference-depth-calibration/spec.md | 108 ++++++++ .../specs/relief-plane-depth-controls/spec.md | 77 ++++++ .../selection-content-processing/spec.md | 143 ++++++++++ .../specs/selection-rasterization/spec.md | 68 +++++ .../separate-depth-relief-planes/spec.md | 53 ++++ openspec/specs/server-cache-display/spec.md | 20 ++ .../specs/server-lifecycle-controls/spec.md | 20 ++ openspec/specs/shell-normal-smoothing/spec.md | 23 ++ .../specs/texture-node-image-actions/spec.md | 85 ++++++ openspec/specs/two-times-upscale/spec.md | 78 ++++++ openspec/specs/unified-depth-plane/spec.md | 62 +++++ pyproject.toml | 2 +- src/anyimage/blender_manifest.toml | 2 +- uv.lock | 2 +- 524 files changed, 6514 insertions(+), 3 deletions(-) rename openspec/changes/{add-ben2-hdr-support => archive/2026-09-17-add-ben2-hdr-support}/.openspec.yaml (100%) rename openspec/changes/{add-ben2-hdr-support => archive/2026-09-17-add-ben2-hdr-support}/design.md (100%) rename openspec/changes/{add-ben2-hdr-support => archive/2026-09-17-add-ben2-hdr-support}/proposal.md (100%) rename openspec/changes/{add-ben2-hdr-support => archive/2026-09-17-add-ben2-hdr-support}/specs/hdr-background-removal/spec.md (100%) rename openspec/changes/{add-ben2-hdr-support => archive/2026-09-17-add-ben2-hdr-support}/tasks.md (100%) rename openspec/changes/{add-ben2-hdr-support => archive/2026-09-17-add-ben2-hdr-support}/validation.md (100%) rename openspec/changes/{add-cutout-polyline => archive/2026-09-17-add-cutout-polyline}/.openspec.yaml (100%) rename openspec/changes/{add-cutout-polyline => archive/2026-09-17-add-cutout-polyline}/design.md (100%) rename openspec/changes/{add-cutout-polyline => archive/2026-09-17-add-cutout-polyline}/proposal.md (100%) rename openspec/changes/{add-cutout-polyline => archive/2026-09-17-add-cutout-polyline}/specs/cutout-polyline/spec.md (100%) rename openspec/changes/{add-cutout-polyline => archive/2026-09-17-add-cutout-polyline}/specs/mask-mode-options/spec.md (100%) rename openspec/changes/{add-cutout-polyline => archive/2026-09-17-add-cutout-polyline}/tasks.md (100%) rename openspec/changes/{add-image-layer-alpha-adjustment => archive/2026-09-17-add-image-layer-alpha-adjustment}/.openspec.yaml (100%) rename openspec/changes/{add-image-layer-alpha-adjustment => archive/2026-09-17-add-image-layer-alpha-adjustment}/design.md (100%) rename openspec/changes/{add-image-layer-alpha-adjustment => archive/2026-09-17-add-image-layer-alpha-adjustment}/proposal.md (100%) rename openspec/changes/{add-image-layer-alpha-adjustment => archive/2026-09-17-add-image-layer-alpha-adjustment}/specs/image-layer-alpha-adjustment/spec.md (100%) rename openspec/changes/{add-image-layer-alpha-adjustment => archive/2026-09-17-add-image-layer-alpha-adjustment}/tasks.md (100%) rename openspec/changes/{add-live-mask-preview-and-polyline => archive/2026-09-17-add-live-mask-preview-and-polyline}/.openspec.yaml (100%) rename openspec/changes/{add-live-mask-preview-and-polyline => archive/2026-09-17-add-live-mask-preview-and-polyline}/design.md (100%) rename openspec/changes/{add-live-mask-preview-and-polyline => archive/2026-09-17-add-live-mask-preview-and-polyline}/proposal.md (100%) rename openspec/changes/{add-live-mask-preview-and-polyline => archive/2026-09-17-add-live-mask-preview-and-polyline}/specs/mask-editing/spec.md (100%) rename openspec/changes/{add-live-mask-preview-and-polyline => archive/2026-09-17-add-live-mask-preview-and-polyline}/tasks.md (100%) rename openspec/changes/{add-moge3-model => archive/2026-09-17-add-moge3-model}/.openspec.yaml (100%) rename openspec/changes/{add-moge3-model => archive/2026-09-17-add-moge3-model}/design.md (100%) rename openspec/changes/{add-moge3-model => archive/2026-09-17-add-moge3-model}/proposal.md (100%) rename openspec/changes/{add-moge3-model => archive/2026-09-17-add-moge3-model}/specs/moge3-model/spec.md (100%) rename openspec/changes/{add-moge3-model => archive/2026-09-17-add-moge3-model}/tasks.md (100%) rename openspec/changes/{add-moge3-model => archive/2026-09-17-add-moge3-model}/validation.md (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/.openspec.yaml (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/design.md (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/proposal.md (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/specs/panorama-conversion/spec.md (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/specs/panorama-geometry-generation/spec.md (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/specs/panorama-surface/spec.md (100%) rename openspec/changes/{add-panorama-conversion => archive/2026-09-17-add-panorama-conversion}/tasks.md (100%) rename openspec/changes/{add-pisa-sr-two-times-upscale => archive/2026-09-17-add-pisa-sr-two-times-upscale}/.openspec.yaml (100%) rename openspec/changes/{add-pisa-sr-two-times-upscale => archive/2026-09-17-add-pisa-sr-two-times-upscale}/design.md (100%) rename openspec/changes/{add-pisa-sr-two-times-upscale => archive/2026-09-17-add-pisa-sr-two-times-upscale}/proposal.md (100%) rename openspec/changes/{add-pisa-sr-two-times-upscale => archive/2026-09-17-add-pisa-sr-two-times-upscale}/specs/two-times-upscale/spec.md (100%) rename openspec/changes/{add-pisa-sr-two-times-upscale => archive/2026-09-17-add-pisa-sr-two-times-upscale}/tasks.md (100%) rename openspec/changes/{add-pisa-sr-two-times-upscale => archive/2026-09-17-add-pisa-sr-two-times-upscale}/validation.md (100%) rename openspec/changes/{add-projected-depth-plane => archive/2026-09-17-add-projected-depth-plane}/.openspec.yaml (100%) rename openspec/changes/{add-projected-depth-plane => archive/2026-09-17-add-projected-depth-plane}/design.md (100%) rename openspec/changes/{add-projected-depth-plane => archive/2026-09-17-add-projected-depth-plane}/proposal.md (100%) rename openspec/changes/{add-projected-depth-plane => archive/2026-09-17-add-projected-depth-plane}/specs/cutout-lasso-entry/spec.md (100%) rename openspec/changes/{add-projected-depth-plane => archive/2026-09-17-add-projected-depth-plane}/specs/projected-depth-surface/spec.md (100%) rename openspec/changes/{add-projected-depth-plane => archive/2026-09-17-add-projected-depth-plane}/tasks.md (100%) rename openspec/changes/{add-projected-depth-symmetry => archive/2026-09-17-add-projected-depth-symmetry}/.openspec.yaml (100%) rename openspec/changes/{add-projected-depth-symmetry => archive/2026-09-17-add-projected-depth-symmetry}/design.md (100%) rename openspec/changes/{add-projected-depth-symmetry => archive/2026-09-17-add-projected-depth-symmetry}/proposal.md (100%) rename openspec/changes/{add-projected-depth-symmetry => archive/2026-09-17-add-projected-depth-symmetry}/specs/image-depth-symmetry/spec.md (100%) rename openspec/changes/{add-projected-depth-symmetry => archive/2026-09-17-add-projected-depth-symmetry}/tasks.md (100%) rename openspec/changes/{add-projected-depth-symmetry => archive/2026-09-17-add-projected-depth-symmetry}/validation.md (100%) rename openspec/changes/{add-texture-node-image-actions => archive/2026-09-17-add-texture-node-image-actions}/.openspec.yaml (100%) rename openspec/changes/{add-texture-node-image-actions => archive/2026-09-17-add-texture-node-image-actions}/design.md (100%) rename openspec/changes/{add-texture-node-image-actions => archive/2026-09-17-add-texture-node-image-actions}/proposal.md (100%) rename openspec/changes/{add-texture-node-image-actions => archive/2026-09-17-add-texture-node-image-actions}/specs/texture-node-image-actions/spec.md (100%) rename openspec/changes/{add-texture-node-image-actions => archive/2026-09-17-add-texture-node-image-actions}/tasks.md (100%) rename openspec/changes/{auto-crop-panorama-input => archive/2026-09-17-auto-crop-panorama-input}/.openspec.yaml (100%) rename openspec/changes/{auto-crop-panorama-input => archive/2026-09-17-auto-crop-panorama-input}/design.md (100%) rename openspec/changes/{auto-crop-panorama-input => archive/2026-09-17-auto-crop-panorama-input}/proposal.md (100%) rename openspec/changes/{auto-crop-panorama-input => archive/2026-09-17-auto-crop-panorama-input}/specs/panorama-input-cropping/spec.md (100%) rename openspec/changes/{auto-crop-panorama-input => archive/2026-09-17-auto-crop-panorama-input}/tasks.md (100%) rename openspec/changes/{clean-depth-surface-strip-faces => archive/2026-09-17-clean-depth-surface-strip-faces}/.openspec.yaml (100%) rename openspec/changes/{clean-depth-surface-strip-faces => archive/2026-09-17-clean-depth-surface-strip-faces}/design.md (100%) rename openspec/changes/{clean-depth-surface-strip-faces => archive/2026-09-17-clean-depth-surface-strip-faces}/proposal.md (100%) rename openspec/changes/{clean-depth-surface-strip-faces => archive/2026-09-17-clean-depth-surface-strip-faces}/specs/depth-surface-strip-cleanup/spec.md (100%) rename openspec/changes/{clean-depth-surface-strip-faces => archive/2026-09-17-clean-depth-surface-strip-faces}/tasks.md (100%) rename openspec/changes/{compact-repeat-node-arrangement => archive/2026-09-17-compact-repeat-node-arrangement}/.openspec.yaml (100%) rename openspec/changes/{compact-repeat-node-arrangement => archive/2026-09-17-compact-repeat-node-arrangement}/design.md (100%) rename openspec/changes/{compact-repeat-node-arrangement => archive/2026-09-17-compact-repeat-node-arrangement}/proposal.md (100%) rename openspec/changes/{compact-repeat-node-arrangement => archive/2026-09-17-compact-repeat-node-arrangement}/specs/compact-repeat-arrangement/spec.md (100%) rename openspec/changes/{compact-repeat-node-arrangement => archive/2026-09-17-compact-repeat-node-arrangement}/tasks.md (100%) rename openspec/changes/{compose-depth-cutout-symmetry => archive/2026-09-17-compose-depth-cutout-symmetry}/.openspec.yaml (100%) rename openspec/changes/{compose-depth-cutout-symmetry => archive/2026-09-17-compose-depth-cutout-symmetry}/design.md (100%) rename openspec/changes/{compose-depth-cutout-symmetry => archive/2026-09-17-compose-depth-cutout-symmetry}/proposal.md (100%) rename openspec/changes/{compose-depth-cutout-symmetry => archive/2026-09-17-compose-depth-cutout-symmetry}/specs/post-cutout-symmetry/spec.md (100%) rename openspec/changes/{compose-depth-cutout-symmetry => archive/2026-09-17-compose-depth-cutout-symmetry}/specs/shell-normal-smoothing/spec.md (100%) rename openspec/changes/{compose-depth-cutout-symmetry => archive/2026-09-17-compose-depth-cutout-symmetry}/tasks.md (100%) rename openspec/changes/{configure-material-view-adaptation => archive/2026-09-17-configure-material-view-adaptation}/.openspec.yaml (100%) rename openspec/changes/{configure-material-view-adaptation => archive/2026-09-17-configure-material-view-adaptation}/design.md (100%) rename openspec/changes/{configure-material-view-adaptation => archive/2026-09-17-configure-material-view-adaptation}/proposal.md (100%) rename openspec/changes/{configure-material-view-adaptation => archive/2026-09-17-configure-material-view-adaptation}/specs/material-view-adaptation/spec.md (100%) rename openspec/changes/{configure-material-view-adaptation => archive/2026-09-17-configure-material-view-adaptation}/tasks.md (100%) rename openspec/changes/{correct-coreml-device-support => archive/2026-09-17-correct-coreml-device-support}/.openspec.yaml (100%) rename openspec/changes/{correct-coreml-device-support => archive/2026-09-17-correct-coreml-device-support}/design.md (100%) rename openspec/changes/{correct-coreml-device-support => archive/2026-09-17-correct-coreml-device-support}/proposal.md (100%) rename openspec/changes/{correct-coreml-device-support => archive/2026-09-17-correct-coreml-device-support}/specs/coreml-inference/spec.md (100%) rename openspec/changes/{correct-coreml-device-support => archive/2026-09-17-correct-coreml-device-support}/tasks.md (100%) rename openspec/changes/{enable-image-alpha-blending-and-premul => archive/2026-09-17-enable-image-alpha-blending-and-premul}/.openspec.yaml (100%) rename openspec/changes/{enable-image-alpha-blending-and-premul => archive/2026-09-17-enable-image-alpha-blending-and-premul}/design.md (100%) rename openspec/changes/{enable-image-alpha-blending-and-premul => archive/2026-09-17-enable-image-alpha-blending-and-premul}/proposal.md (100%) rename openspec/changes/{enable-image-alpha-blending-and-premul => archive/2026-09-17-enable-image-alpha-blending-and-premul}/specs/frame-edge-antialiasing/spec.md (100%) rename openspec/changes/{enable-image-alpha-blending-and-premul => archive/2026-09-17-enable-image-alpha-blending-and-premul}/specs/image-alpha-presentation/spec.md (100%) rename openspec/changes/{enable-image-alpha-blending-and-premul => archive/2026-09-17-enable-image-alpha-blending-and-premul}/tasks.md (100%) rename openspec/changes/{extend-cutout-edge-depth => archive/2026-09-17-extend-cutout-edge-depth}/.openspec.yaml (100%) rename openspec/changes/{extend-cutout-edge-depth => archive/2026-09-17-extend-cutout-edge-depth}/design.md (100%) rename openspec/changes/{extend-cutout-edge-depth => archive/2026-09-17-extend-cutout-edge-depth}/proposal.md (100%) rename openspec/changes/{extend-cutout-edge-depth => archive/2026-09-17-extend-cutout-edge-depth}/specs/cutout-edge-depth/spec.md (100%) rename openspec/changes/{extend-cutout-edge-depth => archive/2026-09-17-extend-cutout-edge-depth}/tasks.md (100%) rename openspec/changes/{fix-cutout-density-and-image-edit-names => archive/2026-09-17-fix-cutout-density-and-image-edit-names}/.openspec.yaml (100%) rename openspec/changes/{fix-cutout-density-and-image-edit-names => archive/2026-09-17-fix-cutout-density-and-image-edit-names}/design.md (100%) rename openspec/changes/{fix-cutout-density-and-image-edit-names => archive/2026-09-17-fix-cutout-density-and-image-edit-names}/proposal.md (100%) rename openspec/changes/{fix-cutout-density-and-image-edit-names => archive/2026-09-17-fix-cutout-density-and-image-edit-names}/specs/cutout-mesh-sampling/spec.md (100%) rename openspec/changes/{fix-cutout-density-and-image-edit-names => archive/2026-09-17-fix-cutout-density-and-image-edit-names}/specs/image-edit-results/spec.md (100%) rename openspec/changes/{fix-cutout-density-and-image-edit-names => archive/2026-09-17-fix-cutout-density-and-image-edit-names}/tasks.md (100%) rename openspec/changes/{gate-cutout-ai-options => archive/2026-09-17-gate-cutout-ai-options}/.openspec.yaml (100%) rename openspec/changes/{gate-cutout-ai-options => archive/2026-09-17-gate-cutout-ai-options}/design.md (100%) rename openspec/changes/{gate-cutout-ai-options => archive/2026-09-17-gate-cutout-ai-options}/proposal.md (100%) rename openspec/changes/{gate-cutout-ai-options => archive/2026-09-17-gate-cutout-ai-options}/specs/image-tool-ai-availability/spec.md (100%) rename openspec/changes/{gate-cutout-ai-options => archive/2026-09-17-gate-cutout-ai-options}/tasks.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/.openspec.yaml (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/design.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/experiment-cases.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/experiment.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/implementation.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/proposal.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/specs/cutout-adaptive-mesh/spec.md (100%) rename openspec/changes/{improve-cutout-adaptive-mesh => archive/2026-09-17-improve-cutout-adaptive-mesh}/tasks.md (100%) rename openspec/changes/{improve-relief-depth-controls => archive/2026-09-17-improve-relief-depth-controls}/.openspec.yaml (100%) rename openspec/changes/{improve-relief-depth-controls => archive/2026-09-17-improve-relief-depth-controls}/design.md (100%) rename openspec/changes/{improve-relief-depth-controls => archive/2026-09-17-improve-relief-depth-controls}/proposal.md (100%) rename openspec/changes/{improve-relief-depth-controls => archive/2026-09-17-improve-relief-depth-controls}/specs/relief-plane-depth-controls/spec.md (100%) rename openspec/changes/{improve-relief-depth-controls => archive/2026-09-17-improve-relief-depth-controls}/tasks.md (100%) rename openspec/changes/{isolate-shared-empty-image-edits => archive/2026-09-17-isolate-shared-empty-image-edits}/.openspec.yaml (100%) rename openspec/changes/{isolate-shared-empty-image-edits => archive/2026-09-17-isolate-shared-empty-image-edits}/design.md (100%) rename openspec/changes/{isolate-shared-empty-image-edits => archive/2026-09-17-isolate-shared-empty-image-edits}/proposal.md (100%) rename openspec/changes/{isolate-shared-empty-image-edits => archive/2026-09-17-isolate-shared-empty-image-edits}/specs/empty-image-edit-isolation/spec.md (100%) rename openspec/changes/{isolate-shared-empty-image-edits => archive/2026-09-17-isolate-shared-empty-image-edits}/tasks.md (100%) rename openspec/changes/{joint-geometry-node-layout => archive/2026-09-17-joint-geometry-node-layout}/.openspec.yaml (100%) rename openspec/changes/{joint-geometry-node-layout => archive/2026-09-17-joint-geometry-node-layout}/design.md (100%) rename openspec/changes/{joint-geometry-node-layout => archive/2026-09-17-joint-geometry-node-layout}/proposal.md (100%) rename openspec/changes/{joint-geometry-node-layout => archive/2026-09-17-joint-geometry-node-layout}/specs/joint-geometry-node-layout/spec.md (100%) rename openspec/changes/{joint-geometry-node-layout => archive/2026-09-17-joint-geometry-node-layout}/tasks.md (100%) rename openspec/changes/{match-polyline-gesture-interaction => archive/2026-09-17-match-polyline-gesture-interaction}/.openspec.yaml (100%) rename openspec/changes/{match-polyline-gesture-interaction => archive/2026-09-17-match-polyline-gesture-interaction}/design.md (100%) rename openspec/changes/{match-polyline-gesture-interaction => archive/2026-09-17-match-polyline-gesture-interaction}/proposal.md (100%) rename openspec/changes/{match-polyline-gesture-interaction => archive/2026-09-17-match-polyline-gesture-interaction}/specs/polyline-gesture-interaction/spec.md (100%) rename openspec/changes/{match-polyline-gesture-interaction => archive/2026-09-17-match-polyline-gesture-interaction}/tasks.md (100%) rename openspec/changes/{normalize-moge-alpha-depth => archive/2026-09-17-normalize-moge-alpha-depth}/.openspec.yaml (100%) rename openspec/changes/{normalize-moge-alpha-depth => archive/2026-09-17-normalize-moge-alpha-depth}/design.md (100%) rename openspec/changes/{normalize-moge-alpha-depth => archive/2026-09-17-normalize-moge-alpha-depth}/proposal.md (100%) rename openspec/changes/{normalize-moge-alpha-depth => archive/2026-09-17-normalize-moge-alpha-depth}/specs/moge-depth-artifacts/spec.md (100%) rename openspec/changes/{normalize-moge-alpha-depth => archive/2026-09-17-normalize-moge-alpha-depth}/tasks.md (100%) rename openspec/changes/{optimize-cutout-geometry-nodes => archive/2026-09-17-optimize-cutout-geometry-nodes}/.openspec.yaml (100%) rename openspec/changes/{optimize-cutout-geometry-nodes => archive/2026-09-17-optimize-cutout-geometry-nodes}/design.md (100%) rename openspec/changes/{optimize-cutout-geometry-nodes => archive/2026-09-17-optimize-cutout-geometry-nodes}/proposal.md (100%) rename openspec/changes/{optimize-cutout-geometry-nodes => archive/2026-09-17-optimize-cutout-geometry-nodes}/specs/cutout-attribute-lifecycle/spec.md (100%) rename openspec/changes/{optimize-cutout-geometry-nodes => archive/2026-09-17-optimize-cutout-geometry-nodes}/specs/cutout-geometry-node-efficiency/spec.md (100%) rename openspec/changes/{optimize-cutout-geometry-nodes => archive/2026-09-17-optimize-cutout-geometry-nodes}/tasks.md (100%) rename openspec/changes/{organize-tool-cli => archive/2026-09-17-organize-tool-cli}/.openspec.yaml (100%) rename openspec/changes/{organize-tool-cli => archive/2026-09-17-organize-tool-cli}/design.md (100%) rename openspec/changes/{organize-tool-cli => archive/2026-09-17-organize-tool-cli}/proposal.md (100%) rename openspec/changes/{organize-tool-cli => archive/2026-09-17-organize-tool-cli}/specs/developer-tool-cli/spec.md (100%) rename openspec/changes/{organize-tool-cli => archive/2026-09-17-organize-tool-cli}/tasks.md (100%) rename openspec/changes/{pad-cutout-material-edges => archive/2026-09-17-pad-cutout-material-edges}/.openspec.yaml (100%) rename openspec/changes/{pad-cutout-material-edges => archive/2026-09-17-pad-cutout-material-edges}/design.md (100%) rename openspec/changes/{pad-cutout-material-edges => archive/2026-09-17-pad-cutout-material-edges}/proposal.md (100%) rename openspec/changes/{pad-cutout-material-edges => archive/2026-09-17-pad-cutout-material-edges}/specs/cutout-boundary-padding/spec.md (100%) rename openspec/changes/{pad-cutout-material-edges => archive/2026-09-17-pad-cutout-material-edges}/tasks.md (100%) rename openspec/changes/{persist-image-tool-options => archive/2026-09-17-persist-image-tool-options}/.openspec.yaml (100%) rename openspec/changes/{persist-image-tool-options => archive/2026-09-17-persist-image-tool-options}/design.md (100%) rename openspec/changes/{persist-image-tool-options => archive/2026-09-17-persist-image-tool-options}/proposal.md (100%) rename openspec/changes/{persist-image-tool-options => archive/2026-09-17-persist-image-tool-options}/specs/crop-tool-options/spec.md (100%) rename openspec/changes/{persist-image-tool-options => archive/2026-09-17-persist-image-tool-options}/tasks.md (100%) rename openspec/changes/{pin-geometry-node-smoothing => archive/2026-09-17-pin-geometry-node-smoothing}/.openspec.yaml (100%) rename openspec/changes/{pin-geometry-node-smoothing => archive/2026-09-17-pin-geometry-node-smoothing}/design.md (100%) rename openspec/changes/{pin-geometry-node-smoothing => archive/2026-09-17-pin-geometry-node-smoothing}/proposal.md (100%) rename openspec/changes/{pin-geometry-node-smoothing => archive/2026-09-17-pin-geometry-node-smoothing}/specs/pinned-geometry-smoothing/spec.md (100%) rename openspec/changes/{pin-geometry-node-smoothing => archive/2026-09-17-pin-geometry-node-smoothing}/tasks.md (100%) rename openspec/changes/{preserve-frame-bottom-layer-rgb => archive/2026-09-17-preserve-frame-bottom-layer-rgb}/.openspec.yaml (100%) rename openspec/changes/{preserve-frame-bottom-layer-rgb => archive/2026-09-17-preserve-frame-bottom-layer-rgb}/design.md (100%) rename openspec/changes/{preserve-frame-bottom-layer-rgb => archive/2026-09-17-preserve-frame-bottom-layer-rgb}/proposal.md (100%) rename openspec/changes/{preserve-frame-bottom-layer-rgb => archive/2026-09-17-preserve-frame-bottom-layer-rgb}/specs/frame-transparent-rgb/spec.md (100%) rename openspec/changes/{preserve-frame-bottom-layer-rgb => archive/2026-09-17-preserve-frame-bottom-layer-rgb}/tasks.md (100%) rename openspec/changes/{preserve-image-tool-source-on-empty => archive/2026-09-17-preserve-image-tool-source-on-empty}/.openspec.yaml (100%) rename openspec/changes/{preserve-image-tool-source-on-empty => archive/2026-09-17-preserve-image-tool-source-on-empty}/design.md (100%) rename openspec/changes/{preserve-image-tool-source-on-empty => archive/2026-09-17-preserve-image-tool-source-on-empty}/proposal.md (100%) rename openspec/changes/{preserve-image-tool-source-on-empty => archive/2026-09-17-preserve-image-tool-source-on-empty}/specs/image-tool-interaction/spec.md (100%) rename openspec/changes/{preserve-image-tool-source-on-empty => archive/2026-09-17-preserve-image-tool-source-on-empty}/tasks.md (100%) rename openspec/changes/{process-image-selection-once => archive/2026-09-17-process-image-selection-once}/.openspec.yaml (100%) rename openspec/changes/{process-image-selection-once => archive/2026-09-17-process-image-selection-once}/design.md (100%) rename openspec/changes/{process-image-selection-once => archive/2026-09-17-process-image-selection-once}/proposal.md (100%) rename openspec/changes/{process-image-selection-once => archive/2026-09-17-process-image-selection-once}/specs/image-selection-processing/spec.md (100%) rename openspec/changes/{process-image-selection-once => archive/2026-09-17-process-image-selection-once}/tasks.md (100%) rename openspec/changes/{prototype-panorama-surface => archive/2026-09-17-prototype-panorama-surface}/.openspec.yaml (100%) rename openspec/changes/{prototype-panorama-surface => archive/2026-09-17-prototype-panorama-surface}/design.md (100%) rename openspec/changes/{prototype-panorama-surface => archive/2026-09-17-prototype-panorama-surface}/proposal.md (100%) rename openspec/changes/{prototype-panorama-surface => archive/2026-09-17-prototype-panorama-surface}/specs/panorama-surface-prototype/spec.md (100%) rename openspec/changes/{prototype-panorama-surface => archive/2026-09-17-prototype-panorama-surface}/tasks.md (100%) rename openspec/changes/{refine-cutout-split-boundaries => archive/2026-09-17-refine-cutout-split-boundaries}/.openspec.yaml (100%) rename openspec/changes/{refine-cutout-split-boundaries => archive/2026-09-17-refine-cutout-split-boundaries}/design.md (100%) rename openspec/changes/{refine-cutout-split-boundaries => archive/2026-09-17-refine-cutout-split-boundaries}/proposal.md (100%) rename openspec/changes/{refine-cutout-split-boundaries => archive/2026-09-17-refine-cutout-split-boundaries}/specs/cutout-split-boundary-shaping/spec.md (100%) rename openspec/changes/{refine-cutout-split-boundaries => archive/2026-09-17-refine-cutout-split-boundaries}/tasks.md (100%) rename openspec/changes/{refine-cutout-symmetry-seam => archive/2026-09-17-refine-cutout-symmetry-seam}/.openspec.yaml (100%) rename openspec/changes/{refine-cutout-symmetry-seam => archive/2026-09-17-refine-cutout-symmetry-seam}/design.md (100%) rename openspec/changes/{refine-cutout-symmetry-seam => archive/2026-09-17-refine-cutout-symmetry-seam}/proposal.md (100%) rename openspec/changes/{refine-cutout-symmetry-seam => archive/2026-09-17-refine-cutout-symmetry-seam}/specs/cutout-symmetry-seam/spec.md (100%) rename openspec/changes/{refine-cutout-symmetry-seam => archive/2026-09-17-refine-cutout-symmetry-seam}/tasks.md (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/.openspec.yaml (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/design.md (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/proposal.md (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/specs/geometry-depth-controls/spec.md (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/specs/image-result-materials/spec.md (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/specs/selection-content-processing/spec.md (100%) rename openspec/changes/{refine-image-output-semantics => archive/2026-09-17-refine-image-output-semantics}/tasks.md (100%) rename openspec/changes/{release-model-inference-memory => archive/2026-09-17-release-model-inference-memory}/.openspec.yaml (100%) rename openspec/changes/{release-model-inference-memory => archive/2026-09-17-release-model-inference-memory}/design.md (100%) rename openspec/changes/{release-model-inference-memory => archive/2026-09-17-release-model-inference-memory}/proposal.md (100%) rename openspec/changes/{release-model-inference-memory => archive/2026-09-17-release-model-inference-memory}/specs/model-inference-memory/spec.md (100%) rename openspec/changes/{release-model-inference-memory => archive/2026-09-17-release-model-inference-memory}/tasks.md (100%) rename openspec/changes/{remove-debug-workflows => archive/2026-09-17-remove-debug-workflows}/.openspec.yaml (100%) rename openspec/changes/{remove-debug-workflows => archive/2026-09-17-remove-debug-workflows}/design.md (100%) rename openspec/changes/{remove-debug-workflows => archive/2026-09-17-remove-debug-workflows}/proposal.md (100%) rename openspec/changes/{remove-debug-workflows => archive/2026-09-17-remove-debug-workflows}/specs/maintainable-project-structure/spec.md (100%) rename openspec/changes/{remove-debug-workflows => archive/2026-09-17-remove-debug-workflows}/specs/production-only-ai-workflows/spec.md (100%) rename openspec/changes/{remove-debug-workflows => archive/2026-09-17-remove-debug-workflows}/tasks.md (100%) rename openspec/changes/{remove-geometry-capture-attributes => archive/2026-09-17-remove-geometry-capture-attributes}/.openspec.yaml (100%) rename openspec/changes/{remove-geometry-capture-attributes => archive/2026-09-17-remove-geometry-capture-attributes}/design.md (100%) rename openspec/changes/{remove-geometry-capture-attributes => archive/2026-09-17-remove-geometry-capture-attributes}/proposal.md (100%) rename openspec/changes/{remove-geometry-capture-attributes => archive/2026-09-17-remove-geometry-capture-attributes}/specs/geometry-field-flow/spec.md (100%) rename openspec/changes/{remove-geometry-capture-attributes => archive/2026-09-17-remove-geometry-capture-attributes}/tasks.md (100%) rename openspec/changes/{remove-geometry-capture-attributes => archive/2026-09-17-remove-geometry-capture-attributes}/validation.md (100%) rename openspec/changes/{remove-geometry-node-smoothing => archive/2026-09-17-remove-geometry-node-smoothing}/.openspec.yaml (100%) rename openspec/changes/{remove-geometry-node-smoothing => archive/2026-09-17-remove-geometry-node-smoothing}/design.md (100%) rename openspec/changes/{remove-geometry-node-smoothing => archive/2026-09-17-remove-geometry-node-smoothing}/proposal.md (100%) rename openspec/changes/{remove-geometry-node-smoothing => archive/2026-09-17-remove-geometry-node-smoothing}/specs/geometry-node-smoothing/spec.md (100%) rename openspec/changes/{remove-geometry-node-smoothing => archive/2026-09-17-remove-geometry-node-smoothing}/tasks.md (100%) rename openspec/changes/{reorganize-common-modules => archive/2026-09-17-reorganize-common-modules}/.openspec.yaml (100%) rename openspec/changes/{reorganize-common-modules => archive/2026-09-17-reorganize-common-modules}/design.md (100%) rename openspec/changes/{reorganize-common-modules => archive/2026-09-17-reorganize-common-modules}/proposal.md (100%) rename openspec/changes/{reorganize-common-modules => archive/2026-09-17-reorganize-common-modules}/tasks.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/.openspec.yaml (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/design.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/proposal.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/specs/cutout-normal-policy/spec.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/specs/cutout-shape-presets/spec.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/specs/depth-cutout-volume/spec.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/tasks.md (100%) rename openspec/changes/{reorganize-cutout-shapes => archive/2026-09-17-reorganize-cutout-shapes}/validation.md (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/.openspec.yaml (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/design.md (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/proposal.md (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/specs/brush-stroke-processing/spec.md (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/specs/frame-projection-clipping/spec.md (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/specs/image-pixel-read-integrity/spec.md (100%) rename openspec/changes/{repair-image-edit-pipelines => archive/2026-09-17-repair-image-edit-pipelines}/tasks.md (100%) rename openspec/changes/{replace-crop-box-with-frame => archive/2026-09-17-replace-crop-box-with-frame}/.openspec.yaml (100%) rename openspec/changes/{replace-crop-box-with-frame => archive/2026-09-17-replace-crop-box-with-frame}/design.md (100%) rename openspec/changes/{replace-crop-box-with-frame => archive/2026-09-17-replace-crop-box-with-frame}/proposal.md (100%) rename openspec/changes/{replace-crop-box-with-frame => archive/2026-09-17-replace-crop-box-with-frame}/specs/frame-tool/spec.md (100%) rename openspec/changes/{replace-crop-box-with-frame => archive/2026-09-17-replace-crop-box-with-frame}/tasks.md (100%) rename openspec/changes/{replace-crop-with-mask-and-rectify => archive/2026-09-17-replace-crop-with-mask-and-rectify}/.openspec.yaml (100%) rename openspec/changes/{replace-crop-with-mask-and-rectify => archive/2026-09-17-replace-crop-with-mask-and-rectify}/design.md (100%) rename openspec/changes/{replace-crop-with-mask-and-rectify => archive/2026-09-17-replace-crop-with-mask-and-rectify}/proposal.md (100%) rename openspec/changes/{replace-crop-with-mask-and-rectify => archive/2026-09-17-replace-crop-with-mask-and-rectify}/specs/image-edit-tools/spec.md (100%) rename openspec/changes/{replace-crop-with-mask-and-rectify => archive/2026-09-17-replace-crop-with-mask-and-rectify}/tasks.md (100%) rename openspec/changes/{replace-depth-surface-stretch-with-split => archive/2026-09-17-replace-depth-surface-stretch-with-split}/.openspec.yaml (100%) rename openspec/changes/{replace-depth-surface-stretch-with-split => archive/2026-09-17-replace-depth-surface-stretch-with-split}/design.md (100%) rename openspec/changes/{replace-depth-surface-stretch-with-split => archive/2026-09-17-replace-depth-surface-stretch-with-split}/proposal.md (100%) rename openspec/changes/{replace-depth-surface-stretch-with-split => archive/2026-09-17-replace-depth-surface-stretch-with-split}/specs/depth-surface-face-separation/spec.md (100%) rename openspec/changes/{replace-depth-surface-stretch-with-split => archive/2026-09-17-replace-depth-surface-stretch-with-split}/tasks.md (100%) rename openspec/changes/{replace-depth-surface-stretch-with-split => archive/2026-09-17-replace-depth-surface-stretch-with-split}/validation.md (100%) rename openspec/changes/{restore-cutout-depth-extension => archive/2026-09-17-restore-cutout-depth-extension}/.openspec.yaml (100%) rename openspec/changes/{restore-cutout-depth-extension => archive/2026-09-17-restore-cutout-depth-extension}/design.md (100%) rename openspec/changes/{restore-cutout-depth-extension => archive/2026-09-17-restore-cutout-depth-extension}/proposal.md (100%) rename openspec/changes/{restore-cutout-depth-extension => archive/2026-09-17-restore-cutout-depth-extension}/specs/cutout-depth-extension/spec.md (100%) rename openspec/changes/{restore-cutout-depth-extension => archive/2026-09-17-restore-cutout-depth-extension}/tasks.md (100%) rename openspec/changes/{restore-depth-surface-stretch-culling => archive/2026-09-17-restore-depth-surface-stretch-culling}/.openspec.yaml (100%) rename openspec/changes/{restore-depth-surface-stretch-culling => archive/2026-09-17-restore-depth-surface-stretch-culling}/design.md (100%) rename openspec/changes/{restore-depth-surface-stretch-culling => archive/2026-09-17-restore-depth-surface-stretch-culling}/proposal.md (100%) rename openspec/changes/{restore-depth-surface-stretch-culling => archive/2026-09-17-restore-depth-surface-stretch-culling}/specs/depth-surface-stretch-culling/spec.md (100%) rename openspec/changes/{restore-depth-surface-stretch-culling => archive/2026-09-17-restore-depth-surface-stretch-culling}/tasks.md (100%) rename openspec/changes/{reuse-conversion-source-images => archive/2026-09-17-reuse-conversion-source-images}/.openspec.yaml (100%) rename openspec/changes/{reuse-conversion-source-images => archive/2026-09-17-reuse-conversion-source-images}/design.md (100%) rename openspec/changes/{reuse-conversion-source-images => archive/2026-09-17-reuse-conversion-source-images}/proposal.md (100%) rename openspec/changes/{reuse-conversion-source-images => archive/2026-09-17-reuse-conversion-source-images}/specs/conversion-source-images/spec.md (100%) rename openspec/changes/{reuse-conversion-source-images => archive/2026-09-17-reuse-conversion-source-images}/tasks.md (100%) rename openspec/changes/{rework-plane-conversion => archive/2026-09-17-rework-plane-conversion}/.openspec.yaml (100%) rename openspec/changes/{rework-plane-conversion => archive/2026-09-17-rework-plane-conversion}/design.md (100%) rename openspec/changes/{rework-plane-conversion => archive/2026-09-17-rework-plane-conversion}/proposal.md (100%) rename openspec/changes/{rework-plane-conversion => archive/2026-09-17-rework-plane-conversion}/specs/plane-conversion/spec.md (100%) rename openspec/changes/{rework-plane-conversion => archive/2026-09-17-rework-plane-conversion}/tasks.md (100%) rename openspec/changes/{scale-depth-split-by-depth-scale => archive/2026-09-17-scale-depth-split-by-depth-scale}/.openspec.yaml (100%) rename openspec/changes/{scale-depth-split-by-depth-scale => archive/2026-09-17-scale-depth-split-by-depth-scale}/design.md (100%) rename openspec/changes/{scale-depth-split-by-depth-scale => archive/2026-09-17-scale-depth-split-by-depth-scale}/proposal.md (100%) rename openspec/changes/{scale-depth-split-by-depth-scale => archive/2026-09-17-scale-depth-split-by-depth-scale}/specs/depth-split-selection/spec.md (100%) rename openspec/changes/{scale-depth-split-by-depth-scale => archive/2026-09-17-scale-depth-split-by-depth-scale}/tasks.md (100%) rename openspec/changes/{scale-tangent-normals-with-depth => archive/2026-09-17-scale-tangent-normals-with-depth}/.openspec.yaml (100%) rename openspec/changes/{scale-tangent-normals-with-depth => archive/2026-09-17-scale-tangent-normals-with-depth}/design.md (100%) rename openspec/changes/{scale-tangent-normals-with-depth => archive/2026-09-17-scale-tangent-normals-with-depth}/proposal.md (100%) rename openspec/changes/{scale-tangent-normals-with-depth => archive/2026-09-17-scale-tangent-normals-with-depth}/specs/depth-scaled-tangent-normals/spec.md (100%) rename openspec/changes/{scale-tangent-normals-with-depth => archive/2026-09-17-scale-tangent-normals-with-depth}/tasks.md (100%) rename openspec/changes/{separate-depth-alpha-and-model-input => archive/2026-09-17-separate-depth-alpha-and-model-input}/.openspec.yaml (100%) rename openspec/changes/{separate-depth-alpha-and-model-input => archive/2026-09-17-separate-depth-alpha-and-model-input}/design.md (100%) rename openspec/changes/{separate-depth-alpha-and-model-input => archive/2026-09-17-separate-depth-alpha-and-model-input}/proposal.md (100%) rename openspec/changes/{separate-depth-alpha-and-model-input => archive/2026-09-17-separate-depth-alpha-and-model-input}/specs/depth-alpha-semantics/spec.md (100%) rename openspec/changes/{separate-depth-alpha-and-model-input => archive/2026-09-17-separate-depth-alpha-and-model-input}/tasks.md (100%) rename openspec/changes/{separate-panorama-mask-regions => archive/2026-09-17-separate-panorama-mask-regions}/.openspec.yaml (100%) rename openspec/changes/{separate-panorama-mask-regions => archive/2026-09-17-separate-panorama-mask-regions}/design.md (100%) rename openspec/changes/{separate-panorama-mask-regions => archive/2026-09-17-separate-panorama-mask-regions}/proposal.md (100%) rename openspec/changes/{separate-panorama-mask-regions => archive/2026-09-17-separate-panorama-mask-regions}/specs/panorama-mask-regions/spec.md (100%) rename openspec/changes/{separate-panorama-mask-regions => archive/2026-09-17-separate-panorama-mask-regions}/tasks.md (100%) rename openspec/changes/{separate-selection-from-image-content => archive/2026-09-17-separate-selection-from-image-content}/.openspec.yaml (100%) rename openspec/changes/{separate-selection-from-image-content => archive/2026-09-17-separate-selection-from-image-content}/design.md (100%) rename openspec/changes/{separate-selection-from-image-content => archive/2026-09-17-separate-selection-from-image-content}/proposal.md (100%) rename openspec/changes/{separate-selection-from-image-content => archive/2026-09-17-separate-selection-from-image-content}/specs/selection-content-processing/spec.md (100%) rename openspec/changes/{separate-selection-from-image-content => archive/2026-09-17-separate-selection-from-image-content}/tasks.md (100%) rename openspec/changes/{show-server-controls-while-running => archive/2026-09-17-show-server-controls-while-running}/.openspec.yaml (100%) rename openspec/changes/{show-server-controls-while-running => archive/2026-09-17-show-server-controls-while-running}/design.md (100%) rename openspec/changes/{show-server-controls-while-running => archive/2026-09-17-show-server-controls-while-running}/proposal.md (100%) rename openspec/changes/{show-server-controls-while-running => archive/2026-09-17-show-server-controls-while-running}/specs/server-cache-display/spec.md (100%) rename openspec/changes/{show-server-controls-while-running => archive/2026-09-17-show-server-controls-while-running}/specs/server-lifecycle-controls/spec.md (100%) rename openspec/changes/{show-server-controls-while-running => archive/2026-09-17-show-server-controls-while-running}/tasks.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/.openspec.yaml (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/design.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/mirror-validation.json (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/proposal.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/retirement-results.json (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/retirement.json (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/specs/ai-model-tiers/spec.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/specs/background-removal-models/spec.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/specs/hat-upscale/spec.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/specs/model-asset-retirement/spec.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/tasks.md (100%) rename openspec/changes/{simplify-ai-model-tiers => archive/2026-09-17-simplify-ai-model-tiers}/validation.md (100%) rename openspec/changes/{simplify-depth-group-options => archive/2026-09-17-simplify-depth-group-options}/.openspec.yaml (100%) rename openspec/changes/{simplify-depth-group-options => archive/2026-09-17-simplify-depth-group-options}/design.md (100%) rename openspec/changes/{simplify-depth-group-options => archive/2026-09-17-simplify-depth-group-options}/proposal.md (100%) rename openspec/changes/{simplify-depth-group-options => archive/2026-09-17-simplify-depth-group-options}/specs/depth-node-options/spec.md (100%) rename openspec/changes/{simplify-depth-group-options => archive/2026-09-17-simplify-depth-group-options}/tasks.md (100%) rename openspec/changes/{simplify-node-attribute-flow => archive/2026-09-17-simplify-node-attribute-flow}/.openspec.yaml (100%) rename openspec/changes/{simplify-node-attribute-flow => archive/2026-09-17-simplify-node-attribute-flow}/design.md (100%) rename openspec/changes/{simplify-node-attribute-flow => archive/2026-09-17-simplify-node-attribute-flow}/proposal.md (100%) rename openspec/changes/{simplify-node-attribute-flow => archive/2026-09-17-simplify-node-attribute-flow}/tasks.md (100%) rename openspec/changes/{simplify-node-attribute-flow => archive/2026-09-17-simplify-node-attribute-flow}/validation.md (100%) rename openspec/changes/{simplify-selection-rasterization => archive/2026-09-17-simplify-selection-rasterization}/.openspec.yaml (100%) rename openspec/changes/{simplify-selection-rasterization => archive/2026-09-17-simplify-selection-rasterization}/design.md (100%) rename openspec/changes/{simplify-selection-rasterization => archive/2026-09-17-simplify-selection-rasterization}/proposal.md (100%) rename openspec/changes/{simplify-selection-rasterization => archive/2026-09-17-simplify-selection-rasterization}/specs/selection-rasterization/spec.md (100%) rename openspec/changes/{simplify-selection-rasterization => archive/2026-09-17-simplify-selection-rasterization}/tasks.md (100%) rename openspec/changes/{split-depth-and-relief-planes => archive/2026-09-17-split-depth-and-relief-planes}/.openspec.yaml (100%) rename openspec/changes/{split-depth-and-relief-planes => archive/2026-09-17-split-depth-and-relief-planes}/design.md (100%) rename openspec/changes/{split-depth-and-relief-planes => archive/2026-09-17-split-depth-and-relief-planes}/proposal.md (100%) rename openspec/changes/{split-depth-and-relief-planes => archive/2026-09-17-split-depth-and-relief-planes}/specs/depth-plane-alpha-culling/spec.md (100%) rename openspec/changes/{split-depth-and-relief-planes => archive/2026-09-17-split-depth-and-relief-planes}/specs/separate-depth-relief-planes/spec.md (100%) rename openspec/changes/{split-depth-and-relief-planes => archive/2026-09-17-split-depth-and-relief-planes}/tasks.md (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/.openspec.yaml (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/design.md (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/proposal.md (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/specs/canonical-object-frame/spec.md (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/specs/depth-symmetry-orientation/spec.md (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/specs/object-space-normal-alignment/spec.md (100%) rename openspec/changes/{standardize-blender-object-coordinates => archive/2026-09-17-standardize-blender-object-coordinates}/tasks.md (100%) rename openspec/changes/{standardize-cutout-z-axis => archive/2026-09-17-standardize-cutout-z-axis}/.openspec.yaml (100%) rename openspec/changes/{standardize-cutout-z-axis => archive/2026-09-17-standardize-cutout-z-axis}/design.md (100%) rename openspec/changes/{standardize-cutout-z-axis => archive/2026-09-17-standardize-cutout-z-axis}/proposal.md (100%) rename openspec/changes/{standardize-cutout-z-axis => archive/2026-09-17-standardize-cutout-z-axis}/specs/cutout-axis-convention/spec.md (100%) rename openspec/changes/{standardize-cutout-z-axis => archive/2026-09-17-standardize-cutout-z-axis}/tasks.md (100%) rename openspec/changes/{support-frame-view-plane-intersections => archive/2026-09-17-support-frame-view-plane-intersections}/.openspec.yaml (100%) rename openspec/changes/{support-frame-view-plane-intersections => archive/2026-09-17-support-frame-view-plane-intersections}/design.md (100%) rename openspec/changes/{support-frame-view-plane-intersections => archive/2026-09-17-support-frame-view-plane-intersections}/proposal.md (100%) rename openspec/changes/{support-frame-view-plane-intersections => archive/2026-09-17-support-frame-view-plane-intersections}/specs/frame-tool/spec.md (100%) rename openspec/changes/{support-frame-view-plane-intersections => archive/2026-09-17-support-frame-view-plane-intersections}/tasks.md (100%) rename openspec/changes/{unify-ai-input-validation => archive/2026-09-17-unify-ai-input-validation}/.openspec.yaml (100%) rename openspec/changes/{unify-ai-input-validation => archive/2026-09-17-unify-ai-input-validation}/design.md (100%) rename openspec/changes/{unify-ai-input-validation => archive/2026-09-17-unify-ai-input-validation}/proposal.md (100%) rename openspec/changes/{unify-ai-input-validation => archive/2026-09-17-unify-ai-input-validation}/specs/ai-input-validation/spec.md (100%) rename openspec/changes/{unify-ai-input-validation => archive/2026-09-17-unify-ai-input-validation}/specs/ai-operator-failure-handling/spec.md (100%) rename openspec/changes/{unify-ai-input-validation => archive/2026-09-17-unify-ai-input-validation}/tasks.md (100%) rename openspec/changes/{unify-boundary-depth-smoothing => archive/2026-09-17-unify-boundary-depth-smoothing}/.openspec.yaml (100%) rename openspec/changes/{unify-boundary-depth-smoothing => archive/2026-09-17-unify-boundary-depth-smoothing}/design.md (100%) rename openspec/changes/{unify-boundary-depth-smoothing => archive/2026-09-17-unify-boundary-depth-smoothing}/proposal.md (100%) rename openspec/changes/{unify-boundary-depth-smoothing => archive/2026-09-17-unify-boundary-depth-smoothing}/specs/boundary-depth-smoothing/spec.md (100%) rename openspec/changes/{unify-boundary-depth-smoothing => archive/2026-09-17-unify-boundary-depth-smoothing}/tasks.md (100%) rename openspec/changes/{unify-depth-plane-and-artifact => archive/2026-09-17-unify-depth-plane-and-artifact}/.openspec.yaml (100%) rename openspec/changes/{unify-depth-plane-and-artifact => archive/2026-09-17-unify-depth-plane-and-artifact}/design.md (100%) rename openspec/changes/{unify-depth-plane-and-artifact => archive/2026-09-17-unify-depth-plane-and-artifact}/proposal.md (100%) rename openspec/changes/{unify-depth-plane-and-artifact => archive/2026-09-17-unify-depth-plane-and-artifact}/specs/depth-artifact-contract/spec.md (100%) rename openspec/changes/{unify-depth-plane-and-artifact => archive/2026-09-17-unify-depth-plane-and-artifact}/specs/unified-depth-plane/spec.md (100%) rename openspec/changes/{unify-depth-plane-and-artifact => archive/2026-09-17-unify-depth-plane-and-artifact}/tasks.md (100%) rename openspec/changes/{unify-mask-cut-depth => archive/2026-09-17-unify-mask-cut-depth}/.openspec.yaml (100%) rename openspec/changes/{unify-mask-cut-depth => archive/2026-09-17-unify-mask-cut-depth}/design.md (100%) rename openspec/changes/{unify-mask-cut-depth => archive/2026-09-17-unify-mask-cut-depth}/proposal.md (100%) rename openspec/changes/{unify-mask-cut-depth => archive/2026-09-17-unify-mask-cut-depth}/specs/mask-cut-depth-sampling/spec.md (100%) rename openspec/changes/{unify-mask-cut-depth => archive/2026-09-17-unify-mask-cut-depth}/tasks.md (100%) rename openspec/changes/{unify-material-color-images => archive/2026-09-17-unify-material-color-images}/.openspec.yaml (100%) rename openspec/changes/{unify-material-color-images => archive/2026-09-17-unify-material-color-images}/design.md (100%) rename openspec/changes/{unify-material-color-images => archive/2026-09-17-unify-material-color-images}/proposal.md (100%) rename openspec/changes/{unify-material-color-images => archive/2026-09-17-unify-material-color-images}/specs/clipboard-image-cache/spec.md (100%) rename openspec/changes/{unify-material-color-images => archive/2026-09-17-unify-material-color-images}/specs/material-color-images/spec.md (100%) rename openspec/changes/{unify-material-color-images => archive/2026-09-17-unify-material-color-images}/tasks.md (100%) rename openspec/changes/{unify-reference-depth => archive/2026-09-17-unify-reference-depth}/.openspec.yaml (100%) rename openspec/changes/{unify-reference-depth => archive/2026-09-17-unify-reference-depth}/design.md (100%) rename openspec/changes/{unify-reference-depth => archive/2026-09-17-unify-reference-depth}/proposal.md (100%) rename openspec/changes/{unify-reference-depth => archive/2026-09-17-unify-reference-depth}/specs/reference-depth-calibration/spec.md (100%) rename openspec/changes/{unify-reference-depth => archive/2026-09-17-unify-reference-depth}/tasks.md (100%) rename openspec/changes/{unify-selection-terminology => archive/2026-09-17-unify-selection-terminology}/.openspec.yaml (100%) rename openspec/changes/{unify-selection-terminology => archive/2026-09-17-unify-selection-terminology}/design.md (100%) rename openspec/changes/{unify-selection-terminology => archive/2026-09-17-unify-selection-terminology}/proposal.md (100%) rename openspec/changes/{unify-selection-terminology => archive/2026-09-17-unify-selection-terminology}/specs/image-selection/spec.md (100%) rename openspec/changes/{unify-selection-terminology => archive/2026-09-17-unify-selection-terminology}/tasks.md (100%) rename openspec/changes/{update-pasted-brush-preview => archive/2026-09-17-update-pasted-brush-preview}/.openspec.yaml (100%) rename openspec/changes/{update-pasted-brush-preview => archive/2026-09-17-update-pasted-brush-preview}/design.md (100%) rename openspec/changes/{update-pasted-brush-preview => archive/2026-09-17-update-pasted-brush-preview}/proposal.md (100%) rename openspec/changes/{update-pasted-brush-preview => archive/2026-09-17-update-pasted-brush-preview}/specs/pasted-brush-preview/spec.md (100%) rename openspec/changes/{update-pasted-brush-preview => archive/2026-09-17-update-pasted-brush-preview}/tasks.md (100%) rename openspec/changes/{use-tangle-cutout-meshing => archive/2026-09-17-use-tangle-cutout-meshing}/.openspec.yaml (100%) rename openspec/changes/{use-tangle-cutout-meshing => archive/2026-09-17-use-tangle-cutout-meshing}/design.md (100%) rename openspec/changes/{use-tangle-cutout-meshing => archive/2026-09-17-use-tangle-cutout-meshing}/proposal.md (100%) rename openspec/changes/{use-tangle-cutout-meshing => archive/2026-09-17-use-tangle-cutout-meshing}/specs/cutout-quality-meshing/spec.md (100%) rename openspec/changes/{use-tangle-cutout-meshing => archive/2026-09-17-use-tangle-cutout-meshing}/tasks.md (100%) rename openspec/changes/{use-world-space-cutout-edge-length => archive/2026-09-17-use-world-space-cutout-edge-length}/.openspec.yaml (100%) rename openspec/changes/{use-world-space-cutout-edge-length => archive/2026-09-17-use-world-space-cutout-edge-length}/design.md (100%) rename openspec/changes/{use-world-space-cutout-edge-length => archive/2026-09-17-use-world-space-cutout-edge-length}/proposal.md (100%) rename openspec/changes/{use-world-space-cutout-edge-length => archive/2026-09-17-use-world-space-cutout-edge-length}/specs/cutout-edge-length/spec.md (100%) rename openspec/changes/{use-world-space-cutout-edge-length => archive/2026-09-17-use-world-space-cutout-edge-length}/tasks.md (100%) rename openspec/changes/{weight-cutout-boundary-smoothing => archive/2026-09-17-weight-cutout-boundary-smoothing}/.openspec.yaml (100%) rename openspec/changes/{weight-cutout-boundary-smoothing => archive/2026-09-17-weight-cutout-boundary-smoothing}/design.md (100%) rename openspec/changes/{weight-cutout-boundary-smoothing => archive/2026-09-17-weight-cutout-boundary-smoothing}/proposal.md (100%) rename openspec/changes/{weight-cutout-boundary-smoothing => archive/2026-09-17-weight-cutout-boundary-smoothing}/specs/cutout-boundary-smoothing/spec.md (100%) rename openspec/changes/{weight-cutout-boundary-smoothing => archive/2026-09-17-weight-cutout-boundary-smoothing}/tasks.md (100%) create mode 100644 openspec/specs/ai-input-validation/spec.md create mode 100644 openspec/specs/ai-model-tiers/spec.md create mode 100644 openspec/specs/ai-operator-failure-handling/spec.md create mode 100644 openspec/specs/background-removal-models/spec.md create mode 100644 openspec/specs/boundary-depth-smoothing/spec.md create mode 100644 openspec/specs/brush-stroke-processing/spec.md create mode 100644 openspec/specs/canonical-object-frame/spec.md create mode 100644 openspec/specs/clipboard-image-cache/spec.md create mode 100644 openspec/specs/compact-repeat-arrangement/spec.md create mode 100644 openspec/specs/conversion-source-images/spec.md create mode 100644 openspec/specs/coreml-inference/spec.md create mode 100644 openspec/specs/crop-tool-options/spec.md create mode 100644 openspec/specs/cutout-adaptive-mesh/spec.md create mode 100644 openspec/specs/cutout-attribute-lifecycle/spec.md create mode 100644 openspec/specs/cutout-axis-convention/spec.md create mode 100644 openspec/specs/cutout-boundary-padding/spec.md create mode 100644 openspec/specs/cutout-boundary-smoothing/spec.md create mode 100644 openspec/specs/cutout-depth-extension/spec.md create mode 100644 openspec/specs/cutout-edge-depth/spec.md create mode 100644 openspec/specs/cutout-edge-length/spec.md create mode 100644 openspec/specs/cutout-geometry-node-efficiency/spec.md create mode 100644 openspec/specs/cutout-lasso-entry/spec.md create mode 100644 openspec/specs/cutout-mesh-sampling/spec.md create mode 100644 openspec/specs/cutout-normal-policy/spec.md create mode 100644 openspec/specs/cutout-polyline/spec.md create mode 100644 openspec/specs/cutout-quality-meshing/spec.md create mode 100644 openspec/specs/cutout-shape-presets/spec.md create mode 100644 openspec/specs/cutout-split-boundary-shaping/spec.md create mode 100644 openspec/specs/cutout-symmetry-seam/spec.md create mode 100644 openspec/specs/depth-alpha-semantics/spec.md create mode 100644 openspec/specs/depth-artifact-contract/spec.md create mode 100644 openspec/specs/depth-cutout-volume/spec.md create mode 100644 openspec/specs/depth-node-options/spec.md create mode 100644 openspec/specs/depth-plane-alpha-culling/spec.md create mode 100644 openspec/specs/depth-scaled-tangent-normals/spec.md create mode 100644 openspec/specs/depth-split-selection/spec.md create mode 100644 openspec/specs/depth-surface-face-separation/spec.md create mode 100644 openspec/specs/depth-surface-stretch-culling/spec.md create mode 100644 openspec/specs/depth-surface-strip-cleanup/spec.md create mode 100644 openspec/specs/depth-symmetry-orientation/spec.md create mode 100644 openspec/specs/developer-tool-cli/spec.md create mode 100644 openspec/specs/empty-image-edit-isolation/spec.md create mode 100644 openspec/specs/frame-edge-antialiasing/spec.md create mode 100644 openspec/specs/frame-projection-clipping/spec.md create mode 100644 openspec/specs/frame-tool/spec.md create mode 100644 openspec/specs/frame-transparent-rgb/spec.md create mode 100644 openspec/specs/geometry-depth-controls/spec.md create mode 100644 openspec/specs/geometry-field-flow/spec.md create mode 100644 openspec/specs/geometry-node-smoothing/spec.md create mode 100644 openspec/specs/hat-upscale/spec.md create mode 100644 openspec/specs/hdr-background-removal/spec.md create mode 100644 openspec/specs/image-alpha-presentation/spec.md create mode 100644 openspec/specs/image-depth-symmetry/spec.md create mode 100644 openspec/specs/image-edit-results/spec.md create mode 100644 openspec/specs/image-edit-tools/spec.md create mode 100644 openspec/specs/image-layer-alpha-adjustment/spec.md create mode 100644 openspec/specs/image-pixel-read-integrity/spec.md create mode 100644 openspec/specs/image-result-materials/spec.md create mode 100644 openspec/specs/image-selection-processing/spec.md create mode 100644 openspec/specs/image-selection/spec.md create mode 100644 openspec/specs/image-tool-ai-availability/spec.md create mode 100644 openspec/specs/image-tool-interaction/spec.md create mode 100644 openspec/specs/joint-geometry-node-layout/spec.md create mode 100644 openspec/specs/maintainable-project-structure/spec.md create mode 100644 openspec/specs/mask-cut-depth-sampling/spec.md create mode 100644 openspec/specs/mask-editing/spec.md create mode 100644 openspec/specs/mask-mode-options/spec.md create mode 100644 openspec/specs/material-color-images/spec.md create mode 100644 openspec/specs/material-view-adaptation/spec.md create mode 100644 openspec/specs/model-asset-retirement/spec.md create mode 100644 openspec/specs/model-inference-memory/spec.md create mode 100644 openspec/specs/moge-depth-artifacts/spec.md create mode 100644 openspec/specs/moge3-model/spec.md create mode 100644 openspec/specs/object-space-normal-alignment/spec.md create mode 100644 openspec/specs/panorama-conversion/spec.md create mode 100644 openspec/specs/panorama-geometry-generation/spec.md create mode 100644 openspec/specs/panorama-input-cropping/spec.md create mode 100644 openspec/specs/panorama-mask-regions/spec.md create mode 100644 openspec/specs/panorama-surface-prototype/spec.md create mode 100644 openspec/specs/panorama-surface/spec.md create mode 100644 openspec/specs/pasted-brush-preview/spec.md create mode 100644 openspec/specs/pinned-geometry-smoothing/spec.md create mode 100644 openspec/specs/plane-conversion/spec.md create mode 100644 openspec/specs/polyline-gesture-interaction/spec.md create mode 100644 openspec/specs/post-cutout-symmetry/spec.md create mode 100644 openspec/specs/production-only-ai-workflows/spec.md create mode 100644 openspec/specs/projected-depth-surface/spec.md create mode 100644 openspec/specs/reference-depth-calibration/spec.md create mode 100644 openspec/specs/relief-plane-depth-controls/spec.md create mode 100644 openspec/specs/selection-content-processing/spec.md create mode 100644 openspec/specs/selection-rasterization/spec.md create mode 100644 openspec/specs/separate-depth-relief-planes/spec.md create mode 100644 openspec/specs/server-cache-display/spec.md create mode 100644 openspec/specs/server-lifecycle-controls/spec.md create mode 100644 openspec/specs/shell-normal-smoothing/spec.md create mode 100644 openspec/specs/texture-node-image-actions/spec.md create mode 100644 openspec/specs/two-times-upscale/spec.md create mode 100644 openspec/specs/unified-depth-plane/spec.md diff --git a/openspec/changes/add-ben2-hdr-support/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-ben2-hdr-support/.openspec.yaml similarity index 100% rename from openspec/changes/add-ben2-hdr-support/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-ben2-hdr-support/.openspec.yaml diff --git a/openspec/changes/add-ben2-hdr-support/design.md b/openspec/changes/archive/2026-09-17-add-ben2-hdr-support/design.md similarity index 100% rename from openspec/changes/add-ben2-hdr-support/design.md rename to openspec/changes/archive/2026-09-17-add-ben2-hdr-support/design.md diff --git a/openspec/changes/add-ben2-hdr-support/proposal.md b/openspec/changes/archive/2026-09-17-add-ben2-hdr-support/proposal.md similarity index 100% rename from openspec/changes/add-ben2-hdr-support/proposal.md rename to openspec/changes/archive/2026-09-17-add-ben2-hdr-support/proposal.md diff --git a/openspec/changes/add-ben2-hdr-support/specs/hdr-background-removal/spec.md b/openspec/changes/archive/2026-09-17-add-ben2-hdr-support/specs/hdr-background-removal/spec.md similarity index 100% rename from openspec/changes/add-ben2-hdr-support/specs/hdr-background-removal/spec.md rename to openspec/changes/archive/2026-09-17-add-ben2-hdr-support/specs/hdr-background-removal/spec.md diff --git a/openspec/changes/add-ben2-hdr-support/tasks.md b/openspec/changes/archive/2026-09-17-add-ben2-hdr-support/tasks.md similarity index 100% rename from openspec/changes/add-ben2-hdr-support/tasks.md rename to openspec/changes/archive/2026-09-17-add-ben2-hdr-support/tasks.md diff --git a/openspec/changes/add-ben2-hdr-support/validation.md b/openspec/changes/archive/2026-09-17-add-ben2-hdr-support/validation.md similarity index 100% rename from openspec/changes/add-ben2-hdr-support/validation.md rename to openspec/changes/archive/2026-09-17-add-ben2-hdr-support/validation.md diff --git a/openspec/changes/add-cutout-polyline/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-cutout-polyline/.openspec.yaml similarity index 100% rename from openspec/changes/add-cutout-polyline/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-cutout-polyline/.openspec.yaml diff --git a/openspec/changes/add-cutout-polyline/design.md b/openspec/changes/archive/2026-09-17-add-cutout-polyline/design.md similarity index 100% rename from openspec/changes/add-cutout-polyline/design.md rename to openspec/changes/archive/2026-09-17-add-cutout-polyline/design.md diff --git a/openspec/changes/add-cutout-polyline/proposal.md b/openspec/changes/archive/2026-09-17-add-cutout-polyline/proposal.md similarity index 100% rename from openspec/changes/add-cutout-polyline/proposal.md rename to openspec/changes/archive/2026-09-17-add-cutout-polyline/proposal.md diff --git a/openspec/changes/add-cutout-polyline/specs/cutout-polyline/spec.md b/openspec/changes/archive/2026-09-17-add-cutout-polyline/specs/cutout-polyline/spec.md similarity index 100% rename from openspec/changes/add-cutout-polyline/specs/cutout-polyline/spec.md rename to openspec/changes/archive/2026-09-17-add-cutout-polyline/specs/cutout-polyline/spec.md diff --git a/openspec/changes/add-cutout-polyline/specs/mask-mode-options/spec.md b/openspec/changes/archive/2026-09-17-add-cutout-polyline/specs/mask-mode-options/spec.md similarity index 100% rename from openspec/changes/add-cutout-polyline/specs/mask-mode-options/spec.md rename to openspec/changes/archive/2026-09-17-add-cutout-polyline/specs/mask-mode-options/spec.md diff --git a/openspec/changes/add-cutout-polyline/tasks.md b/openspec/changes/archive/2026-09-17-add-cutout-polyline/tasks.md similarity index 100% rename from openspec/changes/add-cutout-polyline/tasks.md rename to openspec/changes/archive/2026-09-17-add-cutout-polyline/tasks.md diff --git a/openspec/changes/add-image-layer-alpha-adjustment/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/.openspec.yaml similarity index 100% rename from openspec/changes/add-image-layer-alpha-adjustment/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/.openspec.yaml diff --git a/openspec/changes/add-image-layer-alpha-adjustment/design.md b/openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/design.md similarity index 100% rename from openspec/changes/add-image-layer-alpha-adjustment/design.md rename to openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/design.md diff --git a/openspec/changes/add-image-layer-alpha-adjustment/proposal.md b/openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/proposal.md similarity index 100% rename from openspec/changes/add-image-layer-alpha-adjustment/proposal.md rename to openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/proposal.md diff --git a/openspec/changes/add-image-layer-alpha-adjustment/specs/image-layer-alpha-adjustment/spec.md b/openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/specs/image-layer-alpha-adjustment/spec.md similarity index 100% rename from openspec/changes/add-image-layer-alpha-adjustment/specs/image-layer-alpha-adjustment/spec.md rename to openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/specs/image-layer-alpha-adjustment/spec.md diff --git a/openspec/changes/add-image-layer-alpha-adjustment/tasks.md b/openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/tasks.md similarity index 100% rename from openspec/changes/add-image-layer-alpha-adjustment/tasks.md rename to openspec/changes/archive/2026-09-17-add-image-layer-alpha-adjustment/tasks.md diff --git a/openspec/changes/add-live-mask-preview-and-polyline/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/.openspec.yaml similarity index 100% rename from openspec/changes/add-live-mask-preview-and-polyline/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/.openspec.yaml diff --git a/openspec/changes/add-live-mask-preview-and-polyline/design.md b/openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/design.md similarity index 100% rename from openspec/changes/add-live-mask-preview-and-polyline/design.md rename to openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/design.md diff --git a/openspec/changes/add-live-mask-preview-and-polyline/proposal.md b/openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/proposal.md similarity index 100% rename from openspec/changes/add-live-mask-preview-and-polyline/proposal.md rename to openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/proposal.md diff --git a/openspec/changes/add-live-mask-preview-and-polyline/specs/mask-editing/spec.md b/openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/specs/mask-editing/spec.md similarity index 100% rename from openspec/changes/add-live-mask-preview-and-polyline/specs/mask-editing/spec.md rename to openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/specs/mask-editing/spec.md diff --git a/openspec/changes/add-live-mask-preview-and-polyline/tasks.md b/openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/tasks.md similarity index 100% rename from openspec/changes/add-live-mask-preview-and-polyline/tasks.md rename to openspec/changes/archive/2026-09-17-add-live-mask-preview-and-polyline/tasks.md diff --git a/openspec/changes/add-moge3-model/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-moge3-model/.openspec.yaml similarity index 100% rename from openspec/changes/add-moge3-model/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-moge3-model/.openspec.yaml diff --git a/openspec/changes/add-moge3-model/design.md b/openspec/changes/archive/2026-09-17-add-moge3-model/design.md similarity index 100% rename from openspec/changes/add-moge3-model/design.md rename to openspec/changes/archive/2026-09-17-add-moge3-model/design.md diff --git a/openspec/changes/add-moge3-model/proposal.md b/openspec/changes/archive/2026-09-17-add-moge3-model/proposal.md similarity index 100% rename from openspec/changes/add-moge3-model/proposal.md rename to openspec/changes/archive/2026-09-17-add-moge3-model/proposal.md diff --git a/openspec/changes/add-moge3-model/specs/moge3-model/spec.md b/openspec/changes/archive/2026-09-17-add-moge3-model/specs/moge3-model/spec.md similarity index 100% rename from openspec/changes/add-moge3-model/specs/moge3-model/spec.md rename to openspec/changes/archive/2026-09-17-add-moge3-model/specs/moge3-model/spec.md diff --git a/openspec/changes/add-moge3-model/tasks.md b/openspec/changes/archive/2026-09-17-add-moge3-model/tasks.md similarity index 100% rename from openspec/changes/add-moge3-model/tasks.md rename to openspec/changes/archive/2026-09-17-add-moge3-model/tasks.md diff --git a/openspec/changes/add-moge3-model/validation.md b/openspec/changes/archive/2026-09-17-add-moge3-model/validation.md similarity index 100% rename from openspec/changes/add-moge3-model/validation.md rename to openspec/changes/archive/2026-09-17-add-moge3-model/validation.md diff --git a/openspec/changes/add-panorama-conversion/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-panorama-conversion/.openspec.yaml similarity index 100% rename from openspec/changes/add-panorama-conversion/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/.openspec.yaml diff --git a/openspec/changes/add-panorama-conversion/design.md b/openspec/changes/archive/2026-09-17-add-panorama-conversion/design.md similarity index 100% rename from openspec/changes/add-panorama-conversion/design.md rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/design.md diff --git a/openspec/changes/add-panorama-conversion/proposal.md b/openspec/changes/archive/2026-09-17-add-panorama-conversion/proposal.md similarity index 100% rename from openspec/changes/add-panorama-conversion/proposal.md rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/proposal.md diff --git a/openspec/changes/add-panorama-conversion/specs/panorama-conversion/spec.md b/openspec/changes/archive/2026-09-17-add-panorama-conversion/specs/panorama-conversion/spec.md similarity index 100% rename from openspec/changes/add-panorama-conversion/specs/panorama-conversion/spec.md rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/specs/panorama-conversion/spec.md diff --git a/openspec/changes/add-panorama-conversion/specs/panorama-geometry-generation/spec.md b/openspec/changes/archive/2026-09-17-add-panorama-conversion/specs/panorama-geometry-generation/spec.md similarity index 100% rename from openspec/changes/add-panorama-conversion/specs/panorama-geometry-generation/spec.md rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/specs/panorama-geometry-generation/spec.md diff --git a/openspec/changes/add-panorama-conversion/specs/panorama-surface/spec.md b/openspec/changes/archive/2026-09-17-add-panorama-conversion/specs/panorama-surface/spec.md similarity index 100% rename from openspec/changes/add-panorama-conversion/specs/panorama-surface/spec.md rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/specs/panorama-surface/spec.md diff --git a/openspec/changes/add-panorama-conversion/tasks.md b/openspec/changes/archive/2026-09-17-add-panorama-conversion/tasks.md similarity index 100% rename from openspec/changes/add-panorama-conversion/tasks.md rename to openspec/changes/archive/2026-09-17-add-panorama-conversion/tasks.md diff --git a/openspec/changes/add-pisa-sr-two-times-upscale/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/.openspec.yaml similarity index 100% rename from openspec/changes/add-pisa-sr-two-times-upscale/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/.openspec.yaml diff --git a/openspec/changes/add-pisa-sr-two-times-upscale/design.md b/openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/design.md similarity index 100% rename from openspec/changes/add-pisa-sr-two-times-upscale/design.md rename to openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/design.md diff --git a/openspec/changes/add-pisa-sr-two-times-upscale/proposal.md b/openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/proposal.md similarity index 100% rename from openspec/changes/add-pisa-sr-two-times-upscale/proposal.md rename to openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/proposal.md diff --git a/openspec/changes/add-pisa-sr-two-times-upscale/specs/two-times-upscale/spec.md b/openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/specs/two-times-upscale/spec.md similarity index 100% rename from openspec/changes/add-pisa-sr-two-times-upscale/specs/two-times-upscale/spec.md rename to openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/specs/two-times-upscale/spec.md diff --git a/openspec/changes/add-pisa-sr-two-times-upscale/tasks.md b/openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/tasks.md similarity index 100% rename from openspec/changes/add-pisa-sr-two-times-upscale/tasks.md rename to openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/tasks.md diff --git a/openspec/changes/add-pisa-sr-two-times-upscale/validation.md b/openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/validation.md similarity index 100% rename from openspec/changes/add-pisa-sr-two-times-upscale/validation.md rename to openspec/changes/archive/2026-09-17-add-pisa-sr-two-times-upscale/validation.md diff --git a/openspec/changes/add-projected-depth-plane/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-projected-depth-plane/.openspec.yaml similarity index 100% rename from openspec/changes/add-projected-depth-plane/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-projected-depth-plane/.openspec.yaml diff --git a/openspec/changes/add-projected-depth-plane/design.md b/openspec/changes/archive/2026-09-17-add-projected-depth-plane/design.md similarity index 100% rename from openspec/changes/add-projected-depth-plane/design.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-plane/design.md diff --git a/openspec/changes/add-projected-depth-plane/proposal.md b/openspec/changes/archive/2026-09-17-add-projected-depth-plane/proposal.md similarity index 100% rename from openspec/changes/add-projected-depth-plane/proposal.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-plane/proposal.md diff --git a/openspec/changes/add-projected-depth-plane/specs/cutout-lasso-entry/spec.md b/openspec/changes/archive/2026-09-17-add-projected-depth-plane/specs/cutout-lasso-entry/spec.md similarity index 100% rename from openspec/changes/add-projected-depth-plane/specs/cutout-lasso-entry/spec.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-plane/specs/cutout-lasso-entry/spec.md diff --git a/openspec/changes/add-projected-depth-plane/specs/projected-depth-surface/spec.md b/openspec/changes/archive/2026-09-17-add-projected-depth-plane/specs/projected-depth-surface/spec.md similarity index 100% rename from openspec/changes/add-projected-depth-plane/specs/projected-depth-surface/spec.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-plane/specs/projected-depth-surface/spec.md diff --git a/openspec/changes/add-projected-depth-plane/tasks.md b/openspec/changes/archive/2026-09-17-add-projected-depth-plane/tasks.md similarity index 100% rename from openspec/changes/add-projected-depth-plane/tasks.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-plane/tasks.md diff --git a/openspec/changes/add-projected-depth-symmetry/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/.openspec.yaml similarity index 100% rename from openspec/changes/add-projected-depth-symmetry/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/.openspec.yaml diff --git a/openspec/changes/add-projected-depth-symmetry/design.md b/openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/design.md similarity index 100% rename from openspec/changes/add-projected-depth-symmetry/design.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/design.md diff --git a/openspec/changes/add-projected-depth-symmetry/proposal.md b/openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/proposal.md similarity index 100% rename from openspec/changes/add-projected-depth-symmetry/proposal.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/proposal.md diff --git a/openspec/changes/add-projected-depth-symmetry/specs/image-depth-symmetry/spec.md b/openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/specs/image-depth-symmetry/spec.md similarity index 100% rename from openspec/changes/add-projected-depth-symmetry/specs/image-depth-symmetry/spec.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/specs/image-depth-symmetry/spec.md diff --git a/openspec/changes/add-projected-depth-symmetry/tasks.md b/openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/tasks.md similarity index 100% rename from openspec/changes/add-projected-depth-symmetry/tasks.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/tasks.md diff --git a/openspec/changes/add-projected-depth-symmetry/validation.md b/openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/validation.md similarity index 100% rename from openspec/changes/add-projected-depth-symmetry/validation.md rename to openspec/changes/archive/2026-09-17-add-projected-depth-symmetry/validation.md diff --git a/openspec/changes/add-texture-node-image-actions/.openspec.yaml b/openspec/changes/archive/2026-09-17-add-texture-node-image-actions/.openspec.yaml similarity index 100% rename from openspec/changes/add-texture-node-image-actions/.openspec.yaml rename to openspec/changes/archive/2026-09-17-add-texture-node-image-actions/.openspec.yaml diff --git a/openspec/changes/add-texture-node-image-actions/design.md b/openspec/changes/archive/2026-09-17-add-texture-node-image-actions/design.md similarity index 100% rename from openspec/changes/add-texture-node-image-actions/design.md rename to openspec/changes/archive/2026-09-17-add-texture-node-image-actions/design.md diff --git a/openspec/changes/add-texture-node-image-actions/proposal.md b/openspec/changes/archive/2026-09-17-add-texture-node-image-actions/proposal.md similarity index 100% rename from openspec/changes/add-texture-node-image-actions/proposal.md rename to openspec/changes/archive/2026-09-17-add-texture-node-image-actions/proposal.md diff --git a/openspec/changes/add-texture-node-image-actions/specs/texture-node-image-actions/spec.md b/openspec/changes/archive/2026-09-17-add-texture-node-image-actions/specs/texture-node-image-actions/spec.md similarity index 100% rename from openspec/changes/add-texture-node-image-actions/specs/texture-node-image-actions/spec.md rename to openspec/changes/archive/2026-09-17-add-texture-node-image-actions/specs/texture-node-image-actions/spec.md diff --git a/openspec/changes/add-texture-node-image-actions/tasks.md b/openspec/changes/archive/2026-09-17-add-texture-node-image-actions/tasks.md similarity index 100% rename from openspec/changes/add-texture-node-image-actions/tasks.md rename to openspec/changes/archive/2026-09-17-add-texture-node-image-actions/tasks.md diff --git a/openspec/changes/auto-crop-panorama-input/.openspec.yaml b/openspec/changes/archive/2026-09-17-auto-crop-panorama-input/.openspec.yaml similarity index 100% rename from openspec/changes/auto-crop-panorama-input/.openspec.yaml rename to openspec/changes/archive/2026-09-17-auto-crop-panorama-input/.openspec.yaml diff --git a/openspec/changes/auto-crop-panorama-input/design.md b/openspec/changes/archive/2026-09-17-auto-crop-panorama-input/design.md similarity index 100% rename from openspec/changes/auto-crop-panorama-input/design.md rename to openspec/changes/archive/2026-09-17-auto-crop-panorama-input/design.md diff --git a/openspec/changes/auto-crop-panorama-input/proposal.md b/openspec/changes/archive/2026-09-17-auto-crop-panorama-input/proposal.md similarity index 100% rename from openspec/changes/auto-crop-panorama-input/proposal.md rename to openspec/changes/archive/2026-09-17-auto-crop-panorama-input/proposal.md diff --git a/openspec/changes/auto-crop-panorama-input/specs/panorama-input-cropping/spec.md b/openspec/changes/archive/2026-09-17-auto-crop-panorama-input/specs/panorama-input-cropping/spec.md similarity index 100% rename from openspec/changes/auto-crop-panorama-input/specs/panorama-input-cropping/spec.md rename to openspec/changes/archive/2026-09-17-auto-crop-panorama-input/specs/panorama-input-cropping/spec.md diff --git a/openspec/changes/auto-crop-panorama-input/tasks.md b/openspec/changes/archive/2026-09-17-auto-crop-panorama-input/tasks.md similarity index 100% rename from openspec/changes/auto-crop-panorama-input/tasks.md rename to openspec/changes/archive/2026-09-17-auto-crop-panorama-input/tasks.md diff --git a/openspec/changes/clean-depth-surface-strip-faces/.openspec.yaml b/openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/.openspec.yaml similarity index 100% rename from openspec/changes/clean-depth-surface-strip-faces/.openspec.yaml rename to openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/.openspec.yaml diff --git a/openspec/changes/clean-depth-surface-strip-faces/design.md b/openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/design.md similarity index 100% rename from openspec/changes/clean-depth-surface-strip-faces/design.md rename to openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/design.md diff --git a/openspec/changes/clean-depth-surface-strip-faces/proposal.md b/openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/proposal.md similarity index 100% rename from openspec/changes/clean-depth-surface-strip-faces/proposal.md rename to openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/proposal.md diff --git a/openspec/changes/clean-depth-surface-strip-faces/specs/depth-surface-strip-cleanup/spec.md b/openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/specs/depth-surface-strip-cleanup/spec.md similarity index 100% rename from openspec/changes/clean-depth-surface-strip-faces/specs/depth-surface-strip-cleanup/spec.md rename to openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/specs/depth-surface-strip-cleanup/spec.md diff --git a/openspec/changes/clean-depth-surface-strip-faces/tasks.md b/openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/tasks.md similarity index 100% rename from openspec/changes/clean-depth-surface-strip-faces/tasks.md rename to openspec/changes/archive/2026-09-17-clean-depth-surface-strip-faces/tasks.md diff --git a/openspec/changes/compact-repeat-node-arrangement/.openspec.yaml b/openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/.openspec.yaml similarity index 100% rename from openspec/changes/compact-repeat-node-arrangement/.openspec.yaml rename to openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/.openspec.yaml diff --git a/openspec/changes/compact-repeat-node-arrangement/design.md b/openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/design.md similarity index 100% rename from openspec/changes/compact-repeat-node-arrangement/design.md rename to openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/design.md diff --git a/openspec/changes/compact-repeat-node-arrangement/proposal.md b/openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/proposal.md similarity index 100% rename from openspec/changes/compact-repeat-node-arrangement/proposal.md rename to openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/proposal.md diff --git a/openspec/changes/compact-repeat-node-arrangement/specs/compact-repeat-arrangement/spec.md b/openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/specs/compact-repeat-arrangement/spec.md similarity index 100% rename from openspec/changes/compact-repeat-node-arrangement/specs/compact-repeat-arrangement/spec.md rename to openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/specs/compact-repeat-arrangement/spec.md diff --git a/openspec/changes/compact-repeat-node-arrangement/tasks.md b/openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/tasks.md similarity index 100% rename from openspec/changes/compact-repeat-node-arrangement/tasks.md rename to openspec/changes/archive/2026-09-17-compact-repeat-node-arrangement/tasks.md diff --git a/openspec/changes/compose-depth-cutout-symmetry/.openspec.yaml b/openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/.openspec.yaml similarity index 100% rename from openspec/changes/compose-depth-cutout-symmetry/.openspec.yaml rename to openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/.openspec.yaml diff --git a/openspec/changes/compose-depth-cutout-symmetry/design.md b/openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/design.md similarity index 100% rename from openspec/changes/compose-depth-cutout-symmetry/design.md rename to openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/design.md diff --git a/openspec/changes/compose-depth-cutout-symmetry/proposal.md b/openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/proposal.md similarity index 100% rename from openspec/changes/compose-depth-cutout-symmetry/proposal.md rename to openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/proposal.md diff --git a/openspec/changes/compose-depth-cutout-symmetry/specs/post-cutout-symmetry/spec.md b/openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/specs/post-cutout-symmetry/spec.md similarity index 100% rename from openspec/changes/compose-depth-cutout-symmetry/specs/post-cutout-symmetry/spec.md rename to openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/specs/post-cutout-symmetry/spec.md diff --git a/openspec/changes/compose-depth-cutout-symmetry/specs/shell-normal-smoothing/spec.md b/openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/specs/shell-normal-smoothing/spec.md similarity index 100% rename from openspec/changes/compose-depth-cutout-symmetry/specs/shell-normal-smoothing/spec.md rename to openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/specs/shell-normal-smoothing/spec.md diff --git a/openspec/changes/compose-depth-cutout-symmetry/tasks.md b/openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/tasks.md similarity index 100% rename from openspec/changes/compose-depth-cutout-symmetry/tasks.md rename to openspec/changes/archive/2026-09-17-compose-depth-cutout-symmetry/tasks.md diff --git a/openspec/changes/configure-material-view-adaptation/.openspec.yaml b/openspec/changes/archive/2026-09-17-configure-material-view-adaptation/.openspec.yaml similarity index 100% rename from openspec/changes/configure-material-view-adaptation/.openspec.yaml rename to openspec/changes/archive/2026-09-17-configure-material-view-adaptation/.openspec.yaml diff --git a/openspec/changes/configure-material-view-adaptation/design.md b/openspec/changes/archive/2026-09-17-configure-material-view-adaptation/design.md similarity index 100% rename from openspec/changes/configure-material-view-adaptation/design.md rename to openspec/changes/archive/2026-09-17-configure-material-view-adaptation/design.md diff --git a/openspec/changes/configure-material-view-adaptation/proposal.md b/openspec/changes/archive/2026-09-17-configure-material-view-adaptation/proposal.md similarity index 100% rename from openspec/changes/configure-material-view-adaptation/proposal.md rename to openspec/changes/archive/2026-09-17-configure-material-view-adaptation/proposal.md diff --git a/openspec/changes/configure-material-view-adaptation/specs/material-view-adaptation/spec.md b/openspec/changes/archive/2026-09-17-configure-material-view-adaptation/specs/material-view-adaptation/spec.md similarity index 100% rename from openspec/changes/configure-material-view-adaptation/specs/material-view-adaptation/spec.md rename to openspec/changes/archive/2026-09-17-configure-material-view-adaptation/specs/material-view-adaptation/spec.md diff --git a/openspec/changes/configure-material-view-adaptation/tasks.md b/openspec/changes/archive/2026-09-17-configure-material-view-adaptation/tasks.md similarity index 100% rename from openspec/changes/configure-material-view-adaptation/tasks.md rename to openspec/changes/archive/2026-09-17-configure-material-view-adaptation/tasks.md diff --git a/openspec/changes/correct-coreml-device-support/.openspec.yaml b/openspec/changes/archive/2026-09-17-correct-coreml-device-support/.openspec.yaml similarity index 100% rename from openspec/changes/correct-coreml-device-support/.openspec.yaml rename to openspec/changes/archive/2026-09-17-correct-coreml-device-support/.openspec.yaml diff --git a/openspec/changes/correct-coreml-device-support/design.md b/openspec/changes/archive/2026-09-17-correct-coreml-device-support/design.md similarity index 100% rename from openspec/changes/correct-coreml-device-support/design.md rename to openspec/changes/archive/2026-09-17-correct-coreml-device-support/design.md diff --git a/openspec/changes/correct-coreml-device-support/proposal.md b/openspec/changes/archive/2026-09-17-correct-coreml-device-support/proposal.md similarity index 100% rename from openspec/changes/correct-coreml-device-support/proposal.md rename to openspec/changes/archive/2026-09-17-correct-coreml-device-support/proposal.md diff --git a/openspec/changes/correct-coreml-device-support/specs/coreml-inference/spec.md b/openspec/changes/archive/2026-09-17-correct-coreml-device-support/specs/coreml-inference/spec.md similarity index 100% rename from openspec/changes/correct-coreml-device-support/specs/coreml-inference/spec.md rename to openspec/changes/archive/2026-09-17-correct-coreml-device-support/specs/coreml-inference/spec.md diff --git a/openspec/changes/correct-coreml-device-support/tasks.md b/openspec/changes/archive/2026-09-17-correct-coreml-device-support/tasks.md similarity index 100% rename from openspec/changes/correct-coreml-device-support/tasks.md rename to openspec/changes/archive/2026-09-17-correct-coreml-device-support/tasks.md diff --git a/openspec/changes/enable-image-alpha-blending-and-premul/.openspec.yaml b/openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/.openspec.yaml similarity index 100% rename from openspec/changes/enable-image-alpha-blending-and-premul/.openspec.yaml rename to openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/.openspec.yaml diff --git a/openspec/changes/enable-image-alpha-blending-and-premul/design.md b/openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/design.md similarity index 100% rename from openspec/changes/enable-image-alpha-blending-and-premul/design.md rename to openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/design.md diff --git a/openspec/changes/enable-image-alpha-blending-and-premul/proposal.md b/openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/proposal.md similarity index 100% rename from openspec/changes/enable-image-alpha-blending-and-premul/proposal.md rename to openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/proposal.md diff --git a/openspec/changes/enable-image-alpha-blending-and-premul/specs/frame-edge-antialiasing/spec.md b/openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/specs/frame-edge-antialiasing/spec.md similarity index 100% rename from openspec/changes/enable-image-alpha-blending-and-premul/specs/frame-edge-antialiasing/spec.md rename to openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/specs/frame-edge-antialiasing/spec.md diff --git a/openspec/changes/enable-image-alpha-blending-and-premul/specs/image-alpha-presentation/spec.md b/openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/specs/image-alpha-presentation/spec.md similarity index 100% rename from openspec/changes/enable-image-alpha-blending-and-premul/specs/image-alpha-presentation/spec.md rename to openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/specs/image-alpha-presentation/spec.md diff --git a/openspec/changes/enable-image-alpha-blending-and-premul/tasks.md b/openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/tasks.md similarity index 100% rename from openspec/changes/enable-image-alpha-blending-and-premul/tasks.md rename to openspec/changes/archive/2026-09-17-enable-image-alpha-blending-and-premul/tasks.md diff --git a/openspec/changes/extend-cutout-edge-depth/.openspec.yaml b/openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/.openspec.yaml similarity index 100% rename from openspec/changes/extend-cutout-edge-depth/.openspec.yaml rename to openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/.openspec.yaml diff --git a/openspec/changes/extend-cutout-edge-depth/design.md b/openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/design.md similarity index 100% rename from openspec/changes/extend-cutout-edge-depth/design.md rename to openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/design.md diff --git a/openspec/changes/extend-cutout-edge-depth/proposal.md b/openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/proposal.md similarity index 100% rename from openspec/changes/extend-cutout-edge-depth/proposal.md rename to openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/proposal.md diff --git a/openspec/changes/extend-cutout-edge-depth/specs/cutout-edge-depth/spec.md b/openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/specs/cutout-edge-depth/spec.md similarity index 100% rename from openspec/changes/extend-cutout-edge-depth/specs/cutout-edge-depth/spec.md rename to openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/specs/cutout-edge-depth/spec.md diff --git a/openspec/changes/extend-cutout-edge-depth/tasks.md b/openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/tasks.md similarity index 100% rename from openspec/changes/extend-cutout-edge-depth/tasks.md rename to openspec/changes/archive/2026-09-17-extend-cutout-edge-depth/tasks.md diff --git a/openspec/changes/fix-cutout-density-and-image-edit-names/.openspec.yaml b/openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/.openspec.yaml similarity index 100% rename from openspec/changes/fix-cutout-density-and-image-edit-names/.openspec.yaml rename to openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/.openspec.yaml diff --git a/openspec/changes/fix-cutout-density-and-image-edit-names/design.md b/openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/design.md similarity index 100% rename from openspec/changes/fix-cutout-density-and-image-edit-names/design.md rename to openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/design.md diff --git a/openspec/changes/fix-cutout-density-and-image-edit-names/proposal.md b/openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/proposal.md similarity index 100% rename from openspec/changes/fix-cutout-density-and-image-edit-names/proposal.md rename to openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/proposal.md diff --git a/openspec/changes/fix-cutout-density-and-image-edit-names/specs/cutout-mesh-sampling/spec.md b/openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/specs/cutout-mesh-sampling/spec.md similarity index 100% rename from openspec/changes/fix-cutout-density-and-image-edit-names/specs/cutout-mesh-sampling/spec.md rename to openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/specs/cutout-mesh-sampling/spec.md diff --git a/openspec/changes/fix-cutout-density-and-image-edit-names/specs/image-edit-results/spec.md b/openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/specs/image-edit-results/spec.md similarity index 100% rename from openspec/changes/fix-cutout-density-and-image-edit-names/specs/image-edit-results/spec.md rename to openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/specs/image-edit-results/spec.md diff --git a/openspec/changes/fix-cutout-density-and-image-edit-names/tasks.md b/openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/tasks.md similarity index 100% rename from openspec/changes/fix-cutout-density-and-image-edit-names/tasks.md rename to openspec/changes/archive/2026-09-17-fix-cutout-density-and-image-edit-names/tasks.md diff --git a/openspec/changes/gate-cutout-ai-options/.openspec.yaml b/openspec/changes/archive/2026-09-17-gate-cutout-ai-options/.openspec.yaml similarity index 100% rename from openspec/changes/gate-cutout-ai-options/.openspec.yaml rename to openspec/changes/archive/2026-09-17-gate-cutout-ai-options/.openspec.yaml diff --git a/openspec/changes/gate-cutout-ai-options/design.md b/openspec/changes/archive/2026-09-17-gate-cutout-ai-options/design.md similarity index 100% rename from openspec/changes/gate-cutout-ai-options/design.md rename to openspec/changes/archive/2026-09-17-gate-cutout-ai-options/design.md diff --git a/openspec/changes/gate-cutout-ai-options/proposal.md b/openspec/changes/archive/2026-09-17-gate-cutout-ai-options/proposal.md similarity index 100% rename from openspec/changes/gate-cutout-ai-options/proposal.md rename to openspec/changes/archive/2026-09-17-gate-cutout-ai-options/proposal.md diff --git a/openspec/changes/gate-cutout-ai-options/specs/image-tool-ai-availability/spec.md b/openspec/changes/archive/2026-09-17-gate-cutout-ai-options/specs/image-tool-ai-availability/spec.md similarity index 100% rename from openspec/changes/gate-cutout-ai-options/specs/image-tool-ai-availability/spec.md rename to openspec/changes/archive/2026-09-17-gate-cutout-ai-options/specs/image-tool-ai-availability/spec.md diff --git a/openspec/changes/gate-cutout-ai-options/tasks.md b/openspec/changes/archive/2026-09-17-gate-cutout-ai-options/tasks.md similarity index 100% rename from openspec/changes/gate-cutout-ai-options/tasks.md rename to openspec/changes/archive/2026-09-17-gate-cutout-ai-options/tasks.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/.openspec.yaml b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/.openspec.yaml similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/.openspec.yaml rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/.openspec.yaml diff --git a/openspec/changes/improve-cutout-adaptive-mesh/design.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/design.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/design.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/design.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/experiment-cases.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/experiment-cases.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/experiment-cases.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/experiment-cases.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/experiment.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/experiment.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/experiment.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/experiment.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/implementation.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/implementation.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/implementation.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/implementation.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/proposal.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/proposal.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/proposal.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/proposal.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/specs/cutout-adaptive-mesh/spec.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/specs/cutout-adaptive-mesh/spec.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/specs/cutout-adaptive-mesh/spec.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/specs/cutout-adaptive-mesh/spec.md diff --git a/openspec/changes/improve-cutout-adaptive-mesh/tasks.md b/openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/tasks.md similarity index 100% rename from openspec/changes/improve-cutout-adaptive-mesh/tasks.md rename to openspec/changes/archive/2026-09-17-improve-cutout-adaptive-mesh/tasks.md diff --git a/openspec/changes/improve-relief-depth-controls/.openspec.yaml b/openspec/changes/archive/2026-09-17-improve-relief-depth-controls/.openspec.yaml similarity index 100% rename from openspec/changes/improve-relief-depth-controls/.openspec.yaml rename to openspec/changes/archive/2026-09-17-improve-relief-depth-controls/.openspec.yaml diff --git a/openspec/changes/improve-relief-depth-controls/design.md b/openspec/changes/archive/2026-09-17-improve-relief-depth-controls/design.md similarity index 100% rename from openspec/changes/improve-relief-depth-controls/design.md rename to openspec/changes/archive/2026-09-17-improve-relief-depth-controls/design.md diff --git a/openspec/changes/improve-relief-depth-controls/proposal.md b/openspec/changes/archive/2026-09-17-improve-relief-depth-controls/proposal.md similarity index 100% rename from openspec/changes/improve-relief-depth-controls/proposal.md rename to openspec/changes/archive/2026-09-17-improve-relief-depth-controls/proposal.md diff --git a/openspec/changes/improve-relief-depth-controls/specs/relief-plane-depth-controls/spec.md b/openspec/changes/archive/2026-09-17-improve-relief-depth-controls/specs/relief-plane-depth-controls/spec.md similarity index 100% rename from openspec/changes/improve-relief-depth-controls/specs/relief-plane-depth-controls/spec.md rename to openspec/changes/archive/2026-09-17-improve-relief-depth-controls/specs/relief-plane-depth-controls/spec.md diff --git a/openspec/changes/improve-relief-depth-controls/tasks.md b/openspec/changes/archive/2026-09-17-improve-relief-depth-controls/tasks.md similarity index 100% rename from openspec/changes/improve-relief-depth-controls/tasks.md rename to openspec/changes/archive/2026-09-17-improve-relief-depth-controls/tasks.md diff --git a/openspec/changes/isolate-shared-empty-image-edits/.openspec.yaml b/openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/.openspec.yaml similarity index 100% rename from openspec/changes/isolate-shared-empty-image-edits/.openspec.yaml rename to openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/.openspec.yaml diff --git a/openspec/changes/isolate-shared-empty-image-edits/design.md b/openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/design.md similarity index 100% rename from openspec/changes/isolate-shared-empty-image-edits/design.md rename to openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/design.md diff --git a/openspec/changes/isolate-shared-empty-image-edits/proposal.md b/openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/proposal.md similarity index 100% rename from openspec/changes/isolate-shared-empty-image-edits/proposal.md rename to openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/proposal.md diff --git a/openspec/changes/isolate-shared-empty-image-edits/specs/empty-image-edit-isolation/spec.md b/openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/specs/empty-image-edit-isolation/spec.md similarity index 100% rename from openspec/changes/isolate-shared-empty-image-edits/specs/empty-image-edit-isolation/spec.md rename to openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/specs/empty-image-edit-isolation/spec.md diff --git a/openspec/changes/isolate-shared-empty-image-edits/tasks.md b/openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/tasks.md similarity index 100% rename from openspec/changes/isolate-shared-empty-image-edits/tasks.md rename to openspec/changes/archive/2026-09-17-isolate-shared-empty-image-edits/tasks.md diff --git a/openspec/changes/joint-geometry-node-layout/.openspec.yaml b/openspec/changes/archive/2026-09-17-joint-geometry-node-layout/.openspec.yaml similarity index 100% rename from openspec/changes/joint-geometry-node-layout/.openspec.yaml rename to openspec/changes/archive/2026-09-17-joint-geometry-node-layout/.openspec.yaml diff --git a/openspec/changes/joint-geometry-node-layout/design.md b/openspec/changes/archive/2026-09-17-joint-geometry-node-layout/design.md similarity index 100% rename from openspec/changes/joint-geometry-node-layout/design.md rename to openspec/changes/archive/2026-09-17-joint-geometry-node-layout/design.md diff --git a/openspec/changes/joint-geometry-node-layout/proposal.md b/openspec/changes/archive/2026-09-17-joint-geometry-node-layout/proposal.md similarity index 100% rename from openspec/changes/joint-geometry-node-layout/proposal.md rename to openspec/changes/archive/2026-09-17-joint-geometry-node-layout/proposal.md diff --git a/openspec/changes/joint-geometry-node-layout/specs/joint-geometry-node-layout/spec.md b/openspec/changes/archive/2026-09-17-joint-geometry-node-layout/specs/joint-geometry-node-layout/spec.md similarity index 100% rename from openspec/changes/joint-geometry-node-layout/specs/joint-geometry-node-layout/spec.md rename to openspec/changes/archive/2026-09-17-joint-geometry-node-layout/specs/joint-geometry-node-layout/spec.md diff --git a/openspec/changes/joint-geometry-node-layout/tasks.md b/openspec/changes/archive/2026-09-17-joint-geometry-node-layout/tasks.md similarity index 100% rename from openspec/changes/joint-geometry-node-layout/tasks.md rename to openspec/changes/archive/2026-09-17-joint-geometry-node-layout/tasks.md diff --git a/openspec/changes/match-polyline-gesture-interaction/.openspec.yaml b/openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/.openspec.yaml similarity index 100% rename from openspec/changes/match-polyline-gesture-interaction/.openspec.yaml rename to openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/.openspec.yaml diff --git a/openspec/changes/match-polyline-gesture-interaction/design.md b/openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/design.md similarity index 100% rename from openspec/changes/match-polyline-gesture-interaction/design.md rename to openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/design.md diff --git a/openspec/changes/match-polyline-gesture-interaction/proposal.md b/openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/proposal.md similarity index 100% rename from openspec/changes/match-polyline-gesture-interaction/proposal.md rename to openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/proposal.md diff --git a/openspec/changes/match-polyline-gesture-interaction/specs/polyline-gesture-interaction/spec.md b/openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/specs/polyline-gesture-interaction/spec.md similarity index 100% rename from openspec/changes/match-polyline-gesture-interaction/specs/polyline-gesture-interaction/spec.md rename to openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/specs/polyline-gesture-interaction/spec.md diff --git a/openspec/changes/match-polyline-gesture-interaction/tasks.md b/openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/tasks.md similarity index 100% rename from openspec/changes/match-polyline-gesture-interaction/tasks.md rename to openspec/changes/archive/2026-09-17-match-polyline-gesture-interaction/tasks.md diff --git a/openspec/changes/normalize-moge-alpha-depth/.openspec.yaml b/openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/.openspec.yaml similarity index 100% rename from openspec/changes/normalize-moge-alpha-depth/.openspec.yaml rename to openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/.openspec.yaml diff --git a/openspec/changes/normalize-moge-alpha-depth/design.md b/openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/design.md similarity index 100% rename from openspec/changes/normalize-moge-alpha-depth/design.md rename to openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/design.md diff --git a/openspec/changes/normalize-moge-alpha-depth/proposal.md b/openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/proposal.md similarity index 100% rename from openspec/changes/normalize-moge-alpha-depth/proposal.md rename to openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/proposal.md diff --git a/openspec/changes/normalize-moge-alpha-depth/specs/moge-depth-artifacts/spec.md b/openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/specs/moge-depth-artifacts/spec.md similarity index 100% rename from openspec/changes/normalize-moge-alpha-depth/specs/moge-depth-artifacts/spec.md rename to openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/specs/moge-depth-artifacts/spec.md diff --git a/openspec/changes/normalize-moge-alpha-depth/tasks.md b/openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/tasks.md similarity index 100% rename from openspec/changes/normalize-moge-alpha-depth/tasks.md rename to openspec/changes/archive/2026-09-17-normalize-moge-alpha-depth/tasks.md diff --git a/openspec/changes/optimize-cutout-geometry-nodes/.openspec.yaml b/openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/.openspec.yaml similarity index 100% rename from openspec/changes/optimize-cutout-geometry-nodes/.openspec.yaml rename to openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/.openspec.yaml diff --git a/openspec/changes/optimize-cutout-geometry-nodes/design.md b/openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/design.md similarity index 100% rename from openspec/changes/optimize-cutout-geometry-nodes/design.md rename to openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/design.md diff --git a/openspec/changes/optimize-cutout-geometry-nodes/proposal.md b/openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/proposal.md similarity index 100% rename from openspec/changes/optimize-cutout-geometry-nodes/proposal.md rename to openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/proposal.md diff --git a/openspec/changes/optimize-cutout-geometry-nodes/specs/cutout-attribute-lifecycle/spec.md b/openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/specs/cutout-attribute-lifecycle/spec.md similarity index 100% rename from openspec/changes/optimize-cutout-geometry-nodes/specs/cutout-attribute-lifecycle/spec.md rename to openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/specs/cutout-attribute-lifecycle/spec.md diff --git a/openspec/changes/optimize-cutout-geometry-nodes/specs/cutout-geometry-node-efficiency/spec.md b/openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/specs/cutout-geometry-node-efficiency/spec.md similarity index 100% rename from openspec/changes/optimize-cutout-geometry-nodes/specs/cutout-geometry-node-efficiency/spec.md rename to openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/specs/cutout-geometry-node-efficiency/spec.md diff --git a/openspec/changes/optimize-cutout-geometry-nodes/tasks.md b/openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/tasks.md similarity index 100% rename from openspec/changes/optimize-cutout-geometry-nodes/tasks.md rename to openspec/changes/archive/2026-09-17-optimize-cutout-geometry-nodes/tasks.md diff --git a/openspec/changes/organize-tool-cli/.openspec.yaml b/openspec/changes/archive/2026-09-17-organize-tool-cli/.openspec.yaml similarity index 100% rename from openspec/changes/organize-tool-cli/.openspec.yaml rename to openspec/changes/archive/2026-09-17-organize-tool-cli/.openspec.yaml diff --git a/openspec/changes/organize-tool-cli/design.md b/openspec/changes/archive/2026-09-17-organize-tool-cli/design.md similarity index 100% rename from openspec/changes/organize-tool-cli/design.md rename to openspec/changes/archive/2026-09-17-organize-tool-cli/design.md diff --git a/openspec/changes/organize-tool-cli/proposal.md b/openspec/changes/archive/2026-09-17-organize-tool-cli/proposal.md similarity index 100% rename from openspec/changes/organize-tool-cli/proposal.md rename to openspec/changes/archive/2026-09-17-organize-tool-cli/proposal.md diff --git a/openspec/changes/organize-tool-cli/specs/developer-tool-cli/spec.md b/openspec/changes/archive/2026-09-17-organize-tool-cli/specs/developer-tool-cli/spec.md similarity index 100% rename from openspec/changes/organize-tool-cli/specs/developer-tool-cli/spec.md rename to openspec/changes/archive/2026-09-17-organize-tool-cli/specs/developer-tool-cli/spec.md diff --git a/openspec/changes/organize-tool-cli/tasks.md b/openspec/changes/archive/2026-09-17-organize-tool-cli/tasks.md similarity index 100% rename from openspec/changes/organize-tool-cli/tasks.md rename to openspec/changes/archive/2026-09-17-organize-tool-cli/tasks.md diff --git a/openspec/changes/pad-cutout-material-edges/.openspec.yaml b/openspec/changes/archive/2026-09-17-pad-cutout-material-edges/.openspec.yaml similarity index 100% rename from openspec/changes/pad-cutout-material-edges/.openspec.yaml rename to openspec/changes/archive/2026-09-17-pad-cutout-material-edges/.openspec.yaml diff --git a/openspec/changes/pad-cutout-material-edges/design.md b/openspec/changes/archive/2026-09-17-pad-cutout-material-edges/design.md similarity index 100% rename from openspec/changes/pad-cutout-material-edges/design.md rename to openspec/changes/archive/2026-09-17-pad-cutout-material-edges/design.md diff --git a/openspec/changes/pad-cutout-material-edges/proposal.md b/openspec/changes/archive/2026-09-17-pad-cutout-material-edges/proposal.md similarity index 100% rename from openspec/changes/pad-cutout-material-edges/proposal.md rename to openspec/changes/archive/2026-09-17-pad-cutout-material-edges/proposal.md diff --git a/openspec/changes/pad-cutout-material-edges/specs/cutout-boundary-padding/spec.md b/openspec/changes/archive/2026-09-17-pad-cutout-material-edges/specs/cutout-boundary-padding/spec.md similarity index 100% rename from openspec/changes/pad-cutout-material-edges/specs/cutout-boundary-padding/spec.md rename to openspec/changes/archive/2026-09-17-pad-cutout-material-edges/specs/cutout-boundary-padding/spec.md diff --git a/openspec/changes/pad-cutout-material-edges/tasks.md b/openspec/changes/archive/2026-09-17-pad-cutout-material-edges/tasks.md similarity index 100% rename from openspec/changes/pad-cutout-material-edges/tasks.md rename to openspec/changes/archive/2026-09-17-pad-cutout-material-edges/tasks.md diff --git a/openspec/changes/persist-image-tool-options/.openspec.yaml b/openspec/changes/archive/2026-09-17-persist-image-tool-options/.openspec.yaml similarity index 100% rename from openspec/changes/persist-image-tool-options/.openspec.yaml rename to openspec/changes/archive/2026-09-17-persist-image-tool-options/.openspec.yaml diff --git a/openspec/changes/persist-image-tool-options/design.md b/openspec/changes/archive/2026-09-17-persist-image-tool-options/design.md similarity index 100% rename from openspec/changes/persist-image-tool-options/design.md rename to openspec/changes/archive/2026-09-17-persist-image-tool-options/design.md diff --git a/openspec/changes/persist-image-tool-options/proposal.md b/openspec/changes/archive/2026-09-17-persist-image-tool-options/proposal.md similarity index 100% rename from openspec/changes/persist-image-tool-options/proposal.md rename to openspec/changes/archive/2026-09-17-persist-image-tool-options/proposal.md diff --git a/openspec/changes/persist-image-tool-options/specs/crop-tool-options/spec.md b/openspec/changes/archive/2026-09-17-persist-image-tool-options/specs/crop-tool-options/spec.md similarity index 100% rename from openspec/changes/persist-image-tool-options/specs/crop-tool-options/spec.md rename to openspec/changes/archive/2026-09-17-persist-image-tool-options/specs/crop-tool-options/spec.md diff --git a/openspec/changes/persist-image-tool-options/tasks.md b/openspec/changes/archive/2026-09-17-persist-image-tool-options/tasks.md similarity index 100% rename from openspec/changes/persist-image-tool-options/tasks.md rename to openspec/changes/archive/2026-09-17-persist-image-tool-options/tasks.md diff --git a/openspec/changes/pin-geometry-node-smoothing/.openspec.yaml b/openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/.openspec.yaml similarity index 100% rename from openspec/changes/pin-geometry-node-smoothing/.openspec.yaml rename to openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/.openspec.yaml diff --git a/openspec/changes/pin-geometry-node-smoothing/design.md b/openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/design.md similarity index 100% rename from openspec/changes/pin-geometry-node-smoothing/design.md rename to openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/design.md diff --git a/openspec/changes/pin-geometry-node-smoothing/proposal.md b/openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/proposal.md similarity index 100% rename from openspec/changes/pin-geometry-node-smoothing/proposal.md rename to openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/proposal.md diff --git a/openspec/changes/pin-geometry-node-smoothing/specs/pinned-geometry-smoothing/spec.md b/openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/specs/pinned-geometry-smoothing/spec.md similarity index 100% rename from openspec/changes/pin-geometry-node-smoothing/specs/pinned-geometry-smoothing/spec.md rename to openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/specs/pinned-geometry-smoothing/spec.md diff --git a/openspec/changes/pin-geometry-node-smoothing/tasks.md b/openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/tasks.md similarity index 100% rename from openspec/changes/pin-geometry-node-smoothing/tasks.md rename to openspec/changes/archive/2026-09-17-pin-geometry-node-smoothing/tasks.md diff --git a/openspec/changes/preserve-frame-bottom-layer-rgb/.openspec.yaml b/openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/.openspec.yaml similarity index 100% rename from openspec/changes/preserve-frame-bottom-layer-rgb/.openspec.yaml rename to openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/.openspec.yaml diff --git a/openspec/changes/preserve-frame-bottom-layer-rgb/design.md b/openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/design.md similarity index 100% rename from openspec/changes/preserve-frame-bottom-layer-rgb/design.md rename to openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/design.md diff --git a/openspec/changes/preserve-frame-bottom-layer-rgb/proposal.md b/openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/proposal.md similarity index 100% rename from openspec/changes/preserve-frame-bottom-layer-rgb/proposal.md rename to openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/proposal.md diff --git a/openspec/changes/preserve-frame-bottom-layer-rgb/specs/frame-transparent-rgb/spec.md b/openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/specs/frame-transparent-rgb/spec.md similarity index 100% rename from openspec/changes/preserve-frame-bottom-layer-rgb/specs/frame-transparent-rgb/spec.md rename to openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/specs/frame-transparent-rgb/spec.md diff --git a/openspec/changes/preserve-frame-bottom-layer-rgb/tasks.md b/openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/tasks.md similarity index 100% rename from openspec/changes/preserve-frame-bottom-layer-rgb/tasks.md rename to openspec/changes/archive/2026-09-17-preserve-frame-bottom-layer-rgb/tasks.md diff --git a/openspec/changes/preserve-image-tool-source-on-empty/.openspec.yaml b/openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/.openspec.yaml similarity index 100% rename from openspec/changes/preserve-image-tool-source-on-empty/.openspec.yaml rename to openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/.openspec.yaml diff --git a/openspec/changes/preserve-image-tool-source-on-empty/design.md b/openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/design.md similarity index 100% rename from openspec/changes/preserve-image-tool-source-on-empty/design.md rename to openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/design.md diff --git a/openspec/changes/preserve-image-tool-source-on-empty/proposal.md b/openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/proposal.md similarity index 100% rename from openspec/changes/preserve-image-tool-source-on-empty/proposal.md rename to openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/proposal.md diff --git a/openspec/changes/preserve-image-tool-source-on-empty/specs/image-tool-interaction/spec.md b/openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/specs/image-tool-interaction/spec.md similarity index 100% rename from openspec/changes/preserve-image-tool-source-on-empty/specs/image-tool-interaction/spec.md rename to openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/specs/image-tool-interaction/spec.md diff --git a/openspec/changes/preserve-image-tool-source-on-empty/tasks.md b/openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/tasks.md similarity index 100% rename from openspec/changes/preserve-image-tool-source-on-empty/tasks.md rename to openspec/changes/archive/2026-09-17-preserve-image-tool-source-on-empty/tasks.md diff --git a/openspec/changes/process-image-selection-once/.openspec.yaml b/openspec/changes/archive/2026-09-17-process-image-selection-once/.openspec.yaml similarity index 100% rename from openspec/changes/process-image-selection-once/.openspec.yaml rename to openspec/changes/archive/2026-09-17-process-image-selection-once/.openspec.yaml diff --git a/openspec/changes/process-image-selection-once/design.md b/openspec/changes/archive/2026-09-17-process-image-selection-once/design.md similarity index 100% rename from openspec/changes/process-image-selection-once/design.md rename to openspec/changes/archive/2026-09-17-process-image-selection-once/design.md diff --git a/openspec/changes/process-image-selection-once/proposal.md b/openspec/changes/archive/2026-09-17-process-image-selection-once/proposal.md similarity index 100% rename from openspec/changes/process-image-selection-once/proposal.md rename to openspec/changes/archive/2026-09-17-process-image-selection-once/proposal.md diff --git a/openspec/changes/process-image-selection-once/specs/image-selection-processing/spec.md b/openspec/changes/archive/2026-09-17-process-image-selection-once/specs/image-selection-processing/spec.md similarity index 100% rename from openspec/changes/process-image-selection-once/specs/image-selection-processing/spec.md rename to openspec/changes/archive/2026-09-17-process-image-selection-once/specs/image-selection-processing/spec.md diff --git a/openspec/changes/process-image-selection-once/tasks.md b/openspec/changes/archive/2026-09-17-process-image-selection-once/tasks.md similarity index 100% rename from openspec/changes/process-image-selection-once/tasks.md rename to openspec/changes/archive/2026-09-17-process-image-selection-once/tasks.md diff --git a/openspec/changes/prototype-panorama-surface/.openspec.yaml b/openspec/changes/archive/2026-09-17-prototype-panorama-surface/.openspec.yaml similarity index 100% rename from openspec/changes/prototype-panorama-surface/.openspec.yaml rename to openspec/changes/archive/2026-09-17-prototype-panorama-surface/.openspec.yaml diff --git a/openspec/changes/prototype-panorama-surface/design.md b/openspec/changes/archive/2026-09-17-prototype-panorama-surface/design.md similarity index 100% rename from openspec/changes/prototype-panorama-surface/design.md rename to openspec/changes/archive/2026-09-17-prototype-panorama-surface/design.md diff --git a/openspec/changes/prototype-panorama-surface/proposal.md b/openspec/changes/archive/2026-09-17-prototype-panorama-surface/proposal.md similarity index 100% rename from openspec/changes/prototype-panorama-surface/proposal.md rename to openspec/changes/archive/2026-09-17-prototype-panorama-surface/proposal.md diff --git a/openspec/changes/prototype-panorama-surface/specs/panorama-surface-prototype/spec.md b/openspec/changes/archive/2026-09-17-prototype-panorama-surface/specs/panorama-surface-prototype/spec.md similarity index 100% rename from openspec/changes/prototype-panorama-surface/specs/panorama-surface-prototype/spec.md rename to openspec/changes/archive/2026-09-17-prototype-panorama-surface/specs/panorama-surface-prototype/spec.md diff --git a/openspec/changes/prototype-panorama-surface/tasks.md b/openspec/changes/archive/2026-09-17-prototype-panorama-surface/tasks.md similarity index 100% rename from openspec/changes/prototype-panorama-surface/tasks.md rename to openspec/changes/archive/2026-09-17-prototype-panorama-surface/tasks.md diff --git a/openspec/changes/refine-cutout-split-boundaries/.openspec.yaml b/openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/.openspec.yaml similarity index 100% rename from openspec/changes/refine-cutout-split-boundaries/.openspec.yaml rename to openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/.openspec.yaml diff --git a/openspec/changes/refine-cutout-split-boundaries/design.md b/openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/design.md similarity index 100% rename from openspec/changes/refine-cutout-split-boundaries/design.md rename to openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/design.md diff --git a/openspec/changes/refine-cutout-split-boundaries/proposal.md b/openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/proposal.md similarity index 100% rename from openspec/changes/refine-cutout-split-boundaries/proposal.md rename to openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/proposal.md diff --git a/openspec/changes/refine-cutout-split-boundaries/specs/cutout-split-boundary-shaping/spec.md b/openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/specs/cutout-split-boundary-shaping/spec.md similarity index 100% rename from openspec/changes/refine-cutout-split-boundaries/specs/cutout-split-boundary-shaping/spec.md rename to openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/specs/cutout-split-boundary-shaping/spec.md diff --git a/openspec/changes/refine-cutout-split-boundaries/tasks.md b/openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/tasks.md similarity index 100% rename from openspec/changes/refine-cutout-split-boundaries/tasks.md rename to openspec/changes/archive/2026-09-17-refine-cutout-split-boundaries/tasks.md diff --git a/openspec/changes/refine-cutout-symmetry-seam/.openspec.yaml b/openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/.openspec.yaml similarity index 100% rename from openspec/changes/refine-cutout-symmetry-seam/.openspec.yaml rename to openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/.openspec.yaml diff --git a/openspec/changes/refine-cutout-symmetry-seam/design.md b/openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/design.md similarity index 100% rename from openspec/changes/refine-cutout-symmetry-seam/design.md rename to openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/design.md diff --git a/openspec/changes/refine-cutout-symmetry-seam/proposal.md b/openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/proposal.md similarity index 100% rename from openspec/changes/refine-cutout-symmetry-seam/proposal.md rename to openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/proposal.md diff --git a/openspec/changes/refine-cutout-symmetry-seam/specs/cutout-symmetry-seam/spec.md b/openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/specs/cutout-symmetry-seam/spec.md similarity index 100% rename from openspec/changes/refine-cutout-symmetry-seam/specs/cutout-symmetry-seam/spec.md rename to openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/specs/cutout-symmetry-seam/spec.md diff --git a/openspec/changes/refine-cutout-symmetry-seam/tasks.md b/openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/tasks.md similarity index 100% rename from openspec/changes/refine-cutout-symmetry-seam/tasks.md rename to openspec/changes/archive/2026-09-17-refine-cutout-symmetry-seam/tasks.md diff --git a/openspec/changes/refine-image-output-semantics/.openspec.yaml b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/.openspec.yaml similarity index 100% rename from openspec/changes/refine-image-output-semantics/.openspec.yaml rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/.openspec.yaml diff --git a/openspec/changes/refine-image-output-semantics/design.md b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/design.md similarity index 100% rename from openspec/changes/refine-image-output-semantics/design.md rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/design.md diff --git a/openspec/changes/refine-image-output-semantics/proposal.md b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/proposal.md similarity index 100% rename from openspec/changes/refine-image-output-semantics/proposal.md rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/proposal.md diff --git a/openspec/changes/refine-image-output-semantics/specs/geometry-depth-controls/spec.md b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/specs/geometry-depth-controls/spec.md similarity index 100% rename from openspec/changes/refine-image-output-semantics/specs/geometry-depth-controls/spec.md rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/specs/geometry-depth-controls/spec.md diff --git a/openspec/changes/refine-image-output-semantics/specs/image-result-materials/spec.md b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/specs/image-result-materials/spec.md similarity index 100% rename from openspec/changes/refine-image-output-semantics/specs/image-result-materials/spec.md rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/specs/image-result-materials/spec.md diff --git a/openspec/changes/refine-image-output-semantics/specs/selection-content-processing/spec.md b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/specs/selection-content-processing/spec.md similarity index 100% rename from openspec/changes/refine-image-output-semantics/specs/selection-content-processing/spec.md rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/specs/selection-content-processing/spec.md diff --git a/openspec/changes/refine-image-output-semantics/tasks.md b/openspec/changes/archive/2026-09-17-refine-image-output-semantics/tasks.md similarity index 100% rename from openspec/changes/refine-image-output-semantics/tasks.md rename to openspec/changes/archive/2026-09-17-refine-image-output-semantics/tasks.md diff --git a/openspec/changes/release-model-inference-memory/.openspec.yaml b/openspec/changes/archive/2026-09-17-release-model-inference-memory/.openspec.yaml similarity index 100% rename from openspec/changes/release-model-inference-memory/.openspec.yaml rename to openspec/changes/archive/2026-09-17-release-model-inference-memory/.openspec.yaml diff --git a/openspec/changes/release-model-inference-memory/design.md b/openspec/changes/archive/2026-09-17-release-model-inference-memory/design.md similarity index 100% rename from openspec/changes/release-model-inference-memory/design.md rename to openspec/changes/archive/2026-09-17-release-model-inference-memory/design.md diff --git a/openspec/changes/release-model-inference-memory/proposal.md b/openspec/changes/archive/2026-09-17-release-model-inference-memory/proposal.md similarity index 100% rename from openspec/changes/release-model-inference-memory/proposal.md rename to openspec/changes/archive/2026-09-17-release-model-inference-memory/proposal.md diff --git a/openspec/changes/release-model-inference-memory/specs/model-inference-memory/spec.md b/openspec/changes/archive/2026-09-17-release-model-inference-memory/specs/model-inference-memory/spec.md similarity index 100% rename from openspec/changes/release-model-inference-memory/specs/model-inference-memory/spec.md rename to openspec/changes/archive/2026-09-17-release-model-inference-memory/specs/model-inference-memory/spec.md diff --git a/openspec/changes/release-model-inference-memory/tasks.md b/openspec/changes/archive/2026-09-17-release-model-inference-memory/tasks.md similarity index 100% rename from openspec/changes/release-model-inference-memory/tasks.md rename to openspec/changes/archive/2026-09-17-release-model-inference-memory/tasks.md diff --git a/openspec/changes/remove-debug-workflows/.openspec.yaml b/openspec/changes/archive/2026-09-17-remove-debug-workflows/.openspec.yaml similarity index 100% rename from openspec/changes/remove-debug-workflows/.openspec.yaml rename to openspec/changes/archive/2026-09-17-remove-debug-workflows/.openspec.yaml diff --git a/openspec/changes/remove-debug-workflows/design.md b/openspec/changes/archive/2026-09-17-remove-debug-workflows/design.md similarity index 100% rename from openspec/changes/remove-debug-workflows/design.md rename to openspec/changes/archive/2026-09-17-remove-debug-workflows/design.md diff --git a/openspec/changes/remove-debug-workflows/proposal.md b/openspec/changes/archive/2026-09-17-remove-debug-workflows/proposal.md similarity index 100% rename from openspec/changes/remove-debug-workflows/proposal.md rename to openspec/changes/archive/2026-09-17-remove-debug-workflows/proposal.md diff --git a/openspec/changes/remove-debug-workflows/specs/maintainable-project-structure/spec.md b/openspec/changes/archive/2026-09-17-remove-debug-workflows/specs/maintainable-project-structure/spec.md similarity index 100% rename from openspec/changes/remove-debug-workflows/specs/maintainable-project-structure/spec.md rename to openspec/changes/archive/2026-09-17-remove-debug-workflows/specs/maintainable-project-structure/spec.md diff --git a/openspec/changes/remove-debug-workflows/specs/production-only-ai-workflows/spec.md b/openspec/changes/archive/2026-09-17-remove-debug-workflows/specs/production-only-ai-workflows/spec.md similarity index 100% rename from openspec/changes/remove-debug-workflows/specs/production-only-ai-workflows/spec.md rename to openspec/changes/archive/2026-09-17-remove-debug-workflows/specs/production-only-ai-workflows/spec.md diff --git a/openspec/changes/remove-debug-workflows/tasks.md b/openspec/changes/archive/2026-09-17-remove-debug-workflows/tasks.md similarity index 100% rename from openspec/changes/remove-debug-workflows/tasks.md rename to openspec/changes/archive/2026-09-17-remove-debug-workflows/tasks.md diff --git a/openspec/changes/remove-geometry-capture-attributes/.openspec.yaml b/openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/.openspec.yaml similarity index 100% rename from openspec/changes/remove-geometry-capture-attributes/.openspec.yaml rename to openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/.openspec.yaml diff --git a/openspec/changes/remove-geometry-capture-attributes/design.md b/openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/design.md similarity index 100% rename from openspec/changes/remove-geometry-capture-attributes/design.md rename to openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/design.md diff --git a/openspec/changes/remove-geometry-capture-attributes/proposal.md b/openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/proposal.md similarity index 100% rename from openspec/changes/remove-geometry-capture-attributes/proposal.md rename to openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/proposal.md diff --git a/openspec/changes/remove-geometry-capture-attributes/specs/geometry-field-flow/spec.md b/openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/specs/geometry-field-flow/spec.md similarity index 100% rename from openspec/changes/remove-geometry-capture-attributes/specs/geometry-field-flow/spec.md rename to openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/specs/geometry-field-flow/spec.md diff --git a/openspec/changes/remove-geometry-capture-attributes/tasks.md b/openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/tasks.md similarity index 100% rename from openspec/changes/remove-geometry-capture-attributes/tasks.md rename to openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/tasks.md diff --git a/openspec/changes/remove-geometry-capture-attributes/validation.md b/openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/validation.md similarity index 100% rename from openspec/changes/remove-geometry-capture-attributes/validation.md rename to openspec/changes/archive/2026-09-17-remove-geometry-capture-attributes/validation.md diff --git a/openspec/changes/remove-geometry-node-smoothing/.openspec.yaml b/openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/.openspec.yaml similarity index 100% rename from openspec/changes/remove-geometry-node-smoothing/.openspec.yaml rename to openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/.openspec.yaml diff --git a/openspec/changes/remove-geometry-node-smoothing/design.md b/openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/design.md similarity index 100% rename from openspec/changes/remove-geometry-node-smoothing/design.md rename to openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/design.md diff --git a/openspec/changes/remove-geometry-node-smoothing/proposal.md b/openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/proposal.md similarity index 100% rename from openspec/changes/remove-geometry-node-smoothing/proposal.md rename to openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/proposal.md diff --git a/openspec/changes/remove-geometry-node-smoothing/specs/geometry-node-smoothing/spec.md b/openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/specs/geometry-node-smoothing/spec.md similarity index 100% rename from openspec/changes/remove-geometry-node-smoothing/specs/geometry-node-smoothing/spec.md rename to openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/specs/geometry-node-smoothing/spec.md diff --git a/openspec/changes/remove-geometry-node-smoothing/tasks.md b/openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/tasks.md similarity index 100% rename from openspec/changes/remove-geometry-node-smoothing/tasks.md rename to openspec/changes/archive/2026-09-17-remove-geometry-node-smoothing/tasks.md diff --git a/openspec/changes/reorganize-common-modules/.openspec.yaml b/openspec/changes/archive/2026-09-17-reorganize-common-modules/.openspec.yaml similarity index 100% rename from openspec/changes/reorganize-common-modules/.openspec.yaml rename to openspec/changes/archive/2026-09-17-reorganize-common-modules/.openspec.yaml diff --git a/openspec/changes/reorganize-common-modules/design.md b/openspec/changes/archive/2026-09-17-reorganize-common-modules/design.md similarity index 100% rename from openspec/changes/reorganize-common-modules/design.md rename to openspec/changes/archive/2026-09-17-reorganize-common-modules/design.md diff --git a/openspec/changes/reorganize-common-modules/proposal.md b/openspec/changes/archive/2026-09-17-reorganize-common-modules/proposal.md similarity index 100% rename from openspec/changes/reorganize-common-modules/proposal.md rename to openspec/changes/archive/2026-09-17-reorganize-common-modules/proposal.md diff --git a/openspec/changes/reorganize-common-modules/tasks.md b/openspec/changes/archive/2026-09-17-reorganize-common-modules/tasks.md similarity index 100% rename from openspec/changes/reorganize-common-modules/tasks.md rename to openspec/changes/archive/2026-09-17-reorganize-common-modules/tasks.md diff --git a/openspec/changes/reorganize-cutout-shapes/.openspec.yaml b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/.openspec.yaml similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/.openspec.yaml rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/.openspec.yaml diff --git a/openspec/changes/reorganize-cutout-shapes/design.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/design.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/design.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/design.md diff --git a/openspec/changes/reorganize-cutout-shapes/proposal.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/proposal.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/proposal.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/proposal.md diff --git a/openspec/changes/reorganize-cutout-shapes/specs/cutout-normal-policy/spec.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/specs/cutout-normal-policy/spec.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/specs/cutout-normal-policy/spec.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/specs/cutout-normal-policy/spec.md diff --git a/openspec/changes/reorganize-cutout-shapes/specs/cutout-shape-presets/spec.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/specs/cutout-shape-presets/spec.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/specs/cutout-shape-presets/spec.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/specs/cutout-shape-presets/spec.md diff --git a/openspec/changes/reorganize-cutout-shapes/specs/depth-cutout-volume/spec.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/specs/depth-cutout-volume/spec.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/specs/depth-cutout-volume/spec.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/specs/depth-cutout-volume/spec.md diff --git a/openspec/changes/reorganize-cutout-shapes/tasks.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/tasks.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/tasks.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/tasks.md diff --git a/openspec/changes/reorganize-cutout-shapes/validation.md b/openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/validation.md similarity index 100% rename from openspec/changes/reorganize-cutout-shapes/validation.md rename to openspec/changes/archive/2026-09-17-reorganize-cutout-shapes/validation.md diff --git a/openspec/changes/repair-image-edit-pipelines/.openspec.yaml b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/.openspec.yaml similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/.openspec.yaml rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/.openspec.yaml diff --git a/openspec/changes/repair-image-edit-pipelines/design.md b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/design.md similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/design.md rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/design.md diff --git a/openspec/changes/repair-image-edit-pipelines/proposal.md b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/proposal.md similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/proposal.md rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/proposal.md diff --git a/openspec/changes/repair-image-edit-pipelines/specs/brush-stroke-processing/spec.md b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/specs/brush-stroke-processing/spec.md similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/specs/brush-stroke-processing/spec.md rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/specs/brush-stroke-processing/spec.md diff --git a/openspec/changes/repair-image-edit-pipelines/specs/frame-projection-clipping/spec.md b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/specs/frame-projection-clipping/spec.md similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/specs/frame-projection-clipping/spec.md rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/specs/frame-projection-clipping/spec.md diff --git a/openspec/changes/repair-image-edit-pipelines/specs/image-pixel-read-integrity/spec.md b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/specs/image-pixel-read-integrity/spec.md similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/specs/image-pixel-read-integrity/spec.md rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/specs/image-pixel-read-integrity/spec.md diff --git a/openspec/changes/repair-image-edit-pipelines/tasks.md b/openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/tasks.md similarity index 100% rename from openspec/changes/repair-image-edit-pipelines/tasks.md rename to openspec/changes/archive/2026-09-17-repair-image-edit-pipelines/tasks.md diff --git a/openspec/changes/replace-crop-box-with-frame/.openspec.yaml b/openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/.openspec.yaml similarity index 100% rename from openspec/changes/replace-crop-box-with-frame/.openspec.yaml rename to openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/.openspec.yaml diff --git a/openspec/changes/replace-crop-box-with-frame/design.md b/openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/design.md similarity index 100% rename from openspec/changes/replace-crop-box-with-frame/design.md rename to openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/design.md diff --git a/openspec/changes/replace-crop-box-with-frame/proposal.md b/openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/proposal.md similarity index 100% rename from openspec/changes/replace-crop-box-with-frame/proposal.md rename to openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/proposal.md diff --git a/openspec/changes/replace-crop-box-with-frame/specs/frame-tool/spec.md b/openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/specs/frame-tool/spec.md similarity index 100% rename from openspec/changes/replace-crop-box-with-frame/specs/frame-tool/spec.md rename to openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/specs/frame-tool/spec.md diff --git a/openspec/changes/replace-crop-box-with-frame/tasks.md b/openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/tasks.md similarity index 100% rename from openspec/changes/replace-crop-box-with-frame/tasks.md rename to openspec/changes/archive/2026-09-17-replace-crop-box-with-frame/tasks.md diff --git a/openspec/changes/replace-crop-with-mask-and-rectify/.openspec.yaml b/openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/.openspec.yaml similarity index 100% rename from openspec/changes/replace-crop-with-mask-and-rectify/.openspec.yaml rename to openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/.openspec.yaml diff --git a/openspec/changes/replace-crop-with-mask-and-rectify/design.md b/openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/design.md similarity index 100% rename from openspec/changes/replace-crop-with-mask-and-rectify/design.md rename to openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/design.md diff --git a/openspec/changes/replace-crop-with-mask-and-rectify/proposal.md b/openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/proposal.md similarity index 100% rename from openspec/changes/replace-crop-with-mask-and-rectify/proposal.md rename to openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/proposal.md diff --git a/openspec/changes/replace-crop-with-mask-and-rectify/specs/image-edit-tools/spec.md b/openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/specs/image-edit-tools/spec.md similarity index 100% rename from openspec/changes/replace-crop-with-mask-and-rectify/specs/image-edit-tools/spec.md rename to openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/specs/image-edit-tools/spec.md diff --git a/openspec/changes/replace-crop-with-mask-and-rectify/tasks.md b/openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/tasks.md similarity index 100% rename from openspec/changes/replace-crop-with-mask-and-rectify/tasks.md rename to openspec/changes/archive/2026-09-17-replace-crop-with-mask-and-rectify/tasks.md diff --git a/openspec/changes/replace-depth-surface-stretch-with-split/.openspec.yaml b/openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/.openspec.yaml similarity index 100% rename from openspec/changes/replace-depth-surface-stretch-with-split/.openspec.yaml rename to openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/.openspec.yaml diff --git a/openspec/changes/replace-depth-surface-stretch-with-split/design.md b/openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/design.md similarity index 100% rename from openspec/changes/replace-depth-surface-stretch-with-split/design.md rename to openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/design.md diff --git a/openspec/changes/replace-depth-surface-stretch-with-split/proposal.md b/openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/proposal.md similarity index 100% rename from openspec/changes/replace-depth-surface-stretch-with-split/proposal.md rename to openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/proposal.md diff --git a/openspec/changes/replace-depth-surface-stretch-with-split/specs/depth-surface-face-separation/spec.md b/openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/specs/depth-surface-face-separation/spec.md similarity index 100% rename from openspec/changes/replace-depth-surface-stretch-with-split/specs/depth-surface-face-separation/spec.md rename to openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/specs/depth-surface-face-separation/spec.md diff --git a/openspec/changes/replace-depth-surface-stretch-with-split/tasks.md b/openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/tasks.md similarity index 100% rename from openspec/changes/replace-depth-surface-stretch-with-split/tasks.md rename to openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/tasks.md diff --git a/openspec/changes/replace-depth-surface-stretch-with-split/validation.md b/openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/validation.md similarity index 100% rename from openspec/changes/replace-depth-surface-stretch-with-split/validation.md rename to openspec/changes/archive/2026-09-17-replace-depth-surface-stretch-with-split/validation.md diff --git a/openspec/changes/restore-cutout-depth-extension/.openspec.yaml b/openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/.openspec.yaml similarity index 100% rename from openspec/changes/restore-cutout-depth-extension/.openspec.yaml rename to openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/.openspec.yaml diff --git a/openspec/changes/restore-cutout-depth-extension/design.md b/openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/design.md similarity index 100% rename from openspec/changes/restore-cutout-depth-extension/design.md rename to openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/design.md diff --git a/openspec/changes/restore-cutout-depth-extension/proposal.md b/openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/proposal.md similarity index 100% rename from openspec/changes/restore-cutout-depth-extension/proposal.md rename to openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/proposal.md diff --git a/openspec/changes/restore-cutout-depth-extension/specs/cutout-depth-extension/spec.md b/openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/specs/cutout-depth-extension/spec.md similarity index 100% rename from openspec/changes/restore-cutout-depth-extension/specs/cutout-depth-extension/spec.md rename to openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/specs/cutout-depth-extension/spec.md diff --git a/openspec/changes/restore-cutout-depth-extension/tasks.md b/openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/tasks.md similarity index 100% rename from openspec/changes/restore-cutout-depth-extension/tasks.md rename to openspec/changes/archive/2026-09-17-restore-cutout-depth-extension/tasks.md diff --git a/openspec/changes/restore-depth-surface-stretch-culling/.openspec.yaml b/openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/.openspec.yaml similarity index 100% rename from openspec/changes/restore-depth-surface-stretch-culling/.openspec.yaml rename to openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/.openspec.yaml diff --git a/openspec/changes/restore-depth-surface-stretch-culling/design.md b/openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/design.md similarity index 100% rename from openspec/changes/restore-depth-surface-stretch-culling/design.md rename to openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/design.md diff --git a/openspec/changes/restore-depth-surface-stretch-culling/proposal.md b/openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/proposal.md similarity index 100% rename from openspec/changes/restore-depth-surface-stretch-culling/proposal.md rename to openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/proposal.md diff --git a/openspec/changes/restore-depth-surface-stretch-culling/specs/depth-surface-stretch-culling/spec.md b/openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/specs/depth-surface-stretch-culling/spec.md similarity index 100% rename from openspec/changes/restore-depth-surface-stretch-culling/specs/depth-surface-stretch-culling/spec.md rename to openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/specs/depth-surface-stretch-culling/spec.md diff --git a/openspec/changes/restore-depth-surface-stretch-culling/tasks.md b/openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/tasks.md similarity index 100% rename from openspec/changes/restore-depth-surface-stretch-culling/tasks.md rename to openspec/changes/archive/2026-09-17-restore-depth-surface-stretch-culling/tasks.md diff --git a/openspec/changes/reuse-conversion-source-images/.openspec.yaml b/openspec/changes/archive/2026-09-17-reuse-conversion-source-images/.openspec.yaml similarity index 100% rename from openspec/changes/reuse-conversion-source-images/.openspec.yaml rename to openspec/changes/archive/2026-09-17-reuse-conversion-source-images/.openspec.yaml diff --git a/openspec/changes/reuse-conversion-source-images/design.md b/openspec/changes/archive/2026-09-17-reuse-conversion-source-images/design.md similarity index 100% rename from openspec/changes/reuse-conversion-source-images/design.md rename to openspec/changes/archive/2026-09-17-reuse-conversion-source-images/design.md diff --git a/openspec/changes/reuse-conversion-source-images/proposal.md b/openspec/changes/archive/2026-09-17-reuse-conversion-source-images/proposal.md similarity index 100% rename from openspec/changes/reuse-conversion-source-images/proposal.md rename to openspec/changes/archive/2026-09-17-reuse-conversion-source-images/proposal.md diff --git a/openspec/changes/reuse-conversion-source-images/specs/conversion-source-images/spec.md b/openspec/changes/archive/2026-09-17-reuse-conversion-source-images/specs/conversion-source-images/spec.md similarity index 100% rename from openspec/changes/reuse-conversion-source-images/specs/conversion-source-images/spec.md rename to openspec/changes/archive/2026-09-17-reuse-conversion-source-images/specs/conversion-source-images/spec.md diff --git a/openspec/changes/reuse-conversion-source-images/tasks.md b/openspec/changes/archive/2026-09-17-reuse-conversion-source-images/tasks.md similarity index 100% rename from openspec/changes/reuse-conversion-source-images/tasks.md rename to openspec/changes/archive/2026-09-17-reuse-conversion-source-images/tasks.md diff --git a/openspec/changes/rework-plane-conversion/.openspec.yaml b/openspec/changes/archive/2026-09-17-rework-plane-conversion/.openspec.yaml similarity index 100% rename from openspec/changes/rework-plane-conversion/.openspec.yaml rename to openspec/changes/archive/2026-09-17-rework-plane-conversion/.openspec.yaml diff --git a/openspec/changes/rework-plane-conversion/design.md b/openspec/changes/archive/2026-09-17-rework-plane-conversion/design.md similarity index 100% rename from openspec/changes/rework-plane-conversion/design.md rename to openspec/changes/archive/2026-09-17-rework-plane-conversion/design.md diff --git a/openspec/changes/rework-plane-conversion/proposal.md b/openspec/changes/archive/2026-09-17-rework-plane-conversion/proposal.md similarity index 100% rename from openspec/changes/rework-plane-conversion/proposal.md rename to openspec/changes/archive/2026-09-17-rework-plane-conversion/proposal.md diff --git a/openspec/changes/rework-plane-conversion/specs/plane-conversion/spec.md b/openspec/changes/archive/2026-09-17-rework-plane-conversion/specs/plane-conversion/spec.md similarity index 100% rename from openspec/changes/rework-plane-conversion/specs/plane-conversion/spec.md rename to openspec/changes/archive/2026-09-17-rework-plane-conversion/specs/plane-conversion/spec.md diff --git a/openspec/changes/rework-plane-conversion/tasks.md b/openspec/changes/archive/2026-09-17-rework-plane-conversion/tasks.md similarity index 100% rename from openspec/changes/rework-plane-conversion/tasks.md rename to openspec/changes/archive/2026-09-17-rework-plane-conversion/tasks.md diff --git a/openspec/changes/scale-depth-split-by-depth-scale/.openspec.yaml b/openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/.openspec.yaml similarity index 100% rename from openspec/changes/scale-depth-split-by-depth-scale/.openspec.yaml rename to openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/.openspec.yaml diff --git a/openspec/changes/scale-depth-split-by-depth-scale/design.md b/openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/design.md similarity index 100% rename from openspec/changes/scale-depth-split-by-depth-scale/design.md rename to openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/design.md diff --git a/openspec/changes/scale-depth-split-by-depth-scale/proposal.md b/openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/proposal.md similarity index 100% rename from openspec/changes/scale-depth-split-by-depth-scale/proposal.md rename to openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/proposal.md diff --git a/openspec/changes/scale-depth-split-by-depth-scale/specs/depth-split-selection/spec.md b/openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/specs/depth-split-selection/spec.md similarity index 100% rename from openspec/changes/scale-depth-split-by-depth-scale/specs/depth-split-selection/spec.md rename to openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/specs/depth-split-selection/spec.md diff --git a/openspec/changes/scale-depth-split-by-depth-scale/tasks.md b/openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/tasks.md similarity index 100% rename from openspec/changes/scale-depth-split-by-depth-scale/tasks.md rename to openspec/changes/archive/2026-09-17-scale-depth-split-by-depth-scale/tasks.md diff --git a/openspec/changes/scale-tangent-normals-with-depth/.openspec.yaml b/openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/.openspec.yaml similarity index 100% rename from openspec/changes/scale-tangent-normals-with-depth/.openspec.yaml rename to openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/.openspec.yaml diff --git a/openspec/changes/scale-tangent-normals-with-depth/design.md b/openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/design.md similarity index 100% rename from openspec/changes/scale-tangent-normals-with-depth/design.md rename to openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/design.md diff --git a/openspec/changes/scale-tangent-normals-with-depth/proposal.md b/openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/proposal.md similarity index 100% rename from openspec/changes/scale-tangent-normals-with-depth/proposal.md rename to openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/proposal.md diff --git a/openspec/changes/scale-tangent-normals-with-depth/specs/depth-scaled-tangent-normals/spec.md b/openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/specs/depth-scaled-tangent-normals/spec.md similarity index 100% rename from openspec/changes/scale-tangent-normals-with-depth/specs/depth-scaled-tangent-normals/spec.md rename to openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/specs/depth-scaled-tangent-normals/spec.md diff --git a/openspec/changes/scale-tangent-normals-with-depth/tasks.md b/openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/tasks.md similarity index 100% rename from openspec/changes/scale-tangent-normals-with-depth/tasks.md rename to openspec/changes/archive/2026-09-17-scale-tangent-normals-with-depth/tasks.md diff --git a/openspec/changes/separate-depth-alpha-and-model-input/.openspec.yaml b/openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/.openspec.yaml similarity index 100% rename from openspec/changes/separate-depth-alpha-and-model-input/.openspec.yaml rename to openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/.openspec.yaml diff --git a/openspec/changes/separate-depth-alpha-and-model-input/design.md b/openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/design.md similarity index 100% rename from openspec/changes/separate-depth-alpha-and-model-input/design.md rename to openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/design.md diff --git a/openspec/changes/separate-depth-alpha-and-model-input/proposal.md b/openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/proposal.md similarity index 100% rename from openspec/changes/separate-depth-alpha-and-model-input/proposal.md rename to openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/proposal.md diff --git a/openspec/changes/separate-depth-alpha-and-model-input/specs/depth-alpha-semantics/spec.md b/openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/specs/depth-alpha-semantics/spec.md similarity index 100% rename from openspec/changes/separate-depth-alpha-and-model-input/specs/depth-alpha-semantics/spec.md rename to openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/specs/depth-alpha-semantics/spec.md diff --git a/openspec/changes/separate-depth-alpha-and-model-input/tasks.md b/openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/tasks.md similarity index 100% rename from openspec/changes/separate-depth-alpha-and-model-input/tasks.md rename to openspec/changes/archive/2026-09-17-separate-depth-alpha-and-model-input/tasks.md diff --git a/openspec/changes/separate-panorama-mask-regions/.openspec.yaml b/openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/.openspec.yaml similarity index 100% rename from openspec/changes/separate-panorama-mask-regions/.openspec.yaml rename to openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/.openspec.yaml diff --git a/openspec/changes/separate-panorama-mask-regions/design.md b/openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/design.md similarity index 100% rename from openspec/changes/separate-panorama-mask-regions/design.md rename to openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/design.md diff --git a/openspec/changes/separate-panorama-mask-regions/proposal.md b/openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/proposal.md similarity index 100% rename from openspec/changes/separate-panorama-mask-regions/proposal.md rename to openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/proposal.md diff --git a/openspec/changes/separate-panorama-mask-regions/specs/panorama-mask-regions/spec.md b/openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/specs/panorama-mask-regions/spec.md similarity index 100% rename from openspec/changes/separate-panorama-mask-regions/specs/panorama-mask-regions/spec.md rename to openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/specs/panorama-mask-regions/spec.md diff --git a/openspec/changes/separate-panorama-mask-regions/tasks.md b/openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/tasks.md similarity index 100% rename from openspec/changes/separate-panorama-mask-regions/tasks.md rename to openspec/changes/archive/2026-09-17-separate-panorama-mask-regions/tasks.md diff --git a/openspec/changes/separate-selection-from-image-content/.openspec.yaml b/openspec/changes/archive/2026-09-17-separate-selection-from-image-content/.openspec.yaml similarity index 100% rename from openspec/changes/separate-selection-from-image-content/.openspec.yaml rename to openspec/changes/archive/2026-09-17-separate-selection-from-image-content/.openspec.yaml diff --git a/openspec/changes/separate-selection-from-image-content/design.md b/openspec/changes/archive/2026-09-17-separate-selection-from-image-content/design.md similarity index 100% rename from openspec/changes/separate-selection-from-image-content/design.md rename to openspec/changes/archive/2026-09-17-separate-selection-from-image-content/design.md diff --git a/openspec/changes/separate-selection-from-image-content/proposal.md b/openspec/changes/archive/2026-09-17-separate-selection-from-image-content/proposal.md similarity index 100% rename from openspec/changes/separate-selection-from-image-content/proposal.md rename to openspec/changes/archive/2026-09-17-separate-selection-from-image-content/proposal.md diff --git a/openspec/changes/separate-selection-from-image-content/specs/selection-content-processing/spec.md b/openspec/changes/archive/2026-09-17-separate-selection-from-image-content/specs/selection-content-processing/spec.md similarity index 100% rename from openspec/changes/separate-selection-from-image-content/specs/selection-content-processing/spec.md rename to openspec/changes/archive/2026-09-17-separate-selection-from-image-content/specs/selection-content-processing/spec.md diff --git a/openspec/changes/separate-selection-from-image-content/tasks.md b/openspec/changes/archive/2026-09-17-separate-selection-from-image-content/tasks.md similarity index 100% rename from openspec/changes/separate-selection-from-image-content/tasks.md rename to openspec/changes/archive/2026-09-17-separate-selection-from-image-content/tasks.md diff --git a/openspec/changes/show-server-controls-while-running/.openspec.yaml b/openspec/changes/archive/2026-09-17-show-server-controls-while-running/.openspec.yaml similarity index 100% rename from openspec/changes/show-server-controls-while-running/.openspec.yaml rename to openspec/changes/archive/2026-09-17-show-server-controls-while-running/.openspec.yaml diff --git a/openspec/changes/show-server-controls-while-running/design.md b/openspec/changes/archive/2026-09-17-show-server-controls-while-running/design.md similarity index 100% rename from openspec/changes/show-server-controls-while-running/design.md rename to openspec/changes/archive/2026-09-17-show-server-controls-while-running/design.md diff --git a/openspec/changes/show-server-controls-while-running/proposal.md b/openspec/changes/archive/2026-09-17-show-server-controls-while-running/proposal.md similarity index 100% rename from openspec/changes/show-server-controls-while-running/proposal.md rename to openspec/changes/archive/2026-09-17-show-server-controls-while-running/proposal.md diff --git a/openspec/changes/show-server-controls-while-running/specs/server-cache-display/spec.md b/openspec/changes/archive/2026-09-17-show-server-controls-while-running/specs/server-cache-display/spec.md similarity index 100% rename from openspec/changes/show-server-controls-while-running/specs/server-cache-display/spec.md rename to openspec/changes/archive/2026-09-17-show-server-controls-while-running/specs/server-cache-display/spec.md diff --git a/openspec/changes/show-server-controls-while-running/specs/server-lifecycle-controls/spec.md b/openspec/changes/archive/2026-09-17-show-server-controls-while-running/specs/server-lifecycle-controls/spec.md similarity index 100% rename from openspec/changes/show-server-controls-while-running/specs/server-lifecycle-controls/spec.md rename to openspec/changes/archive/2026-09-17-show-server-controls-while-running/specs/server-lifecycle-controls/spec.md diff --git a/openspec/changes/show-server-controls-while-running/tasks.md b/openspec/changes/archive/2026-09-17-show-server-controls-while-running/tasks.md similarity index 100% rename from openspec/changes/show-server-controls-while-running/tasks.md rename to openspec/changes/archive/2026-09-17-show-server-controls-while-running/tasks.md diff --git a/openspec/changes/simplify-ai-model-tiers/.openspec.yaml b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/.openspec.yaml similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/.openspec.yaml rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/.openspec.yaml diff --git a/openspec/changes/simplify-ai-model-tiers/design.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/design.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/design.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/design.md diff --git a/openspec/changes/simplify-ai-model-tiers/mirror-validation.json b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/mirror-validation.json similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/mirror-validation.json rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/mirror-validation.json diff --git a/openspec/changes/simplify-ai-model-tiers/proposal.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/proposal.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/proposal.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/proposal.md diff --git a/openspec/changes/simplify-ai-model-tiers/retirement-results.json b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/retirement-results.json similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/retirement-results.json rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/retirement-results.json diff --git a/openspec/changes/simplify-ai-model-tiers/retirement.json b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/retirement.json similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/retirement.json rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/retirement.json diff --git a/openspec/changes/simplify-ai-model-tiers/specs/ai-model-tiers/spec.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/ai-model-tiers/spec.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/specs/ai-model-tiers/spec.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/ai-model-tiers/spec.md diff --git a/openspec/changes/simplify-ai-model-tiers/specs/background-removal-models/spec.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/background-removal-models/spec.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/specs/background-removal-models/spec.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/background-removal-models/spec.md diff --git a/openspec/changes/simplify-ai-model-tiers/specs/hat-upscale/spec.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/hat-upscale/spec.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/specs/hat-upscale/spec.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/hat-upscale/spec.md diff --git a/openspec/changes/simplify-ai-model-tiers/specs/model-asset-retirement/spec.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/model-asset-retirement/spec.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/specs/model-asset-retirement/spec.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/specs/model-asset-retirement/spec.md diff --git a/openspec/changes/simplify-ai-model-tiers/tasks.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/tasks.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/tasks.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/tasks.md diff --git a/openspec/changes/simplify-ai-model-tiers/validation.md b/openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/validation.md similarity index 100% rename from openspec/changes/simplify-ai-model-tiers/validation.md rename to openspec/changes/archive/2026-09-17-simplify-ai-model-tiers/validation.md diff --git a/openspec/changes/simplify-depth-group-options/.openspec.yaml b/openspec/changes/archive/2026-09-17-simplify-depth-group-options/.openspec.yaml similarity index 100% rename from openspec/changes/simplify-depth-group-options/.openspec.yaml rename to openspec/changes/archive/2026-09-17-simplify-depth-group-options/.openspec.yaml diff --git a/openspec/changes/simplify-depth-group-options/design.md b/openspec/changes/archive/2026-09-17-simplify-depth-group-options/design.md similarity index 100% rename from openspec/changes/simplify-depth-group-options/design.md rename to openspec/changes/archive/2026-09-17-simplify-depth-group-options/design.md diff --git a/openspec/changes/simplify-depth-group-options/proposal.md b/openspec/changes/archive/2026-09-17-simplify-depth-group-options/proposal.md similarity index 100% rename from openspec/changes/simplify-depth-group-options/proposal.md rename to openspec/changes/archive/2026-09-17-simplify-depth-group-options/proposal.md diff --git a/openspec/changes/simplify-depth-group-options/specs/depth-node-options/spec.md b/openspec/changes/archive/2026-09-17-simplify-depth-group-options/specs/depth-node-options/spec.md similarity index 100% rename from openspec/changes/simplify-depth-group-options/specs/depth-node-options/spec.md rename to openspec/changes/archive/2026-09-17-simplify-depth-group-options/specs/depth-node-options/spec.md diff --git a/openspec/changes/simplify-depth-group-options/tasks.md b/openspec/changes/archive/2026-09-17-simplify-depth-group-options/tasks.md similarity index 100% rename from openspec/changes/simplify-depth-group-options/tasks.md rename to openspec/changes/archive/2026-09-17-simplify-depth-group-options/tasks.md diff --git a/openspec/changes/simplify-node-attribute-flow/.openspec.yaml b/openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/.openspec.yaml similarity index 100% rename from openspec/changes/simplify-node-attribute-flow/.openspec.yaml rename to openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/.openspec.yaml diff --git a/openspec/changes/simplify-node-attribute-flow/design.md b/openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/design.md similarity index 100% rename from openspec/changes/simplify-node-attribute-flow/design.md rename to openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/design.md diff --git a/openspec/changes/simplify-node-attribute-flow/proposal.md b/openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/proposal.md similarity index 100% rename from openspec/changes/simplify-node-attribute-flow/proposal.md rename to openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/proposal.md diff --git a/openspec/changes/simplify-node-attribute-flow/tasks.md b/openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/tasks.md similarity index 100% rename from openspec/changes/simplify-node-attribute-flow/tasks.md rename to openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/tasks.md diff --git a/openspec/changes/simplify-node-attribute-flow/validation.md b/openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/validation.md similarity index 100% rename from openspec/changes/simplify-node-attribute-flow/validation.md rename to openspec/changes/archive/2026-09-17-simplify-node-attribute-flow/validation.md diff --git a/openspec/changes/simplify-selection-rasterization/.openspec.yaml b/openspec/changes/archive/2026-09-17-simplify-selection-rasterization/.openspec.yaml similarity index 100% rename from openspec/changes/simplify-selection-rasterization/.openspec.yaml rename to openspec/changes/archive/2026-09-17-simplify-selection-rasterization/.openspec.yaml diff --git a/openspec/changes/simplify-selection-rasterization/design.md b/openspec/changes/archive/2026-09-17-simplify-selection-rasterization/design.md similarity index 100% rename from openspec/changes/simplify-selection-rasterization/design.md rename to openspec/changes/archive/2026-09-17-simplify-selection-rasterization/design.md diff --git a/openspec/changes/simplify-selection-rasterization/proposal.md b/openspec/changes/archive/2026-09-17-simplify-selection-rasterization/proposal.md similarity index 100% rename from openspec/changes/simplify-selection-rasterization/proposal.md rename to openspec/changes/archive/2026-09-17-simplify-selection-rasterization/proposal.md diff --git a/openspec/changes/simplify-selection-rasterization/specs/selection-rasterization/spec.md b/openspec/changes/archive/2026-09-17-simplify-selection-rasterization/specs/selection-rasterization/spec.md similarity index 100% rename from openspec/changes/simplify-selection-rasterization/specs/selection-rasterization/spec.md rename to openspec/changes/archive/2026-09-17-simplify-selection-rasterization/specs/selection-rasterization/spec.md diff --git a/openspec/changes/simplify-selection-rasterization/tasks.md b/openspec/changes/archive/2026-09-17-simplify-selection-rasterization/tasks.md similarity index 100% rename from openspec/changes/simplify-selection-rasterization/tasks.md rename to openspec/changes/archive/2026-09-17-simplify-selection-rasterization/tasks.md diff --git a/openspec/changes/split-depth-and-relief-planes/.openspec.yaml b/openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/.openspec.yaml similarity index 100% rename from openspec/changes/split-depth-and-relief-planes/.openspec.yaml rename to openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/.openspec.yaml diff --git a/openspec/changes/split-depth-and-relief-planes/design.md b/openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/design.md similarity index 100% rename from openspec/changes/split-depth-and-relief-planes/design.md rename to openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/design.md diff --git a/openspec/changes/split-depth-and-relief-planes/proposal.md b/openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/proposal.md similarity index 100% rename from openspec/changes/split-depth-and-relief-planes/proposal.md rename to openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/proposal.md diff --git a/openspec/changes/split-depth-and-relief-planes/specs/depth-plane-alpha-culling/spec.md b/openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/specs/depth-plane-alpha-culling/spec.md similarity index 100% rename from openspec/changes/split-depth-and-relief-planes/specs/depth-plane-alpha-culling/spec.md rename to openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/specs/depth-plane-alpha-culling/spec.md diff --git a/openspec/changes/split-depth-and-relief-planes/specs/separate-depth-relief-planes/spec.md b/openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/specs/separate-depth-relief-planes/spec.md similarity index 100% rename from openspec/changes/split-depth-and-relief-planes/specs/separate-depth-relief-planes/spec.md rename to openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/specs/separate-depth-relief-planes/spec.md diff --git a/openspec/changes/split-depth-and-relief-planes/tasks.md b/openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/tasks.md similarity index 100% rename from openspec/changes/split-depth-and-relief-planes/tasks.md rename to openspec/changes/archive/2026-09-17-split-depth-and-relief-planes/tasks.md diff --git a/openspec/changes/standardize-blender-object-coordinates/.openspec.yaml b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/.openspec.yaml similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/.openspec.yaml rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/.openspec.yaml diff --git a/openspec/changes/standardize-blender-object-coordinates/design.md b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/design.md similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/design.md rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/design.md diff --git a/openspec/changes/standardize-blender-object-coordinates/proposal.md b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/proposal.md similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/proposal.md rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/proposal.md diff --git a/openspec/changes/standardize-blender-object-coordinates/specs/canonical-object-frame/spec.md b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/specs/canonical-object-frame/spec.md similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/specs/canonical-object-frame/spec.md rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/specs/canonical-object-frame/spec.md diff --git a/openspec/changes/standardize-blender-object-coordinates/specs/depth-symmetry-orientation/spec.md b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/specs/depth-symmetry-orientation/spec.md similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/specs/depth-symmetry-orientation/spec.md rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/specs/depth-symmetry-orientation/spec.md diff --git a/openspec/changes/standardize-blender-object-coordinates/specs/object-space-normal-alignment/spec.md b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/specs/object-space-normal-alignment/spec.md similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/specs/object-space-normal-alignment/spec.md rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/specs/object-space-normal-alignment/spec.md diff --git a/openspec/changes/standardize-blender-object-coordinates/tasks.md b/openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/tasks.md similarity index 100% rename from openspec/changes/standardize-blender-object-coordinates/tasks.md rename to openspec/changes/archive/2026-09-17-standardize-blender-object-coordinates/tasks.md diff --git a/openspec/changes/standardize-cutout-z-axis/.openspec.yaml b/openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/.openspec.yaml similarity index 100% rename from openspec/changes/standardize-cutout-z-axis/.openspec.yaml rename to openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/.openspec.yaml diff --git a/openspec/changes/standardize-cutout-z-axis/design.md b/openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/design.md similarity index 100% rename from openspec/changes/standardize-cutout-z-axis/design.md rename to openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/design.md diff --git a/openspec/changes/standardize-cutout-z-axis/proposal.md b/openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/proposal.md similarity index 100% rename from openspec/changes/standardize-cutout-z-axis/proposal.md rename to openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/proposal.md diff --git a/openspec/changes/standardize-cutout-z-axis/specs/cutout-axis-convention/spec.md b/openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/specs/cutout-axis-convention/spec.md similarity index 100% rename from openspec/changes/standardize-cutout-z-axis/specs/cutout-axis-convention/spec.md rename to openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/specs/cutout-axis-convention/spec.md diff --git a/openspec/changes/standardize-cutout-z-axis/tasks.md b/openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/tasks.md similarity index 100% rename from openspec/changes/standardize-cutout-z-axis/tasks.md rename to openspec/changes/archive/2026-09-17-standardize-cutout-z-axis/tasks.md diff --git a/openspec/changes/support-frame-view-plane-intersections/.openspec.yaml b/openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/.openspec.yaml similarity index 100% rename from openspec/changes/support-frame-view-plane-intersections/.openspec.yaml rename to openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/.openspec.yaml diff --git a/openspec/changes/support-frame-view-plane-intersections/design.md b/openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/design.md similarity index 100% rename from openspec/changes/support-frame-view-plane-intersections/design.md rename to openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/design.md diff --git a/openspec/changes/support-frame-view-plane-intersections/proposal.md b/openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/proposal.md similarity index 100% rename from openspec/changes/support-frame-view-plane-intersections/proposal.md rename to openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/proposal.md diff --git a/openspec/changes/support-frame-view-plane-intersections/specs/frame-tool/spec.md b/openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/specs/frame-tool/spec.md similarity index 100% rename from openspec/changes/support-frame-view-plane-intersections/specs/frame-tool/spec.md rename to openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/specs/frame-tool/spec.md diff --git a/openspec/changes/support-frame-view-plane-intersections/tasks.md b/openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/tasks.md similarity index 100% rename from openspec/changes/support-frame-view-plane-intersections/tasks.md rename to openspec/changes/archive/2026-09-17-support-frame-view-plane-intersections/tasks.md diff --git a/openspec/changes/unify-ai-input-validation/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-ai-input-validation/.openspec.yaml similarity index 100% rename from openspec/changes/unify-ai-input-validation/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-ai-input-validation/.openspec.yaml diff --git a/openspec/changes/unify-ai-input-validation/design.md b/openspec/changes/archive/2026-09-17-unify-ai-input-validation/design.md similarity index 100% rename from openspec/changes/unify-ai-input-validation/design.md rename to openspec/changes/archive/2026-09-17-unify-ai-input-validation/design.md diff --git a/openspec/changes/unify-ai-input-validation/proposal.md b/openspec/changes/archive/2026-09-17-unify-ai-input-validation/proposal.md similarity index 100% rename from openspec/changes/unify-ai-input-validation/proposal.md rename to openspec/changes/archive/2026-09-17-unify-ai-input-validation/proposal.md diff --git a/openspec/changes/unify-ai-input-validation/specs/ai-input-validation/spec.md b/openspec/changes/archive/2026-09-17-unify-ai-input-validation/specs/ai-input-validation/spec.md similarity index 100% rename from openspec/changes/unify-ai-input-validation/specs/ai-input-validation/spec.md rename to openspec/changes/archive/2026-09-17-unify-ai-input-validation/specs/ai-input-validation/spec.md diff --git a/openspec/changes/unify-ai-input-validation/specs/ai-operator-failure-handling/spec.md b/openspec/changes/archive/2026-09-17-unify-ai-input-validation/specs/ai-operator-failure-handling/spec.md similarity index 100% rename from openspec/changes/unify-ai-input-validation/specs/ai-operator-failure-handling/spec.md rename to openspec/changes/archive/2026-09-17-unify-ai-input-validation/specs/ai-operator-failure-handling/spec.md diff --git a/openspec/changes/unify-ai-input-validation/tasks.md b/openspec/changes/archive/2026-09-17-unify-ai-input-validation/tasks.md similarity index 100% rename from openspec/changes/unify-ai-input-validation/tasks.md rename to openspec/changes/archive/2026-09-17-unify-ai-input-validation/tasks.md diff --git a/openspec/changes/unify-boundary-depth-smoothing/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/.openspec.yaml similarity index 100% rename from openspec/changes/unify-boundary-depth-smoothing/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/.openspec.yaml diff --git a/openspec/changes/unify-boundary-depth-smoothing/design.md b/openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/design.md similarity index 100% rename from openspec/changes/unify-boundary-depth-smoothing/design.md rename to openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/design.md diff --git a/openspec/changes/unify-boundary-depth-smoothing/proposal.md b/openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/proposal.md similarity index 100% rename from openspec/changes/unify-boundary-depth-smoothing/proposal.md rename to openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/proposal.md diff --git a/openspec/changes/unify-boundary-depth-smoothing/specs/boundary-depth-smoothing/spec.md b/openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/specs/boundary-depth-smoothing/spec.md similarity index 100% rename from openspec/changes/unify-boundary-depth-smoothing/specs/boundary-depth-smoothing/spec.md rename to openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/specs/boundary-depth-smoothing/spec.md diff --git a/openspec/changes/unify-boundary-depth-smoothing/tasks.md b/openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/tasks.md similarity index 100% rename from openspec/changes/unify-boundary-depth-smoothing/tasks.md rename to openspec/changes/archive/2026-09-17-unify-boundary-depth-smoothing/tasks.md diff --git a/openspec/changes/unify-depth-plane-and-artifact/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/.openspec.yaml similarity index 100% rename from openspec/changes/unify-depth-plane-and-artifact/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/.openspec.yaml diff --git a/openspec/changes/unify-depth-plane-and-artifact/design.md b/openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/design.md similarity index 100% rename from openspec/changes/unify-depth-plane-and-artifact/design.md rename to openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/design.md diff --git a/openspec/changes/unify-depth-plane-and-artifact/proposal.md b/openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/proposal.md similarity index 100% rename from openspec/changes/unify-depth-plane-and-artifact/proposal.md rename to openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/proposal.md diff --git a/openspec/changes/unify-depth-plane-and-artifact/specs/depth-artifact-contract/spec.md b/openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/specs/depth-artifact-contract/spec.md similarity index 100% rename from openspec/changes/unify-depth-plane-and-artifact/specs/depth-artifact-contract/spec.md rename to openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/specs/depth-artifact-contract/spec.md diff --git a/openspec/changes/unify-depth-plane-and-artifact/specs/unified-depth-plane/spec.md b/openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/specs/unified-depth-plane/spec.md similarity index 100% rename from openspec/changes/unify-depth-plane-and-artifact/specs/unified-depth-plane/spec.md rename to openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/specs/unified-depth-plane/spec.md diff --git a/openspec/changes/unify-depth-plane-and-artifact/tasks.md b/openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/tasks.md similarity index 100% rename from openspec/changes/unify-depth-plane-and-artifact/tasks.md rename to openspec/changes/archive/2026-09-17-unify-depth-plane-and-artifact/tasks.md diff --git a/openspec/changes/unify-mask-cut-depth/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-mask-cut-depth/.openspec.yaml similarity index 100% rename from openspec/changes/unify-mask-cut-depth/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-mask-cut-depth/.openspec.yaml diff --git a/openspec/changes/unify-mask-cut-depth/design.md b/openspec/changes/archive/2026-09-17-unify-mask-cut-depth/design.md similarity index 100% rename from openspec/changes/unify-mask-cut-depth/design.md rename to openspec/changes/archive/2026-09-17-unify-mask-cut-depth/design.md diff --git a/openspec/changes/unify-mask-cut-depth/proposal.md b/openspec/changes/archive/2026-09-17-unify-mask-cut-depth/proposal.md similarity index 100% rename from openspec/changes/unify-mask-cut-depth/proposal.md rename to openspec/changes/archive/2026-09-17-unify-mask-cut-depth/proposal.md diff --git a/openspec/changes/unify-mask-cut-depth/specs/mask-cut-depth-sampling/spec.md b/openspec/changes/archive/2026-09-17-unify-mask-cut-depth/specs/mask-cut-depth-sampling/spec.md similarity index 100% rename from openspec/changes/unify-mask-cut-depth/specs/mask-cut-depth-sampling/spec.md rename to openspec/changes/archive/2026-09-17-unify-mask-cut-depth/specs/mask-cut-depth-sampling/spec.md diff --git a/openspec/changes/unify-mask-cut-depth/tasks.md b/openspec/changes/archive/2026-09-17-unify-mask-cut-depth/tasks.md similarity index 100% rename from openspec/changes/unify-mask-cut-depth/tasks.md rename to openspec/changes/archive/2026-09-17-unify-mask-cut-depth/tasks.md diff --git a/openspec/changes/unify-material-color-images/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-material-color-images/.openspec.yaml similarity index 100% rename from openspec/changes/unify-material-color-images/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-material-color-images/.openspec.yaml diff --git a/openspec/changes/unify-material-color-images/design.md b/openspec/changes/archive/2026-09-17-unify-material-color-images/design.md similarity index 100% rename from openspec/changes/unify-material-color-images/design.md rename to openspec/changes/archive/2026-09-17-unify-material-color-images/design.md diff --git a/openspec/changes/unify-material-color-images/proposal.md b/openspec/changes/archive/2026-09-17-unify-material-color-images/proposal.md similarity index 100% rename from openspec/changes/unify-material-color-images/proposal.md rename to openspec/changes/archive/2026-09-17-unify-material-color-images/proposal.md diff --git a/openspec/changes/unify-material-color-images/specs/clipboard-image-cache/spec.md b/openspec/changes/archive/2026-09-17-unify-material-color-images/specs/clipboard-image-cache/spec.md similarity index 100% rename from openspec/changes/unify-material-color-images/specs/clipboard-image-cache/spec.md rename to openspec/changes/archive/2026-09-17-unify-material-color-images/specs/clipboard-image-cache/spec.md diff --git a/openspec/changes/unify-material-color-images/specs/material-color-images/spec.md b/openspec/changes/archive/2026-09-17-unify-material-color-images/specs/material-color-images/spec.md similarity index 100% rename from openspec/changes/unify-material-color-images/specs/material-color-images/spec.md rename to openspec/changes/archive/2026-09-17-unify-material-color-images/specs/material-color-images/spec.md diff --git a/openspec/changes/unify-material-color-images/tasks.md b/openspec/changes/archive/2026-09-17-unify-material-color-images/tasks.md similarity index 100% rename from openspec/changes/unify-material-color-images/tasks.md rename to openspec/changes/archive/2026-09-17-unify-material-color-images/tasks.md diff --git a/openspec/changes/unify-reference-depth/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-reference-depth/.openspec.yaml similarity index 100% rename from openspec/changes/unify-reference-depth/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-reference-depth/.openspec.yaml diff --git a/openspec/changes/unify-reference-depth/design.md b/openspec/changes/archive/2026-09-17-unify-reference-depth/design.md similarity index 100% rename from openspec/changes/unify-reference-depth/design.md rename to openspec/changes/archive/2026-09-17-unify-reference-depth/design.md diff --git a/openspec/changes/unify-reference-depth/proposal.md b/openspec/changes/archive/2026-09-17-unify-reference-depth/proposal.md similarity index 100% rename from openspec/changes/unify-reference-depth/proposal.md rename to openspec/changes/archive/2026-09-17-unify-reference-depth/proposal.md diff --git a/openspec/changes/unify-reference-depth/specs/reference-depth-calibration/spec.md b/openspec/changes/archive/2026-09-17-unify-reference-depth/specs/reference-depth-calibration/spec.md similarity index 100% rename from openspec/changes/unify-reference-depth/specs/reference-depth-calibration/spec.md rename to openspec/changes/archive/2026-09-17-unify-reference-depth/specs/reference-depth-calibration/spec.md diff --git a/openspec/changes/unify-reference-depth/tasks.md b/openspec/changes/archive/2026-09-17-unify-reference-depth/tasks.md similarity index 100% rename from openspec/changes/unify-reference-depth/tasks.md rename to openspec/changes/archive/2026-09-17-unify-reference-depth/tasks.md diff --git a/openspec/changes/unify-selection-terminology/.openspec.yaml b/openspec/changes/archive/2026-09-17-unify-selection-terminology/.openspec.yaml similarity index 100% rename from openspec/changes/unify-selection-terminology/.openspec.yaml rename to openspec/changes/archive/2026-09-17-unify-selection-terminology/.openspec.yaml diff --git a/openspec/changes/unify-selection-terminology/design.md b/openspec/changes/archive/2026-09-17-unify-selection-terminology/design.md similarity index 100% rename from openspec/changes/unify-selection-terminology/design.md rename to openspec/changes/archive/2026-09-17-unify-selection-terminology/design.md diff --git a/openspec/changes/unify-selection-terminology/proposal.md b/openspec/changes/archive/2026-09-17-unify-selection-terminology/proposal.md similarity index 100% rename from openspec/changes/unify-selection-terminology/proposal.md rename to openspec/changes/archive/2026-09-17-unify-selection-terminology/proposal.md diff --git a/openspec/changes/unify-selection-terminology/specs/image-selection/spec.md b/openspec/changes/archive/2026-09-17-unify-selection-terminology/specs/image-selection/spec.md similarity index 100% rename from openspec/changes/unify-selection-terminology/specs/image-selection/spec.md rename to openspec/changes/archive/2026-09-17-unify-selection-terminology/specs/image-selection/spec.md diff --git a/openspec/changes/unify-selection-terminology/tasks.md b/openspec/changes/archive/2026-09-17-unify-selection-terminology/tasks.md similarity index 100% rename from openspec/changes/unify-selection-terminology/tasks.md rename to openspec/changes/archive/2026-09-17-unify-selection-terminology/tasks.md diff --git a/openspec/changes/update-pasted-brush-preview/.openspec.yaml b/openspec/changes/archive/2026-09-17-update-pasted-brush-preview/.openspec.yaml similarity index 100% rename from openspec/changes/update-pasted-brush-preview/.openspec.yaml rename to openspec/changes/archive/2026-09-17-update-pasted-brush-preview/.openspec.yaml diff --git a/openspec/changes/update-pasted-brush-preview/design.md b/openspec/changes/archive/2026-09-17-update-pasted-brush-preview/design.md similarity index 100% rename from openspec/changes/update-pasted-brush-preview/design.md rename to openspec/changes/archive/2026-09-17-update-pasted-brush-preview/design.md diff --git a/openspec/changes/update-pasted-brush-preview/proposal.md b/openspec/changes/archive/2026-09-17-update-pasted-brush-preview/proposal.md similarity index 100% rename from openspec/changes/update-pasted-brush-preview/proposal.md rename to openspec/changes/archive/2026-09-17-update-pasted-brush-preview/proposal.md diff --git a/openspec/changes/update-pasted-brush-preview/specs/pasted-brush-preview/spec.md b/openspec/changes/archive/2026-09-17-update-pasted-brush-preview/specs/pasted-brush-preview/spec.md similarity index 100% rename from openspec/changes/update-pasted-brush-preview/specs/pasted-brush-preview/spec.md rename to openspec/changes/archive/2026-09-17-update-pasted-brush-preview/specs/pasted-brush-preview/spec.md diff --git a/openspec/changes/update-pasted-brush-preview/tasks.md b/openspec/changes/archive/2026-09-17-update-pasted-brush-preview/tasks.md similarity index 100% rename from openspec/changes/update-pasted-brush-preview/tasks.md rename to openspec/changes/archive/2026-09-17-update-pasted-brush-preview/tasks.md diff --git a/openspec/changes/use-tangle-cutout-meshing/.openspec.yaml b/openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/.openspec.yaml similarity index 100% rename from openspec/changes/use-tangle-cutout-meshing/.openspec.yaml rename to openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/.openspec.yaml diff --git a/openspec/changes/use-tangle-cutout-meshing/design.md b/openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/design.md similarity index 100% rename from openspec/changes/use-tangle-cutout-meshing/design.md rename to openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/design.md diff --git a/openspec/changes/use-tangle-cutout-meshing/proposal.md b/openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/proposal.md similarity index 100% rename from openspec/changes/use-tangle-cutout-meshing/proposal.md rename to openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/proposal.md diff --git a/openspec/changes/use-tangle-cutout-meshing/specs/cutout-quality-meshing/spec.md b/openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/specs/cutout-quality-meshing/spec.md similarity index 100% rename from openspec/changes/use-tangle-cutout-meshing/specs/cutout-quality-meshing/spec.md rename to openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/specs/cutout-quality-meshing/spec.md diff --git a/openspec/changes/use-tangle-cutout-meshing/tasks.md b/openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/tasks.md similarity index 100% rename from openspec/changes/use-tangle-cutout-meshing/tasks.md rename to openspec/changes/archive/2026-09-17-use-tangle-cutout-meshing/tasks.md diff --git a/openspec/changes/use-world-space-cutout-edge-length/.openspec.yaml b/openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/.openspec.yaml similarity index 100% rename from openspec/changes/use-world-space-cutout-edge-length/.openspec.yaml rename to openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/.openspec.yaml diff --git a/openspec/changes/use-world-space-cutout-edge-length/design.md b/openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/design.md similarity index 100% rename from openspec/changes/use-world-space-cutout-edge-length/design.md rename to openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/design.md diff --git a/openspec/changes/use-world-space-cutout-edge-length/proposal.md b/openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/proposal.md similarity index 100% rename from openspec/changes/use-world-space-cutout-edge-length/proposal.md rename to openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/proposal.md diff --git a/openspec/changes/use-world-space-cutout-edge-length/specs/cutout-edge-length/spec.md b/openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/specs/cutout-edge-length/spec.md similarity index 100% rename from openspec/changes/use-world-space-cutout-edge-length/specs/cutout-edge-length/spec.md rename to openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/specs/cutout-edge-length/spec.md diff --git a/openspec/changes/use-world-space-cutout-edge-length/tasks.md b/openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/tasks.md similarity index 100% rename from openspec/changes/use-world-space-cutout-edge-length/tasks.md rename to openspec/changes/archive/2026-09-17-use-world-space-cutout-edge-length/tasks.md diff --git a/openspec/changes/weight-cutout-boundary-smoothing/.openspec.yaml b/openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/.openspec.yaml similarity index 100% rename from openspec/changes/weight-cutout-boundary-smoothing/.openspec.yaml rename to openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/.openspec.yaml diff --git a/openspec/changes/weight-cutout-boundary-smoothing/design.md b/openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/design.md similarity index 100% rename from openspec/changes/weight-cutout-boundary-smoothing/design.md rename to openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/design.md diff --git a/openspec/changes/weight-cutout-boundary-smoothing/proposal.md b/openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/proposal.md similarity index 100% rename from openspec/changes/weight-cutout-boundary-smoothing/proposal.md rename to openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/proposal.md diff --git a/openspec/changes/weight-cutout-boundary-smoothing/specs/cutout-boundary-smoothing/spec.md b/openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/specs/cutout-boundary-smoothing/spec.md similarity index 100% rename from openspec/changes/weight-cutout-boundary-smoothing/specs/cutout-boundary-smoothing/spec.md rename to openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/specs/cutout-boundary-smoothing/spec.md diff --git a/openspec/changes/weight-cutout-boundary-smoothing/tasks.md b/openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/tasks.md similarity index 100% rename from openspec/changes/weight-cutout-boundary-smoothing/tasks.md rename to openspec/changes/archive/2026-09-17-weight-cutout-boundary-smoothing/tasks.md diff --git a/openspec/specs/ai-input-validation/spec.md b/openspec/specs/ai-input-validation/spec.md new file mode 100644 index 0000000..7fd36a3 --- /dev/null +++ b/openspec/specs/ai-input-validation/spec.md @@ -0,0 +1,65 @@ +# ai-input-validation Specification + +## Purpose +TBD - created by archiving change unify-ai-input-validation. Update Purpose after archive. +## Requirements +### Requirement: Shared input format validation + +AI 输入 SHALL 使用公共格式规则,在 Blender 提交前与后端处理前校验实际输入路径。支持图片为 JPG/JPEG/PNG/WEBP,视频沿用当前 MP4/MOV/AVI/MKV/WEBM/M4V/MTS/MPG/MPEG,扩展名匹配忽略大小写。 + +#### Scenario: Unsupported file in image edits +- **WHEN** Remove BG 与 Upscale 使用同一个存在的 `.tiff` 普通文件,且环境和模型已就绪 +- **THEN** 两者在提交 Job 前取消,展示相同格式提示,包含 `.tiff` 和分类排序的支持格式 + +#### Scenario: Direct backend input +- **WHEN** 后端直接收到不支持格式的文件或无扩展名文件 +- **THEN** 使用与前端相同的格式提示规则,无扩展名显示 `(no extension)` + +#### Scenario: Supported uppercase extension +- **WHEN** 实际输入为存在的 `.PNG` 或 `.MP4` 文件 +- **THEN** 通过对应图片或视频格式校验 + +#### Scenario: Model image validation +- **WHEN** BEN2 单帧入口收到不支持的图片扩展名 +- **THEN** 使用公共格式提示的图片子集,并与公共图片支持集合一致 + +### Requirement: Distinct input failure reasons + +系统 SHALL 区分缺失路径、不支持格式、目录无有效帧和解码失败;提示 SHALL 适用于 Image Empty 和 Image Texture,解码异常转换 SHALL 限定于读取边界。 + +#### Scenario: Missing source file +- **WHEN** Image Empty 或材质 Image Texture 的源图片或视频文件不存在 +- **THEN** 提示缺失的 source image/video file 及路径,不将原因报告为格式不支持 + +#### Scenario: Invalid encoded data +- **WHEN** 支持扩展名的文件内容无法作为对应图片或视频解码 +- **THEN** 报告无法读取的媒体类型及路径,并保留底层异常供诊断 + +### Requirement: Explicit frame selection + +系统 SHALL 在复制 Blender 序列前校验图片格式;通用目录 SHALL 仅收集支持的图片,保持现有排序及帧范围规则。 + +#### Scenario: Unsupported Blender sequence +- **WHEN** Blender 源序列使用 `.exr` 扩展名 +- **THEN** 复制前提示不支持的图片格式,不创建临时帧目录 + +#### Scenario: Mixed directory +- **WHEN** 通用输入目录包含 JPG、PNG、TXT 和 EXR 文件 +- **THEN** 仅 JPG 与 PNG 参与处理,前后端按相同规则选择并保持原帧范围语义 + +#### Scenario: Empty supported frame set +- **WHEN** 通用目录为空或只有不支持的文件 +- **THEN** 提示无支持的图片帧、目录路径及支持的图片格式 + +### Requirement: Validate prepared media + +系统 SHALL 校验实际提交的文件,保持各功能自身的媒体类型、尺寸和比例约束。 + +#### Scenario: Packed image exported as PNG +- **WHEN** 一个名称带 `.exr` 的 packed Image 经现有流程成功导出 PNG +- **THEN** 按导出的 PNG 校验,不按源名称拒绝 + +#### Scenario: Feature restriction +- **WHEN** 输入通过公共格式校验但不满足 Depth Plane 的静态图限制或 Upscale 的尺寸限制 +- **THEN** 仍按对应业务原因取消,不报告为格式不支持 + diff --git a/openspec/specs/ai-model-tiers/spec.md b/openspec/specs/ai-model-tiers/spec.md new file mode 100644 index 0000000..0552742 --- /dev/null +++ b/openspec/specs/ai-model-tiers/spec.md @@ -0,0 +1,42 @@ +# ai-model-tiers Specification + +## Purpose +TBD - created by archiving change simplify-ai-model-tiers. Update Purpose after archive. +## Requirements +### Requirement: 三类模型采用固定三档集合 +系统 SHALL 按以下顺序提供九款正式模型:Depth 为 MoGe-2 ViT-S Normal、MoGe-2 ViT-B Normal、MoGe-3 ViT-L;Upscale 为 Real-ESRGAN General WDN x4v3、Real-ESRGAN x4plus、Real HAT GAN x4 Sharper;Background 为 BiRefNet Lite、BEN2、BiRefNet HR-matting。各类别第一项 SHALL 为新偏好的默认值。 + +#### Scenario: 查看模型选项 +- **WHEN** 用户查看偏好或模型下载列表 +- **THEN** 每个类别恰好显示上述三款模型,顺序一致,模型名、用途、许可证和下载体积对应实际资源 + +#### Scenario: 服务离线时显示目录 +- **WHEN** 服务尚未启动而界面需要展示模型 +- **THEN** 本地目录与服务启动后返回的正式模型集合一致 + +### Requirement: 首次安装仅要求三个默认模型 +系统 SHALL 将 MoGe-2 ViT-S Normal、General WDN x4v3 和 BiRefNet Lite 作为默认环境初始化所需的模型集合,其他模型按用户选择下载。 + +#### Scenario: 新安装完成初始化 +- **WHEN** 推理环境和三个默认模型都已就绪,其他六款尚未下载 +- **THEN** AI 初始化状态为就绪,默认功能可运行 + +#### Scenario: 选择未下载的高档模型 +- **WHEN** 用户执行功能时选中的非默认模型尚未就绪 +- **THEN** 系统指出需要下载该模型,不使用其他模型替代本次请求 + +### Requirement: 有效偏好保留且退役选择明确处理 +系统 SHALL 保留仍属于所选类别的持久化模型键,并将退役或无效的偏好值归一为类别默认值;服务收到未知或退役模型键时 SHALL 返回明确错误。 + +#### Scenario: 用户已有 BEN2 偏好 +- **WHEN** 更新后读取有效的 BEN2 背景移除选择 +- **THEN** 继续选择 BEN2 + +#### Scenario: 用户已有 PiSA-SR 偏好 +- **WHEN** 更新后读取已退役的 PiSA-SR 超分选择 +- **THEN** 当前有效选择为 General WDN x4v3,界面与后续请求一致 + +#### Scenario: 旧请求直接指定退役模型 +- **WHEN** 服务收到指定 DA3、MoGe-2 L、SAFMN 或 PiSA-SR 的请求 +- **THEN** 返回未知或不可用模型错误,不加载或下载退役资源 + diff --git a/openspec/specs/ai-operator-failure-handling/spec.md b/openspec/specs/ai-operator-failure-handling/spec.md new file mode 100644 index 0000000..2db1245 --- /dev/null +++ b/openspec/specs/ai-operator-failure-handling/spec.md @@ -0,0 +1,37 @@ +# ai-operator-failure-handling Specification + +## Purpose +TBD - created by archiving change unify-ai-input-validation. Update Purpose after archive. +## Requirements +### Requirement: Expected operator failures are reported normally + +AI 操作 SHALL 将输入准备与嵌套 Operator 调用的预期失败转换为普通错误提示和 CANCELLED,包括 Remove BG、Depth/Relief Plane、AI Environment 准备及现有 Panorama 入口。提示 SHALL 保留原因并避免重复的 Blender 错误前缀。 + +#### Scenario: Nested input validation failure +- **WHEN** 内部 Job Operator 报告无效输入且 bpy.ops 抛出 RuntimeError +- **THEN** 外部入口返回 CANCELLED,显示原因,不向用户展示未捕获的 Python traceback + +#### Scenario: Environment setup failure +- **WHEN** AI Environment 设置入口调用安装或下载 Operator 同步失败 +- **THEN** 设置入口及调用它的 AI 操作按普通错误取消 + +#### Scenario: Successful job startup +- **WHEN** 内部 Job Operator 返回 RUNNING_MODAL +- **THEN** 外部入口保持现有成功返回与异步处理语义,源图片及 Undo 行为保持有效 + +### Requirement: Temporary inputs have explicit ownership + +系统 SHALL 释放本次操作创建的临时输入,在准备失败、启动失败、取消及异步结束时均有负责清理的边界;用户原文件 SHALL 保持不变。 + +#### Scenario: Preparation failure +- **WHEN** packed 图片导出或序列复制失败 +- **THEN** 准备函数删除本次临时文件和目录,并保留源图片与源文件 + +#### Scenario: Synchronous nested failure +- **WHEN** 临时输入准备成功后内部 Operator 抛错或返回 CANCELLED +- **THEN** 临时输入被清理,外部操作取消,源图片引用保持不变 + +#### Scenario: Asynchronous completion +- **WHEN** Job 接管临时输入并在之后成功、失败或取消 +- **THEN** 输入在 Job 结束时清理,运行期间保持可用,用户原文件不被删除 + diff --git a/openspec/specs/background-removal-models/spec.md b/openspec/specs/background-removal-models/spec.md new file mode 100644 index 0000000..46be9c8 --- /dev/null +++ b/openspec/specs/background-removal-models/spec.md @@ -0,0 +1,59 @@ +# background-removal-models Specification + +## Purpose +TBD - created by archiving change simplify-ai-model-tiers. Update Purpose after archive. +## Requirements +### Requirement: 背景模型选择贯穿真实执行 +系统 SHALL 使用当前选择的 BiRefNet Lite、BEN2 或 BiRefNet HR-matting 执行所有支持的背景移除输入路径,模型名称 SHALL 与下载、进度和实际加载的资源一致。 + +#### Scenario: 切换到精细抠图 +- **WHEN** 用户选择已经下载的 BiRefNet HR-matting 并执行背景移除 +- **THEN** 使用该模型推理,输出和进度不再固定指向 BEN2 + +### Requirement: 输出有限的连续透明度 +每款背景模型 SHALL 产出与分析图片尺寸一致、有限且位于 `[0,1]` 的连续 Alpha。模型适配 SHALL 保持其官方输出语义,支持发丝、毛绒和半透明边缘的连续透明度。 + +#### Scenario: 半透明边缘 +- **WHEN** HR-matting 对包含薄纱或细发丝的图片输出中间透明度 +- **THEN** 后处理保留有效的中间 Alpha 值,不将其强制二值化或按单图极值重新拉伸 + +#### Scenario: 无效预测 +- **WHEN** 模型返回错误形状或包含 NaN、Inf 的透明度 +- **THEN** 任务明确失败,并保留可诊断的模型错误信息 + +### Requirement: 保持已有前景和 HDR 编辑行为 +系统 SHALL 沿用既有前景 RGB 与源 Alpha 合成规则,支持静态前景、多帧前景及 HDR 的 Alpha-only 输出;HDR 结果 SHALL 保持原始浮点 RGB。 + +#### Scenario: HDR 背景移除 +- **WHEN** 用户对浮点 HDR 图片使用任意背景模型 +- **THEN** 用分析输入预测 Alpha,将结果应用到原始图片,并保留原始 HDR RGB 数值 + +#### Scenario: 多帧透明前景 +- **WHEN** 用户对支持的多帧输入执行背景移除 +- **THEN** 每帧使用选定模型,保持帧数、编号、尺寸与已有透明度合成约定 + +### Requirement: 模型资源可复用且可释放 +系统 SHALL 为相同模型和设备复用会话,在模型切换、资源关闭时释放旧会话,并使取消与失败遵循既有任务清理行为。 + +#### Scenario: 连续帧复用 +- **WHEN** 多帧任务连续处理同一模型和设备 +- **THEN** 各帧复用已加载会话,取消后停止后续推理并清理未完成任务产物 + +#### Scenario: 切换背景模型 +- **WHEN** 用户从 Lite 切换到 BEN2 或 HR-matting +- **THEN** 实际推理使用新选择,并释放原先背景模型占用的会话资源 + +### Requirement: 新背景模型经过真实运行验收 +BiRefNet Lite SHALL 在发布前通过 CPU / DirectML 数值和图片验证,HR-matting SHALL 通过 CPU 验证;记录真实下载体积、加载与热推理耗时、峰值资源占用及边缘效果。Lite SHALL 比 BEN2 下载体积更小,并在相同测试条件下至少降低热推理耗时或峰值占用中的一项。 + +#### Scenario: 确定轻量默认模型 +- **WHEN** Lite 的正式资产准备发布 +- **THEN** 存在与 BEN2 的同条件对照和质量检查记录,且其轻量收益满足要求 + +### Requirement: HR-matting 使用 CPU +HR-matting SHALL 暂时固定使用 CPU。模型选项 SHALL 标明 CPU,进度 SHALL 显示实际设备;会话缓存 SHALL 按实际 CPU 设备复用。 + +#### Scenario: GPU 偏好下选择 HR-matting +- **WHEN** 全局设备为 DirectML,用户选择 HR-matting +- **THEN** 创建 CPU 会话并显示 CPU 进度,保持 HR 的 2048 像素输入及连续 Alpha 语义 + diff --git a/openspec/specs/boundary-depth-smoothing/spec.md b/openspec/specs/boundary-depth-smoothing/spec.md new file mode 100644 index 0000000..f2bb205 --- /dev/null +++ b/openspec/specs/boundary-depth-smoothing/spec.md @@ -0,0 +1,123 @@ +# boundary-depth-smoothing Specification + +## Purpose +TBD - created by archiving change unify-boundary-depth-smoothing. Update Purpose after archive. +## Requirements +### Requirement: Shared Boundary Smooth control + +`O Image Depth Cutout`、`O Image Depth Plane`、`O Image Depth Panorama` SHALL 保留 `Boundary Smooth` 输入,Depth Cutout 默认值为 2,另外两组为 5;Cutout 范围为 1–20,另外两组为 0–20。Depth Cutout 使用它控制每轮深度 Blur 的步数和现有边界网格平滑次数;另外两组使用它控制深度 Blur 与网格平滑次数。 + +#### Scenario: Positive iteration count +- **WHEN** 用户将任一组的 Boundary Smooth 设为 12 +- **THEN** 每批深度 Blur 和现有边界网格平滑均使用 12 次迭代 +- **AND** Depth Cutout 的批次数由 Outline Depth Fix 控制 + +#### Scenario: Disabled smoothing +- **WHEN** Boundary Smooth 为 0 +- **THEN** 输出几何与两种边界平滑均关闭的基线一致 +- **AND** 深度采样直接使用原值 + +### Requirement: Topology-local scalar smoothing + +系统 SHALL 在有效性剔除及 Split 后、投影前的网格 Point 域对深度标量执行 Blur,并在可处理边界及邻近两圈内应用结果。Depth Plane 和 Panorama 的 Blur Weight 为 0.5;Depth Cutout 使用当前残差计算的自适应权重。边界影响范围 SHALL 由边界字段的 2 次 Weight 为 1 的 Blur 与原边界字段取最大值后,排除保护点得到。 + +#### Scenario: Isolated boundary spike +- **WHEN** 原本平缓的表面在一个可处理边界点具有深度尖刺并启用 Boundary Smooth +- **THEN** 深度突变通过当前网格邻域的标量平均减弱 +- **AND** 深度平滑阶段的顶点、边、面及 UV 拓扑保持不变 + +#### Scenario: Interior preservation +- **WHEN** 顶点位于影响范围之外 +- **THEN** 深度平滑阶段直接保留其原采样值 + +#### Scenario: Deleted and separated samples +- **WHEN** 邻近像素所属网格已被有效性剔除,或邻接表面已由 Split 分离 +- **THEN** 平滑仅使用当前连通网格的样本,不跨已删区域或分离表面平均 + +### Requirement: Cutout silhouette depth smoothing + +Depth Cutout SHALL 仅将原 Alpha 轮廓及内侧两圈纳入自适应深度平滑,保护 Split 切边及其与原轮廓的交点。后置网格平滑保持既有职责。对外轮廓术语 SHALL 统一使用 Outline,与 Fine Outline 一致。 + +#### Scenario: Adaptive batches +- **WHEN** Outline Depth Fix 大于 0 且 Boundary Smooth 大于 0 +- **THEN** 每轮根据当前相机深度与 Weight 0.5 的一步 Blur 之差,乘 Uniform Scale 后除以置换前、Split 清理后所有网格边长的中位数 +- **AND** Ratio 固定以 0.25–0.5 的 Smoothstep 映射到 0–1,并乘轮廓影响范围 +- **AND** Weight 不再 Blur,批内固定使用 Boundary Smooth 次,每轮重新计算 +- **AND** Outline Depth Fix 默认 16,范围 0–200;0 关闭轮廓深度处理 + +#### Scenario: Split boundary preservation +- **WHEN** Split 在原轮廓内形成新的切边 +- **THEN** 深度平滑保持这些切边的相机采样不变,包括与原轮廓相交的切分点 + +#### Scenario: Fine Outline with zero thickness +- **WHEN** Fine Outline 的原轮廓存在异常深度,Thickness 为 0 且 Boundary Smooth 大于 0 +- **THEN** 原轮廓点的深度参与平滑 +- **AND** 深度平滑后的相机投影轮廓在浮点容差内保持一致 + +### Requirement: Depth Plane rectangle protection + +Depth Plane SHALL 对有效性剔除或 Split 形成的可处理边界应用深度平滑,并同时从边界起点和最终应用权重中排除原 UV 矩形外框。 + +#### Scenario: Alpha or model-validity opening +- **WHEN** Valid Only 因 Alpha 或模型 validity 删除网格并形成新开口 +- **THEN** 新开口及邻近两圈参与深度平滑 +- **AND** 保留下来的原矩形外框点保持既有保护行为 + +#### Scenario: Intact rectangle +- **WHEN** Valid Only 和 Split 均关闭,网格仅有原矩形外边界 +- **THEN** 新增深度 Blur 对输出不产生变化 + +### Requirement: Projection-aware reconstruction + +Cutout 和 Depth Plane SHALL 在深度平滑阶段保持原相机射线,通过平滑后的 Z 重建相机点;Depth Panorama SHALL 平滑径向距离并保持原球面方向。方向保持以现有后置网格平滑之前的结果为准。 + +#### Scenario: Camera depth reconstruction +- **WHEN** 正的有效相机 Z 被平滑 +- **THEN** 新旧相机点的 X/Z、Y/Z 在浮点容差内一致 + +#### Scenario: Degenerate camera depth +- **WHEN** 原采样 Z 无法安全用于比例重建 +- **THEN** 保留原采样,不因深度平滑产生非有限值 + +#### Scenario: Panorama seam and poles +- **WHEN** 有效表面跨越经度接缝或覆盖极点 +- **THEN** 深度 Blur 使用实际球面邻接,不将 UV 接缝当作网格开口 +- **AND** 径向重建保持每个点的球面方向 + +#### Scenario: Retained invalid panorama region +- **WHEN** Panorama 关闭 Depth Mask 保留无效区域 +- **THEN** 无效面整片落在 `Dome Radius` +- **AND** 该距离不混入有效边界的径向平均 + +### Requirement: Integrated geometry and asset verification + +系统 SHALL 保持厚度、Split、Depth Scale、UV、材质和用户属性的既有职责,所有几何节点及嵌套组 MUST 保持 Capture Attribute 为零。更新后的节点源码、行为测试及保存资产 SHALL 一致。 + +#### Scenario: Surface modes and downstream shaping +- **WHEN** 三个深度表面组合使用边界平滑与自身支持的 Split、Depth Scale、厚度或 Cutout 模式 +- **THEN** 输出为有效几何,属性和材质传递符合原有协议 +- **AND** 深度平滑不改变有效性剔除与 Split 的拓扑判定 + +#### Scenario: Rebuilt asset +- **WHEN** 执行节点资产构建并在独立 Blender 进程中重新加载保存资产 +- **THEN** 资产与源码构建结果通过相同的行为验证 +- **AND** 公共参数、节点连接和 Capture 禁用检查通过 + +### Requirement: Simplified Cutout shaping controls + +Depth Cutout SHALL 固定前后法线平滑为 100 次,原联动宽范围滤波固定为 512 次;移除 Normal Smooth 输入。系统 SHALL 删除 Edge Round 输入及对应前表面位移、厚度补偿和圆化字段,保留 Split 基础收口。 + +#### Scenario: Reduced interface +- **WHEN** 新建 Depth Cutout +- **THEN** Outline Depth Fix 默认 16、Boundary Smooth 默认 2 +- **AND** 输入中没有 Edge Round、Normal Smooth 或 Outline Smooth Iterations + +### Requirement: Cutout Options order and inflation factor + +Depth Cutout 的 Options SHALL 按 Cleanup Threshold、Smooth Weight、Reference Depth、Boundary Smooth、Outline Depth Fix、Front Inflation 排列。Front Inflation SHALL 为 0–1 Factor、默认 0;Boundary Smooth SHALL 为 1–20、默认 2。 + +#### Scenario: New Cutout defaults +- **WHEN** 新建 Depth Cutout +- **THEN** Front Inflation 为 0,Boundary Smooth 不能从界面设为小于 1 +- **AND** Outline Depth Fix 保持默认 16 + diff --git a/openspec/specs/brush-stroke-processing/spec.md b/openspec/specs/brush-stroke-processing/spec.md new file mode 100644 index 0000000..0626980 --- /dev/null +++ b/openspec/specs/brush-stroke-processing/spec.md @@ -0,0 +1,52 @@ +# brush-stroke-processing Specification + +## Purpose +TBD - created by archiving change repair-image-edit-pipelines. Update Purpose after archive. +## Requirements +### Requirement: Stroke simplification bounds the whole segment error +Brush 与共享自由手绘手势 SHALL 将每个保留原始采样点到简化线段的偏差限制在 0.75 个 Viewport 像素以内。Polyline 的已提交顶点 SHALL 按用户输入保留。 + +#### Scenario: Dense curved stroke is simplified +- **WHEN** 用户以高密度采样绘制弯曲轨迹 +- **THEN** 整段简化误差不超过 0.75 px,连续尾点替换不会累计放大偏差 +- **AND** 预览与提交使用同一有效路径 + +### Requirement: Brush antialiases the complete geometric union +Brush SHALL 对全部圆形印记和连接条的完整几何并集应用一次抗锯齿。几何并集相同的图元分段方式 SHALL 产生相同覆盖值。 + +#### Scenario: Stroke turns through a right angle +- **WHEN** 半径为 2、5 或 25 px 的 Brush 形成直角连接或自交 +- **THEN** 内部连接处不会因图元各自的抗锯齿边界减少覆盖 +- **AND** 提交覆盖符合完整并集的抗锯齿参考结果 + +#### Scenario: Equivalent geometry has different segmentation +- **WHEN** 同一个直线 footprint 由不同数量的共线连接条描述 +- **THEN** 输出 Alpha 覆盖在浮点误差内一致 + +### Requirement: Long stroke preview remains responsive +Brush SHALL 在每次有效路径变化后显示整条待提交笔画的合并填充和外轮廓。局部新增笔画的预览计算 SHALL 限定于变化范围,静止重绘 SHALL 复用已有预览结果。 + +#### Scenario: A short segment is appended to a long stroke +- **WHEN** 已有 800、1600 或 3200 个屏幕点,用户追加只影响局部范围的短线段 +- **THEN** 预览更新覆盖新范围并保留其他区域,计算范围不扩展为完整历史笔画 +- **AND** 预览与相同路径的完整几何参考一致 + +#### Scenario: Tail changes or crosses previous geometry +- **WHEN** 活动尾段变化或与已有笔画相交 +- **THEN** 预览清除失效尾段的覆盖及内部轮廓,呈现当前完整 footprint + +### Requirement: Mask provides one transactional gesture edit +Mask SHALL 在启动时冻结 Scene 的 Gesture、Mode 和 Radius;完成时一次提交 Packed Image 并支持单步 Undo。Mask SHALL NOT 暴露无法执行的操作后参数重做面板。 + +#### Scenario: Stroke is completed and undone +- **WHEN** 用户完成一个 Brush、Lasso 或 Polyline Mask 后执行 Undo +- **THEN** 源图片与对象状态恢复,整次手势只产生一个编辑步骤 + +#### Scenario: Stroke is cancelled or tool changes +- **WHEN** 用户按 Esc、RMB,或切换工具取消未提交手势 +- **THEN** 原 Image 不变,所有操作内路径与预览资源释放 + +#### Scenario: User edits tool settings +- **WHEN** 用户修改 Scene 中的 Mask 工具设置 +- **THEN** 新手势采用新设置,进行中的手势保持启动时设置 + diff --git a/openspec/specs/canonical-object-frame/spec.md b/openspec/specs/canonical-object-frame/spec.md new file mode 100644 index 0000000..6e9e6a8 --- /dev/null +++ b/openspec/specs/canonical-object-frame/spec.md @@ -0,0 +1,67 @@ +# canonical-object-frame Specification + +## Purpose +TBD - created by archiving change standardize-blender-object-coordinates. Update Purpose after archive. +## Requirements +### Requirement: Generated meshes use the Blender object frame + +系统 SHALL 让 Plane、Depth Plane、Relief Plane、Cutout BaseShape 与 Depth Cutout 的输入网格使用统一的 Blender 对象局部坐标:X 为图片水平方向,Z 为图片 up,+Y 为图片纸内深度方向。所有未应用 modifier 的输入网格 MUST 位于 `Y=0` 的 XZ 平面。 + +#### Scenario: Create a Plane or Cutout BaseShape + +- **WHEN** 用户创建 Plane 或 Cutout BaseShape +- **THEN** 未应用 modifier 的本地顶点 Y 坐标均为 0 +- **AND** 顶点 X 范围对应源 Image Empty 的水平 bounds +- **AND** 顶点 Z 范围对应源 Image Empty 的 up bounds + +### Requirement: Image UV maps to the XZ plane + +系统 SHALL 将源图片的左右方向映射到本地 X,将源图片的上下方向映射到本地 Z,并保持 UV 采样在生成网格上的方向一致。 + +#### Scenario: Cutout UV corners follow image corners + +- **WHEN** Cutout 从完整图片 bounds 生成 BaseShape +- **THEN** 图片左下角对应的本地顶点位于最小 X 和最小 Z +- **AND** 图片右上角对应的本地顶点位于最大 X 和最大 Z +- **AND** UV 读取的颜色纹理方向与源图片一致 + +### Requirement: Depth and thickness operate along Y + +系统 SHALL 让 Depth Plane、Relief Plane、Cutout 与 Depth Cutout 的投影、厚度和 Shell 位移沿本地 Y 方向表达,并 SHALL 让 front 方向为 -Y、depth/纸内方向为 +Y。 + +#### Scenario: Depth Plane displaces toward positive Y + +- **WHEN** Depth Plane 使用有效深度图且 Reference Depth 大于采样深度 +- **THEN** 生成表面的 Y 坐标向 +Y 方向偏离输入平面 +- **AND** 输入平面保持在 `Y=0` + +#### Scenario: Cutout thickness grows along Y + +- **WHEN** Cutout 设置非零 Thickness +- **THEN** 生成 Shell 的前后范围沿 Y 轴分布 +- **AND** 没有几何沿 Z 轴被当作厚度方向 + +### Requirement: Depth Direction defaults to positive Y + +`O Image Relief Plane` 与 Depth Symmetry 的 canonical 深度方向 SHALL 使用本地 +Y 作为默认方向,而不是 -Z。 + +#### Scenario: Relief Plane direction default + +- **WHEN** 用户从资产加载 `O Image Relief Plane` +- **THEN** `Depth Direction` 默认值为 `(0, 1, 0)` + +#### Scenario: Depth Symmetry canonical direction + +- **WHEN** Depth Symmetry 使用默认方向校准 +- **THEN** 对称前的 canonical 镜像轴 SHALL 为 Y + +### Requirement: Cutout node assets no longer expose Depth Axis + +`O Image Cutout` 与 `O Image Depth Cutout` MUST 不再公开 `Depth Axis` 输入,并且 MUST 不包含把最终几何从 -Z 转到 +X 的旧轴向旋转。 + +#### Scenario: Inspect Cutout interfaces + +- **WHEN** 检查 `O Image Cutout` 和 `O Image Depth Cutout` 的输入接口 +- **THEN** 不存在名为 `Depth Axis` 的 socket +- **AND** 节点组内不存在旧 `CUTOUT_Z_TO_X` 最终旋转 + diff --git a/openspec/specs/clipboard-image-cache/spec.md b/openspec/specs/clipboard-image-cache/spec.md new file mode 100644 index 0000000..7bc3374 --- /dev/null +++ b/openspec/specs/clipboard-image-cache/spec.md @@ -0,0 +1,43 @@ +# clipboard-image-cache Specification + +## Purpose +TBD - created by archiving change unify-material-color-images. Update Purpose after archive. +## Requirements +### Requirement: Clipboard reuse requires an unchanged image + +相同剪贴板输入 SHALL 仅复用当前像素、尺寸、颜色空间和 Alpha 模式均匹配缓存基线的有效 Image。缓存验证 SHALL 包括透明区域 RGB,并保持候选图像原有内容与状态。 + +#### Scenario: Repeated content remains unchanged + +- **WHEN** 相同剪贴板内容再次粘贴,缓存图像内容和设置未改变 +- **THEN** 系统 SHALL 复用该 Image + +#### Scenario: Mask overwrites the cached image + +- **WHEN** Mask 修改了独占的剪贴板 Image,修改结果已打包,再次粘贴原始剪贴板内容 +- **THEN** 系统 SHALL 加载原始剪贴板内容到另一 Image,保留已编辑图像 + +#### Scenario: Shared edit leaves an intact candidate + +- **WHEN** 编辑产生独立结果,另一个用户仍引用未修改的剪贴板原图 +- **THEN** 再次粘贴 SHALL 复用该未修改原图 + +#### Scenario: Other editing or interpretation changes invalidate a candidate + +- **WHEN** 候选图像经过尺寸修改、原生绘制、透明 RGB 修改、颜色空间或 Alpha 模式变更 +- **THEN** 再次粘贴 SHALL 跳过已变更候选,使用原始剪贴板内容及其初始解释设置 + +#### Scenario: Legacy or unreadable cache entry is found + +- **WHEN** 候选缺少内容校验基线或无法读取 +- **THEN** 系统 SHALL 按未命中处理,并保留已有图像 + +### Requirement: Clipboard material results stay outside the source cache + +剪贴板 Plane SHALL 从有效剪贴板原图按整图范围生成独立材质颜色图。参考图、节点和笔刷的后续粘贴 SHALL 按原图缓存有效性选择 Image。 + +#### Scenario: Painted material is followed by another paste + +- **WHEN** 用户粘贴为 Plane 并绘制其材质,随后把相同内容粘贴为参考图、节点或笔刷 +- **THEN** 系统 SHALL 使用原始剪贴板内容,且不复用材质颜色图 + diff --git a/openspec/specs/compact-repeat-arrangement/spec.md b/openspec/specs/compact-repeat-arrangement/spec.md new file mode 100644 index 0000000..6b59add --- /dev/null +++ b/openspec/specs/compact-repeat-arrangement/spec.md @@ -0,0 +1,68 @@ +# compact-repeat-arrangement Specification + +## Purpose +TBD - created by archiving change compact-repeat-node-arrangement. Update Purpose after archive. +## Requirements +### Requirement: Compact repeat execution path + +排列器 SHALL 按循环执行的几何步骤安排 Repeat 两端,取值支线独立排列。 + +#### Scenario: Set Position loop with long value calculations +- **WHEN** 循环只执行 Set Position,输入值来自长计算链 +- **THEN** Repeat 两端保持围绕 Set Position 的紧凑排列,预览执行区域按几何主链确定 + +### Requirement: Geometry processing order + +排列器 SHALL 将前置循环安排在后续几何处理之前。 + +#### Scenario: Depth Cutout boundary smoothing +- **WHEN** 排列 O Image Depth Cutout 的前置边界平滑循环 +- **THEN** 循环位于其后续几何消费者左侧,保持原有连接与求值结果 + +### Requirement: Readable field branches and geometry references + +排列器 SHALL 将取值支线紧凑安排在消费者附近,复杂支线使用互不重叠的 Frame,并分离几何引用的水平通道。 + +#### Scenario: Long calculation chain +- **WHEN** 消费者使用较长的简单计算链 +- **THEN** 计算链使用紧凑折叠及分段纵向布局 +- **AND** 连续依赖按计算方向展开,独立计算按实际消费输入分组 + +#### Scenario: Complex depth cutout branches +- **WHEN** 排列 O Image Depth Cutout +- **THEN** 复杂计算支线分别成框,几何引用线保持独立的水平通道,并通过斜向曲线过渡,原有几何求值保持一致 + +#### Scenario: Geometry trunk alongside its consumer frame +- **WHEN** 几何主干向 Frame 内的计算节点供值 +- **THEN** 主干与完整 Frame 边界保持间距,通过斜向分支接入框内消费者 + +#### Scenario: Long branch near its geometry consumer +- **WHEN** 较大支线需要占用来源和消费者之间的横向空间 +- **THEN** 主线在消费者之前预留支线及斜向出口的空间,连续主线保持直接向前连接 + +#### Scenario: Independent calculation before a geometry consumer +- **WHEN** 支线独立于上游几何结果 +- **THEN** 支线可向主线起点之前展开,主线按实际出口位置保持紧凑 + +#### Scenario: Return connection and neighboring reference tracks +- **WHEN** 几何结果连接左侧消费者 +- **THEN** 连线直接向消费者方向过渡,各水平通道按最终范围保留间距 + +#### Scenario: Geometry fanout without overlapping wires +- **WHEN** 同一几何结果供多个节点消费 +- **THEN** 近处消费者直接接入,公共水平通道按从左到右分发,每段公共连线只绘制一次 +- **AND** 水平连线保持向前,短 reroute 间距合并,近乎平行的曲线与主线保持间距 + +#### Scenario: Shared results outside calculation branches +- **WHEN** 几何或其他类型的结果供多个支线消费 +- **THEN** 共享结果沿支线外围的公共管道分发,在消费者附近斜向接入 +- **AND** 原有主线连接保持执行顺序,公共管道与节点、Frame 及其他管道保留间距 + +#### Scenario: Geometry transits an unrelated calculation +- **WHEN** 几何连接沿途经过其他支线 +- **THEN** 整段曲线绕开不消费该结果的 Frame,仅允许进入自身来源或消费者所属的 Frame + +#### Scenario: Peripheral producers feed their shared pipes forward +- **WHEN** 支线计算的结果通过公共管道供多个消费者使用 +- **THEN** 共享计算支线与管道一同位于消费者外围,管道入口在结果输出节点右侧 + diff --git a/openspec/specs/conversion-source-images/spec.md b/openspec/specs/conversion-source-images/spec.md new file mode 100644 index 0000000..6505c6d --- /dev/null +++ b/openspec/specs/conversion-source-images/spec.md @@ -0,0 +1,93 @@ +# conversion-source-images Specification + +## Purpose +TBD - created by archiving change reuse-conversion-source-images. Update Purpose after archive. +## Requirements +### Requirement: Conversion selects images by other Empty references + +四种 Convert To SHALL 在应用转换时检查整个 Blender 数据中的其他 Image Empty 是否引用同一源 Image。没有其他引用时 SHALL 复用原 Image;存在时 SHALL 使用原 Image 的独立副本。 + +#### Scenario: Only the converted Empty uses the source + +- **WHEN** 源 Image 没有被其他 Image Empty 引用 +- **THEN** 转换材质 SHALL 引用原 Image 数据块并保留其名称和路径 + +#### Scenario: Another Empty shares the image + +- **WHEN** 另一个 Image Empty 引用同一 Image,包括隐藏对象或其他场景对象 +- **THEN** 材质 SHALL 使用独立副本,沿用原名并由 Blender 消歧 +- **AND** 其他 Empty 的 Image 引用、像素、颜色空间和 Alpha 模式 SHALL 保持原值 + +#### Scenario: Existing materials share the image + +- **WHEN** 原 Image 被材质引用且没有其他 Image Empty 引用 +- **THEN** 转换 SHALL 复用原 Image,已有材质 SHALL 同步看到该 Image 的配置变化 + +#### Scenario: Separate images have the same file path + +- **WHEN** 另一 Empty 使用路径相同但数据块不同的 Image +- **THEN** 该引用 SHALL 视为独立图像,转换 SHALL 复用当前源 Image + +### Requirement: Conversion preserves material presentation rules + +转换 SHALL 对选定的 Image 应用既有材质颜色空间和 Alpha 配对规则,保留业务 RGB、Alpha、透明区域 RGB、精度和未保存像素。Panorama 的 float 或非 sRGB shadeless 图像 SHALL 沿用源颜色空间和 Alpha 模式。 + +#### Scenario: Standard material adaptation is applied + +- **WHEN** 普通转换关闭适配,或所需 inverse 空间回退到 sRGB +- **THEN** 选定 Image SHALL 使用 sRGB + PREMUL + +#### Scenario: Inverse adaptation is available + +- **WHEN** 普通转换启用适配且对应 inverse 空间可用 +- **THEN** 选定 Image SHALL 使用该空间与 STRAIGHT + +#### Scenario: Panorama preserves HDR interpretation + +- **WHEN** Panorama 源图为 float 或采用现有保留分支匹配的非 sRGB 空间 +- **THEN** 材质 Image SHALL 保留原颜色空间、Alpha 模式与 HDR 像素 + +#### Scenario: Unsaved and packed pixels remain readable + +- **WHEN** generated、dirty 或 packed 图像完成转换并保存重载 +- **THEN** 图像业务像素 SHALL 在原有编码精度内保留,包括完全透明区域的 RGB + +### Requirement: Conversion uses source color content directly + +转换材质 SHALL 使用源图内容与分辨率。Depth / Relief 和 Panorama Job SHALL 仅输出各自所需的几何数据产物;转换 SHALL 无需产生额外颜色文件。 + +#### Scenario: Depth or relief result is applied + +- **WHEN** Job 返回深度、法线和 metadata +- **THEN** 转换 SHALL 使用原图或其独立副本完成材质绑定 + +#### Scenario: Panorama result is applied + +- **WHEN** Job 返回深度和 metadata +- **THEN** 转换 SHALL 使用源全景颜色完成材质绑定 + +#### Scenario: Plane converts an animated image + +- **WHEN** Plane 转换 movie 或 sequence 图像 +- **THEN** 材质 SHALL 保留源 Empty 的帧起点、偏移、时长和循环设置 + +### Requirement: Conversion preserves source state on failure and undo + +转换 SHALL 在失败时恢复复用原图的有效配置和像素,释放本次创建的临时资源,并保留源 Empty。成功转换 SHALL 支持 Undo / Redo。 + +#### Scenario: Material or object creation fails + +- **WHEN** 原图已配置但转换在完成对象替换前失败 +- **THEN** 原图的颜色空间、Alpha、业务像素和源 Empty 引用 SHALL 恢复 +- **AND** 本次创建的未使用资源 SHALL 清理 + +#### Scenario: The source changes during an asynchronous job + +- **WHEN** 结果应用时源对象失效或其 Image 已替换 +- **THEN** 转换 SHALL 取消应用并保留当前源状态 + +#### Scenario: A completed conversion is undone and redone + +- **WHEN** 用户撤销成功的转换再重做 +- **THEN** 撤销 SHALL 恢复源 Empty 和转换前图像状态,重做 SHALL 恢复转换对象及对应材质配置 + diff --git a/openspec/specs/coreml-inference/spec.md b/openspec/specs/coreml-inference/spec.md new file mode 100644 index 0000000..6ed4112 --- /dev/null +++ b/openspec/specs/coreml-inference/spec.md @@ -0,0 +1,73 @@ +# coreml-inference Specification + +## Purpose +TBD - created by archiving change correct-coreml-device-support. Update Purpose after archive. +## Requirements +### Requirement: CoreML 使用唯一设备标识 +系统 SHALL 在界面、任务参数、运行时设备清单和 ONNX Provider 选择中使用 `coreml` 表示 Apple CoreML Execution Provider,并 SHALL NOT 接受或生成 `mps` 设备标识。 + +#### Scenario: macOS 用户查看推理设备 +- **WHEN** 用户在受支持的 macOS arm64 设备上查看推理设备枚举 +- **THEN** 系统显示 `CoreML` 和 `CPU`,且不显示 `MPS` + +#### Scenario: CoreML 任务参数 +- **WHEN** 用户选择 CoreML 并创建 AI 任务 +- **THEN** 系统提交的设备参数为 `coreml` + +#### Scenario: 旧设备标识不再受支持 +- **WHEN** Provider 选择入口接收到 `mps` +- **THEN** 系统拒绝该值,而不将其转换或转发为 CoreML + +### Requirement: Apple Silicon 支持范围按平台确定 +系统 SHALL 在 macOS 的 `arm64` 或 `aarch64` 架构上提供 CoreML,不得根据 Apple 芯片代际限制该设备。 + +#### Scenario: M1 至 M3 设备 +- **WHEN** 系统运行于 M1、M2 或 M3 的 macOS arm64 环境 +- **THEN** 可用设备包含 `coreml` 和 `cpu` + +#### Scenario: M4 或更新设备 +- **WHEN** 系统运行于 M4 或更新的 macOS arm64 环境 +- **THEN** 可用设备包含 `coreml` 和 `cpu` + +#### Scenario: Apple Silicon 默认设备 +- **WHEN** macOS arm64 同时提供 CPU 与 CoreML +- **THEN** 设备枚举默认选择 CPU,且用户仍可手动选择 CoreML + +#### Scenario: 非 Apple Silicon 平台 +- **WHEN** 系统不运行于 macOS arm64 或 aarch64 环境 +- **THEN** 系统不因 CoreML 规则新增 `coreml` 设备 + +### Requirement: CoreML Session 使用模型兼容配置 +系统 SHALL 以 `CoreMLExecutionProvider` 创建 CoreML Session,并 SHALL NOT 强制设置 `ModelFormat=MLProgram`。普通模型 SHALL 使用默认 CoreML 配置;MoGe-2 SHALL 只允许 CoreML 接管静态输入 shape 分区;CoreML 不支持或未接管的执行 SHALL 由 CPU fallback 处理。 + +#### Scenario: 显式选择 CoreML +- **WHEN** 任务请求设备 `coreml` 且 ONNX Runtime 提供 CoreML Execution Provider +- **THEN** Session 以 CoreML 为首选 Provider、以 CPU 为后备 Provider,且创建参数不包含强制的 CoreML `MLProgram` 配置 + +#### Scenario: MoGe-2 限制 CoreML 动态分区 +- **WHEN** 系统以 CoreML 创建 MoGe-2 Session +- **THEN** CoreML Provider options 包含 `RequireStaticInputShapes=1`,且不包含 `ModelFormat=MLProgram` + +#### Scenario: 其他模型不继承 MoGe-2 限制 +- **WHEN** 系统以 CoreML 创建 BEN2、DA3 或 Upscale Session +- **THEN** CoreML Provider 不包含 MoGe-2 的静态输入限制 + +#### Scenario: 自动选择 CoreML +- **WHEN** 任务请求自动设备且 CoreML 与 CPU Provider 都可用 +- **THEN** CoreML 的优先级高于 CPU,并保留 CPU fallback + +#### Scenario: CoreML 不可用 +- **WHEN** 任务请求 CoreML 但 ONNX Runtime 只提供 CPU Provider +- **THEN** 系统使用 CPU Provider + +### Requirement: MoGe-2 可在 CoreML 模式推理 +系统 MUST 支持 MoGe-2 ONNX 模型在兼容 Apple Silicon CoreML 环境中创建 Session 并完成推理,不得因 AnyImage 强制选择 MLProgram 而在创建模型时失败。 + +#### Scenario: Apple Silicon 执行 MoGe-2 +- **WHEN** M2 或 M4 用户选择 CoreML 并运行 MoGe-2 功能 +- **THEN** 系统成功创建 CoreML Session 并完成至少一帧 MoGe-2 推理 + +#### Scenario: 其他 AI 模型继续使用 CoreML +- **WHEN** 用户选择 CoreML 并运行 BEN2 或 Upscale +- **THEN** 对应模型继续完成 Session 创建和推理 + diff --git a/openspec/specs/crop-tool-options/spec.md b/openspec/specs/crop-tool-options/spec.md new file mode 100644 index 0000000..d72a92e --- /dev/null +++ b/openspec/specs/crop-tool-options/spec.md @@ -0,0 +1,118 @@ +# crop-tool-options Specification + +## Purpose +TBD - created by archiving change persist-image-tool-options. Update Purpose after archive. +## Requirements +### Requirement: Crop Tool is one four-tool toolbar group +系统 SHALL 在 3D Viewport 工具栏中提供两个独立 AnyImage 入口:一个 **Crop Tool** 工具组和一个普通单项 **Cutout Tool**。Crop Tool SHALL 按 **Crop Box**、**Crop Lasso**、**Crop Polyline**、**Crop Perspective** 的顺序包含四个子工具;Cutout Tool SHALL 不属于任何工具组,并紧接在 Crop Tool 组之后。 + +#### Scenario: User opens the Crop Tool group +- **WHEN** 用户展开 Crop Tool +- **THEN** 工具组只显示 Crop Box、Crop Lasso、Crop Polyline 与 Crop Perspective +- **AND** Crop Box 是该组的首个子工具 + +#### Scenario: User views adjacent AnyImage tools +- **WHEN** Crop Tool 组和 Cutout Tool 已注册到 3D Viewport +- **THEN** 工具栏显示 Crop Tool 组与 Cutout Tool 两个独立入口 +- **AND** Cutout Tool 紧接在 Crop Tool 组之后 +- **AND** Cutout Tool 是单个 WorkSpaceTool,不是单项工具组 +- **AND** 两个入口之间没有分割线 +- **AND** Cutout Tool 使用 Add UV Sphere 图标 + +### Requirement: Crop terminology replaces Image Tool terminology +系统 SHALL 对四个裁切子工具、激活入口、Tool ID、Operator、设置属性、源码边界和文档统一使用 Crop 术语,不得保留旧 Image Tool 或独立 Perspective Tool 的兼容名称和转发入口。 + +#### Scenario: User activates the crop entry +- **WHEN** 用户从 AnyImage 入口激活图片裁切功能 +- **THEN** 系统激活 Crop Box 并以 Crop Tool 命名该功能 + +#### Scenario: Extension registers crop tools +- **WHEN** AnyImage 扩展完成注册 +- **THEN** 四个 Crop 子工具使用 `anyimage` 前缀的 Crop Tool ID 注册为同一组 +- **AND** 旧 Image Tool 与独立 Perspective Tool ID 不再注册 + +### Requirement: Image Line is removed +系统 SHALL 删除 Image Line 及其按直线划分图片半平面的裁切行为,不得在 Crop Tool、菜单、快捷入口或公开 Operator 中提供该工具。 + +#### Scenario: User opens Crop Tool +- **WHEN** 用户查看 Crop Tool 的子工具 +- **THEN** 列表中没有 Image Line 或 Crop Line + +### Requirement: Crop options persist independently per sub-tool +系统 SHALL 为 Crop Box、Crop Lasso、Crop Polyline 与 Crop Perspective 分别保存彼此独立的 **Keep Original**,并为前三个 Selection Crop Tool 分别保存彼此独立的 **Invert**;所有选项默认关闭,切换一个子工具的值不得改变其他子工具的值,Crop Perspective SHALL NOT 提供 Invert。 + +#### Scenario: User returns to a Crop sub-tool +- **WHEN** 用户修改一个子工具支持的 Invert 或 Keep Original,切换到其他 Crop 子工具后再返回 +- **THEN** 该子工具恢复自己上次保存的选项值 + +#### Scenario: User uses a Crop sub-tool for the first time +- **WHEN** 当前文件尚未修改该子工具支持的 Invert 或 Keep Original +- **THEN** 该子工具支持的选项均为关闭 + +#### Scenario: User opens Crop Perspective settings +- **WHEN** 用户激活 Crop Perspective +- **THEN** 工具设置提供独立的 Keep Original +- **AND** 工具设置不提供 Invert 或 Refine Selection + +### Requirement: Invert follows the persistent Crop sub-tool option +Crop Box、Crop Lasso 与 Crop Polyline SHALL 使用当前子工具保存的 **Invert** 值决定保留 Selection 内部还是外部,并只通过工具选项修改该值;Crop Perspective 不参与该行为,Crop Tool 的 keymap 与 modal 交互 SHALL NOT 将 `F` 或其他字母键绑定为选项快捷键。 + +#### Scenario: User changes Invert in tool settings +- **WHEN** 用户在任一 Selection Crop Tool 的工具设置中修改 Invert +- **THEN** 系统保存当前子工具的 Invert,下一次裁切使用新值 + +#### Scenario: User operates a Crop tool with a letter key +- **WHEN** Crop Tool 空闲或正在执行 modal 交互 +- **THEN** Crop Tool 不使用 `F` 或其他字母键切换 Invert、Keep Original 或 Refine Selection + +### Requirement: Cutout has no letter option shortcuts +Cutout Tool 的 keymap 与 modal 交互 SHALL NOT 使用 `F` 或其他字母键切换选项。 + +#### Scenario: User presses F during Cutout selection +- **WHEN** Cutout Tool 正在执行 Selection modal 交互 +- **THEN** `F` 不切换 Invert 或其他选项 + +### Requirement: Keep Original preserves the crop source +四个 Crop 子工具 SHALL 在各自的 **Keep Original** 关闭时以裁切结果替换源 Image Empty;开启时 SHALL 为本次裁切创建一个结果 Image Empty,并保留源 Image Empty 的对象、图片、变换和显示设置。 + +#### Scenario: User crops with Keep Original disabled +- **WHEN** 用户在 Keep Original 关闭时完成任一 Crop 操作 +- **THEN** 裁切结果替换源 Image Empty,场景中不额外保留操作前的源对象 + +#### Scenario: User crops with Keep Original enabled +- **WHEN** 用户在 Keep Original 开启时完成任一 Crop 操作 +- **THEN** 场景同时包含未改变的源 Image Empty 和本次裁切的结果 Image Empty +- **AND** 结果 Image Empty 被选中并作为 active object +- **AND** 源 Image Empty 不再保持选中 + +#### Scenario: User continues cropping the result +- **WHEN** 用户在 Keep Original 开启时完成一次裁切后继续操作当前 active object +- **THEN** 下一次裁切以刚创建的结果 Image Empty 作为输入 +- **AND** 前一次源 Image Empty 仍保留在场景中 + +### Requirement: Cutout exposes the inward local axis +Cutout Tool SHALL 提供持久保存的 **Inward Axis** 选项,可选 `-Z` 与 `+X`,默认值 SHALL 为 `-Z`;该选项 SHALL 决定生成 Mesh Object 在局部坐标中从纸面指向内部的轴向。 + +#### Scenario: User creates a Cutout with the default axis +- **WHEN** 用户未修改 Inward Axis 并创建任一 Cutout Shape +- **THEN** 结果 Mesh 的纸面位于局部 XY 平面,向内方向为局部 `-Z` +- **AND** 结果的局部坐标轴与源 Image Empty 的局部坐标轴一致 + +#### Scenario: User chooses the legacy axis +- **WHEN** 用户把 Inward Axis 设为 `+X` 并创建任一 Cutout Shape +- **THEN** 结果 Mesh 的纸面位于局部 YZ 平面,向内方向为局部 `+X` +- **AND** 结果保持变更前的 Cutout 局部坐标约定 + +### Requirement: Cutout orientation preserves world-space output +Cutout SHALL 对 Surface、Balloon、Depth Balloon 与 Depth Surface 一致应用所选 Inward Axis,同时保持结果在源 Image Empty 图片平面上的世界空间位置、尺寸、纹理方向和纸面正反语义不变。 + +#### Scenario: Equivalent shapes use different local axes +- **WHEN** 用户从同一源 Image Empty 和 Selection 分别以 `-Z` 与 `+X` 创建相同 Shape +- **THEN** 两个结果的对应表面点在世界空间重合 +- **AND** 两个结果只在 Mesh Object 的局部坐标与对象变换约定上不同 + +#### Scenario: Oriented Depth Surface uses an Object Space Normal Map +- **WHEN** 用户以任一 Inward Axis 创建启用 Normal Map 的 Depth Surface +- **THEN** Object Space Normal Map 按所选局部坐标约定着色 +- **AND** 可见表面法线方向与对应几何法线一致 + diff --git a/openspec/specs/cutout-adaptive-mesh/spec.md b/openspec/specs/cutout-adaptive-mesh/spec.md new file mode 100644 index 0000000..e776bd7 --- /dev/null +++ b/openspec/specs/cutout-adaptive-mesh/spec.md @@ -0,0 +1,101 @@ +# cutout-adaptive-mesh Specification + +## Purpose +TBD - created by archiving change improve-cutout-adaptive-mesh. Update Purpose after archive. +## Requirements +### Requirement: Fine Outline selects the mesh algorithm +系统 SHALL 提供默认开启的 Fine Outline 设置。开启 SHALL 使用精细轮廓和连续内部支撑算法;关闭 SHALL 使用原始轮廓平滑、概括与均匀布点算法。以下精细网格要求适用于开启状态。 + +#### Scenario: User chooses abstraction for a fern +- **WHEN** 用户关闭 Fine Outline 并创建任一 Cutout Shape +- **THEN** 系统使用原算法,Mesh Detail 和 Alpha Threshold 继续有效 + +#### Scenario: New scene uses fine outlines +- **WHEN** Scene 或 Operator 使用默认属性 +- **THEN** Fine Outline 为开启状态,界面在 Mesh Detail 旁提供切换 + +#### Scenario: Settings change during an AI job +- **WHEN** 用户打开 Shape 菜单后修改 Scene 的 Fine Outline +- **THEN** 已发起操作的本地或 AI 返回创建使用菜单捕获的模式 + +### Requirement: Detail controls actual contour accuracy +系统 SHALL 根据有效 Mesh Detail 同时确定内部目标间距和源轮廓近似精度,保留 Low 的形状概括;High 与 Ultra SHALL 从源轮廓恢复更精细的边缘,而非仅拆分已有直边。系统 SHALL 分别记录相对源轮廓的双向采样误差和网格数量。 + +#### Scenario: Curved outline is refined at higher detail +- **WHEN** 未触及网格上限的弧线样本分别使用 Low、High、Ultra 构建网格 +- **THEN** High 与 Ultra 的实际边界包含恢复曲率的新位置,双向 P95 误差低于 Low,Ultra 在规定数值容差内不劣于 High + +#### Scenario: Source-pixel spacing and mesh limit remain effective +- **WHEN** 用户选择四档 Mesh Detail 或输入触及现有网格上限 +- **THEN** 内部目标间距沿用 32、16、8、4 源像素及上限调整规则,轮廓精度采用相应有效档位 + +### Requirement: Cleanup preserves connected thin structures +系统 SHALL 按独立前景区域面积过滤极小碎片,默认阈值为 16 源像素,保留最大区域。系统 SHALL 保留主体连接的细长结构、有效孔洞及原材质 Alpha。 + +#### Scenario: Tiny islands accompany an insect +- **WHEN** 昆虫主体、与主体相连的单像素细腿和多个小于阈值的独立岛同时存在 +- **THEN** 系统移除极小独立岛,保留主体和细腿,原图片像素保持一致 + +#### Scenario: Entire subject is smaller than the cleanup threshold +- **WHEN** 唯一有效区域小于默认面积阈值 +- **THEN** 最大区域被保留并获得有效网格和内部支撑 + +#### Scenario: Empty selection contains no foreground +- **WHEN** 当前阈值下没有有效前景 +- **THEN** 系统返回现有无有效 Selection 的错误结果,不创建空对象 + +### Requirement: Thin branches retain continuous interior support +系统 SHALL 为过滤后可表示的细长分支提供稀疏连续的内部网格路径,覆盖端部和关节。路径 SHALL 位于有效区域内部,分支连通性 SHALL 在密度变化后维持;已断开的 Alpha 区域独立处理。 + +#### Scenario: Leg changes width at a joint +- **WHEN** 细腿经较宽关节继续延伸,且内部目标间距大于腿宽 +- **THEN** 腿内保持单列内部支撑与连续实际网格边,关节不会因宽度筛选丢失连接 + +#### Scenario: Density and translation change +- **WHEN** 同一弯腿样本在 Low、Medium、High、Ultra 和平移 1 像素后生成 +- **THEN** 各次端部与关节检查位置均由同一内部路径连通,保留路径端点的 Balloon 高度为正 + +#### Scenario: Foreground components are separated by transparency +- **WHEN** 两个细结构之间存在透明间隙 +- **THEN** 系统分别构建各自内部支撑,不跨透明间隙建立连接 + +### Requirement: Constraint validation uses the final mesh +系统 SHALL 在最终三角化结果中验证所需内部约束的实际边覆盖、连通性和内部顶点身份,SHALL 拒绝跨边界、穿孔洞、边界接触和丢失约束的结果。失败 SHALL 触发有效修复或明确错误。 + +#### Scenario: Simplified path would cross a hole +- **WHEN** 路径概括的候选线段穿过孔洞或碰到其边界 +- **THEN** 系统使用有效内部路径重建该段,不能将候选线段直接送入最终网格 + +#### Scenario: Triangulation splits an input constraint +- **WHEN** CDT 将一条内部约束拆分为多个实际网格边 +- **THEN** 系统验证这些边完整覆盖输入约束且首尾相接,而非要求输入边原样保留 + +#### Scenario: Short constraints at large pixel coordinates +- **WHEN** 高分辨率图片的短内部约束经 CDT 发生坐标舍入 +- **THEN** 系统通过原始顶点和边映射验证实际约束链的端点连通,有效约束不因长度舍入误报;真实断线、被过滤的约束边和丢失端点仍报错 + +### Requirement: Ordinary interior sampling remains uniform +系统 SHALL 固定轮廓与稀疏内部路径、均匀化其余内部点,并以局部细分控制边界和内部的尺寸过渡。均匀化 SHALL 有确定的迭代上限和收敛停止条件。 + +#### Scenario: Broad cartoon region accompanies narrow appendages +- **WHEN** 卡通图像包含宽阔中央区域与细耳朵或细腿 +- **THEN** 中央普通点保持接近目标间距,细部使用稀疏内部支撑;通过内部最近邻离散度和邻面尺寸比验收,不能只增加中央点数掩盖不均匀 + +### Requirement: Each creation uses its current inputs +系统 SHALL 在每次创建时根据当前像素、Selection、阈值和有效间距准备结构,计算期间复用分量内的几何查询数组。 + +#### Scenario: Detail or image pixels change +- **WHEN** 用户改变 Mesh Detail 或 Alpha 后再次创建 +- **THEN** 系统从新输入重建轮廓与内部结构 + +### Requirement: Performance is measured with quality checks enabled +系统 SHALL 在四档密度下分别测量两种模式的数值构建与 Blender 创建,保留完整约束检查。基准 SHALL 使用相同已加载 Alpha、固定环境、预热和多次重复,并同时记录点数、轮廓误差、连通性与内存规模。 + +#### Scenario: Optimized implementation is accepted +- **WHEN** 正式实现进行性能验收 +- **THEN** 四张基准图的几何质量通过,记录与原算法的耗时比较及复杂蕨叶的实际开销;首次生成计时包含过滤、轮廓、结构、布点、三角化和高度求解,精细模式不能通过静默切换原算法满足计时要求 + +#### Scenario: Large input reaches the resource limit +- **WHEN** 2K 或 4K 输入包含大量孔洞和独立区域 +- **THEN** 系统验证网格上限、按分量的有界批次与峰值内存,不能使用小图或缓存计时替代该测量 + diff --git a/openspec/specs/cutout-attribute-lifecycle/spec.md b/openspec/specs/cutout-attribute-lifecycle/spec.md new file mode 100644 index 0000000..ec72e6b --- /dev/null +++ b/openspec/specs/cutout-attribute-lifecycle/spec.md @@ -0,0 +1,47 @@ +# cutout-attribute-lifecycle Specification + +## Purpose +TBD - created by archiving change optimize-cutout-geometry-nodes. Update Purpose after archive. +## Requirements +### Requirement: Internal Cutout attributes use the `_o_` prefix + +Attributes that exist only inside the Cutout geometry node lifecycle and are removed before output SHALL use the `_o_` prefix. The system SHALL NOT use the previous `.o_anyimage_*` prefix for new internal attributes. + +#### Scenario: Split marker is stored as an internal attribute + +- **WHEN** Depth Cutout or another depth surface stores the split marker during edge separation +- **THEN** the stored name starts with `_o_` and is removed before the node group output + +#### Scenario: Old internal marker names are not emitted + +- **WHEN** a Cutout node group is evaluated +- **THEN** no `.o_anyimage_depth_cut` or `.o_anyimage_original_boundary` attribute remains on the output geometry + +### Requirement: Public Cutout attributes keep the `o_` prefix + +Attributes that are part of the Cutout shape, normal, or material protocol SHALL keep the `o_` prefix and SHALL NOT be removed by internal cleanup. + +#### Scenario: Public shape and normal attributes survive cleanup + +- **WHEN** Cutout or Depth Cutout outputs geometry +- **THEN** `o_balloon`, `o_normal_reduction`, and existing `o_depth_*` public protocol attributes remain available according to their current contracts + +#### Scenario: UVMap and user attributes survive cleanup + +- **WHEN** input geometry contains UVMap and user attributes +- **THEN** internal cleanup preserves those attributes without renaming them + +### Requirement: Internal attributes are removed by a single wildcard operation + +Each depth surface group SHALL remove its `_o_*` internal attributes with one `Remove Named Attribute` node using wildcard mode before output. It SHALL NOT remove internal attributes one by one. + +#### Scenario: Wildcard cleanup removes all internal attributes + +- **WHEN** a depth surface group evaluates with Split or Thickness enabled +- **THEN** every `_o_*` attribute is absent from the output geometry + +#### Scenario: Wildcard cleanup is safe when no internal attribute exists + +- **WHEN** a depth surface group evaluates with the corresponding internal path disabled +- **THEN** the wildcard removal produces no missing-attribute warning and leaves public attributes unchanged + diff --git a/openspec/specs/cutout-axis-convention/spec.md b/openspec/specs/cutout-axis-convention/spec.md new file mode 100644 index 0000000..a825177 --- /dev/null +++ b/openspec/specs/cutout-axis-convention/spec.md @@ -0,0 +1,65 @@ +# cutout-axis-convention Specification + +## Purpose +TBD - created by archiving change standardize-cutout-z-axis. Update Purpose after archive. +## Requirements +### Requirement: Cutout base mesh matches the source image plane +系统 SHALL 直接在源 Image Empty 的局部 XY 平面创建 Cutout BaseShape,基础顶点 MUST 使用 `z = 0`,逆时针正面 MUST 朝局部 `+Z`,且对象放置 MUST 保持裁切区域的世界空间位置。 + +#### Scenario: Inspect a Cutout before Geometry Nodes evaluation +- **WHEN** 系统已从源图片裁切区域创建 Cutout Mesh,但尚未求值或复用 `O Image Cutout` Modifier +- **THEN** Mesh 位于对象局部 XY 平面,正面朝向与源 Image Empty 一致,且 X/Y 范围对应原裁切区域 + +#### Scenario: Evaluate an undeformed Surface +- **WHEN** Surface Cutout 以零厚度和零平滑求值 +- **THEN** 求值前后的平面内坐标、正面朝向和世界空间位置保持一致,不执行轴交换或补偿旋转 + +### Requirement: Cutout Shape nodes use the local Z axis +`O Image Surface`、`O Image Balloon`、`O Image Depth Balloon` 与 `O Image Depth Surface` SHALL 接收局部 XY 平面几何,并统一使用局部 `-Z` 表示从图片平面向内的方向。外层 `O Image Cutout` 的默认 `-Z` 分支 MUST 直通该原生结果。平面内 X/Y 坐标 MUST 不因 Shape 求值而互换。 + +#### Scenario: Reuse a Shape group with XY geometry +- **WHEN** 任一 Cutout Shape 节点组直接接收位于 XY 平面的输入 Mesh +- **THEN** 节点组沿 Z 轴生成 Shape,并保持输入几何的 X/Y 方向 + +#### Scenario: Build Surface or Balloon thickness +- **WHEN** Surface 或 Balloon 使用正 Thickness 求值 +- **THEN** 厚度和 Balloon 高度沿局部 Z 轴展开,图片内部一侧位于负 Z 方向 + +#### Scenario: Apply depth deformation +- **WHEN** Depth Balloon 或 Depth Surface 使用非零 Depth Scale 求值 +- **THEN** 深度位置在统一的 XY / Z 对象空间中计算,不依赖 X 轴中间坐标系或最终轴变换 + +### Requirement: Cutout exposes selectable output axes +系统 SHALL 保留 **Inward Axis** Scene Property、Operator 参数和 `O Image Cutout` 节点接口。`-Z` MUST 为默认值并直接使用原生 XY / Z Shape 结果;选择 `+X` 时,外层节点组 MUST 将 Z 轴结果转换为 `+X` 内向对象空间,对象矩阵 MUST 执行配对补偿以保持相同世界空间形状。 + +#### Scenario: Create a Cutout from the tool +- **WHEN** 用户打开 Cutout 工具设置并选择任一 Shape +- **THEN** 工具显示并传递 **Inward Axis**,默认 `-Z` 创建结果直接沿用源 Image Empty 的局部轴 + +#### Scenario: Select the positive X output +- **WHEN** 用户把 **Inward Axis** 设为 `+X` +- **THEN** `O Image Cutout` 将原生 Z 轴 Shape 转换为 `+X` 内向的局部几何,对象矩阵同步补偿,世界空间位置和外观与默认结果一致 + +#### Scenario: Reuse a Shape subgroup directly +- **WHEN** 调用方绕过外层 `O Image Cutout` 并直接复用任一 Shape 子节点组 +- **THEN** 子节点组始终使用原生 XY / Z 坐标,不公开或模拟旧 X 轴内部约定 + +### Requirement: Depth Surface normals match the Z-axis object space +Depth Surface 的 Object Space Normal Artifact SHALL 编码原生 Z 轴 Cutout 对象空间;不可见区域 MUST 使用局部 `+Z` 中性法线。默认 `-Z` 输出 MUST 原样使用该图片;选择 `+X` 输出时,Blender MUST 将其转换到配对的 `+X` 对象空间。 + +#### Scenario: Encode a known object-space normal +- **WHEN** Server 为 Depth Surface 编码已知的相机空间法线 +- **THEN** 输出通道直接对应最终 XY / Z Cutout 对象空间,且无需 Blender 后处理即可作为 Object Space Normal 使用 + +#### Scenario: Load a Depth Surface normal image +- **WHEN** Blender 收到 `object-normal.png` 并以默认 `-Z` 创建 Depth Surface 材质 +- **THEN** 图片像素保持 Artifact 原值并以 Object Space 连接到材质 + +#### Scenario: Load a positive X Depth Surface normal image +- **WHEN** Blender 收到 `object-normal.png` 并以 `+X` 创建 Depth Surface 材质 +- **THEN** 图片通道按 Z 到 X 的配对轴变换转换一次,再以 Object Space 连接到材质 + +#### Scenario: Fill an invisible normal region +- **WHEN** Object Space Normal 的像素不在可见内容区域 +- **THEN** 该像素编码局部 `+Z`,不会在正面产生虚假的侧向法线 + diff --git a/openspec/specs/cutout-boundary-padding/spec.md b/openspec/specs/cutout-boundary-padding/spec.md new file mode 100644 index 0000000..80eb5ad --- /dev/null +++ b/openspec/specs/cutout-boundary-padding/spec.md @@ -0,0 +1,64 @@ +# cutout-boundary-padding Specification + +## Purpose +TBD - created by archiving change pad-cutout-material-edges. Update Purpose after archive. +## Requirements +### Requirement: Cutout boundary mapping follows the structural interior +The system SHALL derive one local inward direction from the final Cutout threshold Mask without selecting among content-based donor candidates. For each boundary location it SHALL sample at Boundary Padding pixels along that inward direction and transport the sample through the inner boundary band, boundary and outer padding band. + +#### Scenario: Outer and hole boundaries +- **WHEN** the final Cutout Mask contains an outer outline or a hole +- **THEN** its mapping points from that boundary into the adjacent filled Mask region + +#### Scenario: Thin structure +- **WHEN** the configured inward distance would leave the current Mask along the same ray +- **THEN** the mapping stops at the last position inside that Mask without changing direction or searching another donor + +#### Scenario: Undefined local direction +- **WHEN** a boundary-band location has no finite nonzero local inward direction +- **THEN** that location remains unchanged + +### Requirement: Material and depth share the Cutout boundary mapping +The system SHALL apply the same structural mapping to the independent Cutout material Color Image and optional Depth Image after raw AI artifacts return and before material or geometry consumption. It SHALL scale the mapping to each target image resolution. + +#### Scenario: Material padding +- **WHEN** Boundary Padding is positive +- **THEN** the material boundary band receives complete RGBA from its mapped internal location + +#### Scenario: Depth padding +- **WHEN** a Depth Cutout has positive Boundary Padding +- **THEN** its boundary band receives camera Z and validity from the mapped internal location and camera X/Y are reconstructed for each target pixel from the original intrinsics + +#### Scenario: Different resolutions +- **WHEN** material and Depth images have different dimensions +- **THEN** both mappings represent the same normalized Cutout boundary and proportional padding width + +### Requirement: Boundary processing is isolated from source and AI data +The system SHALL preserve the source Image, original Cutout Mask, mesh geometry, UV coordinates, MoGe input, Normal output, intrinsics and reference depth. The server SHALL generate raw Cutout Depth without edge extension. + +#### Scenario: AI Cutout +- **WHEN** Cutout requests Depth or Normal generation +- **THEN** MoGe processes the unpadded input and only the returned Color and Depth images receive client-side boundary processing + +#### Scenario: Existing depth calibration +- **WHEN** client-side Depth padding changes boundary pixels +- **THEN** the original Metadata reference depth remains the Cutout calibration reference + +### Requirement: Boundary Padding is configurable +The system SHALL expose `Boundary Padding` in Preferences under Cutout Tool as a pixel distance with a default of 2, a minimum of 1 and a maximum of 4. Each Cutout operation SHALL capture the current value and existing objects SHALL remain unchanged. + +#### Scenario: Asynchronous preference change +- **WHEN** the preference changes after an AI Cutout starts +- **THEN** its response uses the value captured when that operation started + +### Requirement: Processed images preserve their lifecycle +The system SHALL preserve existing Color and Depth precision, interpretation, ownership and cleanup behavior after client-side boundary processing. + +#### Scenario: Successful creation +- **WHEN** a padded Cutout is created successfully +- **THEN** its processed independent Color and optional Depth Images are packed with their updated pixels + +#### Scenario: Failed creation +- **WHEN** Cutout creation fails after loading either image +- **THEN** unowned Blender Images and temporary files are released by the existing cleanup path + diff --git a/openspec/specs/cutout-boundary-smoothing/spec.md b/openspec/specs/cutout-boundary-smoothing/spec.md new file mode 100644 index 0000000..637d61c --- /dev/null +++ b/openspec/specs/cutout-boundary-smoothing/spec.md @@ -0,0 +1,34 @@ +# cutout-boundary-smoothing Specification + +## Purpose +TBD - created by archiving change weight-cutout-boundary-smoothing. Update Purpose after archive. +## Requirements +### Requirement: Boundary-specific smoothing strength + +`O Image Depth Cutout` SHALL 使用单个 `Boundary Smooth` 输入控制边界平滑次数。Split 边 SHALL 使用完整 `Smooth Weight`,原始 Outline SHALL 使用 `0.1` 倍 `Smooth Weight`;两类影响相交时 Split 强度 MUST 优先。 + +#### Scenario: Separate Split and Outline response + +- **WHEN** 同一网格同时存在 Split 边与原始 Outline 且启用 Boundary Smooth +- **THEN** Split 边按完整 Smooth Weight 平滑 +- **AND** 仅受 Outline 影响的点按其 0.1 倍平滑 + +#### Scenario: Split and Outline intersection + +- **WHEN** Split 与 Outline 的两圈影响范围相交 +- **THEN** 相交区域使用两者中较强的 Split influence + +### Requirement: Boundary Smooth default and bypass + +`O Image Depth Cutout` 的 `Boundary Smooth` SHALL 默认为 `4`,保持范围 `0–16`;值为 `0` 时 MUST 完全旁路边界平滑。 + +#### Scenario: New node group defaults + +- **WHEN** 构建新的 O Image Depth Cutout 节点组 +- **THEN** Boundary Smooth 默认值为 4 + +#### Scenario: Disabled boundary smoothing + +- **WHEN** Boundary Smooth 为 0 +- **THEN** Split 边和 Outline 均不发生边界平滑位移 + diff --git a/openspec/specs/cutout-depth-extension/spec.md b/openspec/specs/cutout-depth-extension/spec.md new file mode 100644 index 0000000..5eb7d75 --- /dev/null +++ b/openspec/specs/cutout-depth-extension/spec.md @@ -0,0 +1,53 @@ +# cutout-depth-extension Specification + +## Purpose +TBD - created by archiving change restore-cutout-depth-extension. Update Purpose after archive. +## Requirements +### Requirement: Reliable depth sources retain the tested alpha rule + +系统 SHALL 按剪裁 Alpha 定义可见域和8连通分量;供体候选 SHALL 位于同一可见分量内,并满足固定 Alpha ≥ 0.95、Validity > 0.5、有限正深度。系统 SHALL 保留内侧2像素保护及旧版回退规则。 + +#### Scenario: Low clipping threshold retains translucent edges +- **WHEN** 剪裁 Alpha 为0.1,边缘 Alpha 为0.1且同分量存在可靠核心 +- **THEN** 边缘属于可见域但不能充当供体,其深度使用同分量可靠来源补齐 + +#### Scenario: Narrow component lacks a core +- **WHEN** 分量内缩2像素后没有可靠供体 +- **THEN** 使用本分量未内缩的可靠候选;候选也为空时保留该分量原预测 + +### Requirement: Extension preserves reliable interior and projection + +系统 SHALL 沿用旧算法补齐轮廓边缘及可见域外4个深度图像素,使用sigma=0.8、半径3像素的分量内局部平滑。系统 SHALL 保留内部受保护区域及未修补像素,按目标像素射线重建变化像素的XYZ。 + +#### Scenario: Filtered sampling crosses an outline +- **WHEN** 采样邻域跨越主体轮廓 +- **THEN** 邻近透明侧4像素范围提供延伸深度,各分量采用自身供体和平滑输入,其他可见分量保持各自处理结果 + +#### Scenario: Corrected points retain their pixel location +- **WHEN** 像素继承供体Z +- **THEN** XYZ仍投影至目标像素,归一化投影误差小于1e-6;原Alpha和validity保持不变 + +#### Scenario: Opaque input and interior validity holes +- **WHEN** 输入完全不透明,或透明图内部存在模型validity孔洞 +- **THEN** 完全不透明输入原样返回;内部孔洞只限制供体资格,不扩大轮廓修补区域 + +### Requirement: Only cutout depth artifacts use corrected frames + +系统 SHALL 仅由Cutout深度请求开启延伸,使用同一修复Frame写入深度和Metadata。共享入口默认 SHALL 关闭修复,Normal SHALL 使用原始Frame,EXR A SHALL 保持原Alpha乘原validity。 + +#### Scenario: Cutout requests depth and normals +- **WHEN** 同一Cutout请求同时生成深度和Normal +- **THEN** 只执行一次模型预测,深度及Metadata来自修复Frame,Normal来自原始Frame + +#### Scenario: Other artifact requests +- **WHEN** 普通Depth Plane、全景深度或仅Normal请求运行 +- **THEN** 沿用各自原始预测产物行为 + +### Requirement: Real image verification uses a fixed prediction + +验收 SHALL 在工作区外使用用户蜘蛛和头骨原图、固定同一Alpha与预测,在现有几何节点及相同参数下仅替换原始/延伸深度产物,记录截图、耗时与残余问题。 + +#### Scenario: Full modifier evaluation +- **WHEN** 两张图完成深度产物对照 +- **THEN** 全部结果非空、坐标有限、无节点警告,报告一次补边与节点求值的独立耗时,并检查Normal着色 + diff --git a/openspec/specs/cutout-edge-depth/spec.md b/openspec/specs/cutout-edge-depth/spec.md new file mode 100644 index 0000000..a7da07f --- /dev/null +++ b/openspec/specs/cutout-edge-depth/spec.md @@ -0,0 +1,69 @@ +# cutout-edge-depth Specification + +## Purpose +TBD - created by archiving change extend-cutout-edge-depth. Update Purpose after archive. +## Requirements +### Requirement: Reliable interior supplies edge depth + +系统 SHALL 为包含透明内容的 Cutout 深度产物,从 Alpha ≥ 0.95、Validity > 0 且深度有限为正的候选域中,选择欧氏内缩 2 个深度图像素后的可靠核心。可见域 SHALL 继续使用 Alpha ≥ 0.1,核心深度 SHALL 逐值保留。 + +#### Scenario: Hair samples contain background depth +- **WHEN** 半透明毛发仍属于可见域,但其预测深度接近背景,所属分量存在可靠核心 +- **THEN** 系统以该分量最近可靠核心的深度补齐毛发区域,并保留原颜色 Alpha 和裁切轮廓 + +#### Scenario: Subject reaches the image rectangle boundary +- **WHEN** 不透明主体延伸到图片矩形边界 +- **THEN** 系统按边缘延续计算内缩,不将图片外部自动视为透明背景 + +### Requirement: Extension has deterministic component fallbacks + +系统 SHALL 按可见域的 8 连通分量选择供体。缺少内缩核心时 SHALL 使用本分量未内缩的可靠候选;缺少可靠候选时 SHALL 保留该分量原始预测。 + +#### Scenario: Narrow component has no eroded core +- **WHEN** 细小分量的可靠候选在 2 px 内缩后消失 +- **THEN** 系统使用该分量未内缩候选完成外延 + +#### Scenario: Translucent component has no reliable candidates +- **WHEN** 独立分量完全由低 Alpha 或模型无效像素组成 +- **THEN** 系统保留该分量预测,不从另一可见分量复制深度 + +### Requirement: Filtered sampling uses extended depth + +系统 SHALL 向可修复分量相邻透明区外扩 4 px 的范围延伸深度,并以 σ=0.8 px、半径 3 px 的 Gaussian 核仅平滑补齐区域。核心及其他未处理区域 SHALL 保留原值。 + +#### Scenario: Texture sampling straddles the silhouette +- **WHEN** 线性采样邻域跨越可见边缘和邻近透明背景 +- **THEN** 透明侧提供外延深度,采样不会重新混入该邻域原来的背景深度 + +#### Scenario: Adjacent components have different depths +- **WHEN** 两个可见分量分别位于不同深度 +- **THEN** 各分量的供体与平滑输入保持分量隔离 + +### Requirement: Corrected geometry preserves camera projection + +系统 SHALL 根据修复后的 Z 和当前像素中心的相机射线重建 XYZ,使修复前后的相机投影保持一致,并保留核心原始 XYZ。 + +#### Scenario: Edge pixel inherits an interior depth +- **WHEN** 边缘像素采用另一像素的可靠 Z +- **THEN** 重建 XYZ 仍投影到边缘像素自身位置,而不是供体位置;数值测试的归一化投影误差 SHALL 小于 1e-6 + +### Requirement: Cutout artifacts share corrected depth + +系统 SHALL 为 Depth Surface 与 Depth Balloon 的 Vector/Z Depth 和 Metadata 使用同一修复后深度。模型 Validity SHALL 保留其原始语义,Normal Writer SHALL 继续使用原始预测。 + +#### Scenario: Cutout exports vector depth and metadata +- **WHEN** Cutout 请求深度产物 +- **THEN** Vector Depth 的 Z 与 Metadata 参考深度来自同一修复后深度场,所有输出坐标有限 + +#### Scenario: Existing artifact paths do not require correction +- **WHEN** 输入完全不透明,或请求来自普通 Depth Plane,或 Cutout 只生成 Normal +- **THEN** 系统使用对应的原始产物路径与预测行为 + +### Requirement: Edge correction is verified in the full Cutout pipeline + +验收 SHALL 使用猫与人物原图依次执行 Remove Background 和 Cutout,固定同一 Alpha 与模型预测比较修复前后结果,并检查 Depth Surface、Depth Balloon 及 Normal 开关组合。 + +#### Scenario: Side-view regression comparison +- **WHEN** 两张实图完成完整 Blender 修改器求值 +- **THEN** 主要边缘长条后缩明显减少,原 Alpha 可见域保持一致,可靠核心深度逐值不变,并记录残余台阶、着色或剔除问题 + diff --git a/openspec/specs/cutout-edge-length/spec.md b/openspec/specs/cutout-edge-length/spec.md new file mode 100644 index 0000000..3171546 --- /dev/null +++ b/openspec/specs/cutout-edge-length/spec.md @@ -0,0 +1,69 @@ +# cutout-edge-length Specification + +## Purpose +TBD - created by archiving change use-world-space-cutout-edge-length. Update Purpose after archive. +## Requirements +### Requirement: Physical edge length control + +系统 SHALL 提供正值浮点长度参数 Edge Length,初始物理默认值为 0.1 m,遵循 Blender 场景单位显示与换算。 + +#### Scenario: Default length +- **WHEN** 用户首次使用 Cutout +- **THEN** Edge Length 的初始值表示 0.1 m,并可连续调整 + +#### Scenario: Scene unit conversion +- **WHEN** 场景 Unit Scale 为 0.01,用户输入 0.1 m 且相对限制未触发 +- **THEN** 建网目标间距为 10 个 Blender 世界单位 + +### Requirement: Relative spacing floor + +系统 SHALL 在 Preferences 提供 Minimum Relative Edge Length 百分比,默认 1%,并使用 `max(请求边长, 有效裁切包围盒世界长边 × 最小比例)` 计算有效目标间距。包围盒 SHALL 来自参与建网的有效内容,包含所有组件,采用图像平面轴向边经过世界变换后的较长边。 + +#### Scenario: Floor is applied +- **WHEN** 包围盒长边为 1 m,最小比例为 1%,请求边长为 0.001 m +- **THEN** 有效目标间距为 0.01 m + +#### Scenario: Requested spacing is retained +- **WHEN** 包围盒长边为 1 m,最小比例为 1%,请求边长为 0.1 m +- **THEN** 有效目标间距为 0.1 m + +#### Scenario: Preference changes +- **WHEN** 用户将最小比例改为 2%,包围盒长边为 1 m,请求边长为 0.001 m +- **THEN** 有效目标间距为 0.02 m + +### Requirement: Stable world density + +系统 SHALL 根据有效世界间距驱动普通轮廓与 Fine Outline 的基础采样。图片分辨率与有效内容外的画布 SHALL 不参与目标间距定义;对象世界变换 SHALL 参与空间长度计算。 + +#### Scenario: Resolution and canvas changes +- **WHEN** 相同物理尺寸和轮廓的主体更换图像分辨率或增加透明画布,参数保持一致 +- **THEN** 两种建网模式采用相同的有效目标间距 + +#### Scenario: Object scaling below the floor threshold +- **WHEN** 用户放大 Image Empty 且相对限制仍未触发 +- **THEN** 目标边长保持一致,放大的区域获得更多采样点 + +#### Scenario: Rotation and nonuniform scale +- **WHEN** 对象旋转或非均匀缩放 +- **THEN** 两种建网模式在世界平面度量中应用有效间距,旋转不改变包围盒长边基准 + +### Requirement: Detail preserving spacing semantics + +系统 SHALL 将长度限制解释为基础采样目标,允许轮廓适配和三角剖分产生更短的边,并保持 Fine Outline 的细小部件支持。 + +#### Scenario: Thin outline feature +- **WHEN** Fine Outline 处理宽度小于有效间距的可用细小轮廓 +- **THEN** 建网可增加局部采样并生成较短边,以保留该轮廓 + +### Requirement: Effective length feedback and parameter replacement + +系统 SHALL 在相对限制生效后显示有效目标长度,并保留用户请求长度。系统 SHALL 使用新长度属性和相对偏好替换旧 Mesh Detail 枚举与最大网格分辨率设置。 + +#### Scenario: Visible clamp result +- **WHEN** 用户请求 0.1 m,但实际采用 0.2 m +- **THEN** 界面反馈显示有效长度 0.2 m,用户设置仍为 0.1 m + +#### Scenario: Consistent execution paths +- **WHEN** 用户通过同步或 AI 异步路径生成 Cutout +- **THEN** 系统依据最终建网内容采用同一套有效边长规则 + diff --git a/openspec/specs/cutout-geometry-node-efficiency/spec.md b/openspec/specs/cutout-geometry-node-efficiency/spec.md new file mode 100644 index 0000000..96216ea --- /dev/null +++ b/openspec/specs/cutout-geometry-node-efficiency/spec.md @@ -0,0 +1,75 @@ +# cutout-geometry-node-efficiency Specification + +## Purpose +TBD - created by archiving change optimize-cutout-geometry-nodes. Update Purpose after archive. +## Requirements +### Requirement: Depth Cutout reuses the projected outline field + +`O Image Depth Cutout` SHALL calculate the POINT/BOOLEAN outline field once on the post-split, pre-projection topology and reuse it for boundary-sensitive consumers on the same topology after projection. It SHALL NOT create separate Edge Neighbors and Vertex Neighbors fields for each consumer in the same topology stage. + +#### Scenario: Same topology boundary consumers share the field + +- **WHEN** Depth Cutout builds volume fields, original boundary sampling, and boundary smoothing after depth projection +- **THEN** these consumers read the same projected outline field, and the output geometry matches the prior implementation for zero and positive thickness + +#### Scenario: Topology changes still receive a fresh boundary field + +- **WHEN** Split Edges or boundary triangle cleanup changes topology before projection +- **THEN** the shared outline field is evaluated after that topology change, not reused across the destructive operation + +### Requirement: Center field derives from shared low-frequency fields + +`O Image Depth Cutout` SHALL compute the smoothed depth and profile fields with the existing 256-iteration interior-weighted blur, and SHALL derive the center field as `depth_slow - profile_slow` instead of running an additional equivalent blur on `depth - profile`. + +#### Scenario: Center field is equivalent to the direct blur + +- **WHEN** Depth Cutout evaluates a Balloon input with nonzero `o_balloon` and positive Thickness +- **THEN** the derived center values are equal to the prior direct center blur values within floating-point tolerance + +#### Scenario: Uniform surface keeps the same center behavior + +- **WHEN** Depth Cutout evaluates a zero-profile uniform surface +- **THEN** the derived center field produces the same front and rear positions as the prior implementation + +### Requirement: Depth Cutout uses triangle-only strip cleanup + +`O Image Depth Cutout` SHALL build only the triangle strip cleanup path because its input is triangulated. It SHALL NOT construct the quad strip cleanup branch used by other depth surfaces. + +#### Scenario: Triangular strips are cleaned without quad nodes + +- **WHEN** Depth Cutout receives a triangulated cutout mesh with thin boundary strips +- **THEN** strip faces are removed and the remaining geometry matches the prior triangle cleanup result + +#### Scenario: Other depth surfaces keep their existing cleanup path + +- **WHEN** Depth Plane or Depth Panorama evaluates a quad mesh +- **THEN** their existing quad strip cleanup behavior is unchanged + +### Requirement: Zero thickness short-circuits shell construction + +Both `O Image Cutout` and `O Image Depth Cutout` SHALL return the original single-surface geometry when the selected thickness input is below the existing zero threshold, without evaluating shell extrusion or thickness field sampling. + +#### Scenario: Cutout Balloon and Shell remain single surface at zero + +- **WHEN** Mode is Balloon or Shell and the selected Thickness or Shell Thickness is zero +- **THEN** the output is the original Cutout surface with no shell faces, and no thickness-related node warnings occur + +#### Scenario: Depth Cutout keeps the projected front at zero thickness + +- **WHEN** Depth Cutout has zero selected thickness +- **THEN** the output is the projected front surface, and thickness-only fields and shell geometry are not evaluated + +### Requirement: Node graph optimization preserves Cutout geometry + +The boundary reuse, center derivation, triangle-only cleanup, and zero-thickness short-circuit SHALL NOT change the finite output geometry, face order, UVMap, or public attributes for supported Cutout and Depth Cutout combinations. + +#### Scenario: Representative mode and thickness matrix is equivalent + +- **WHEN** Cutout and Depth Cutout are evaluated with Balloon/Shell modes, zero and positive thickness, Depth Split zero and positive, and representative input shapes +- **THEN** vertex positions and face topology are equivalent to the prior implementation within the existing test tolerance + +#### Scenario: High-density mesh remains closed with thickness + +- **WHEN** Depth Cutout uses a mesh with more than 4096 source vertices and positive thickness +- **THEN** the thickened shell remains finite and closed with no non-manifold boundary + diff --git a/openspec/specs/cutout-lasso-entry/spec.md b/openspec/specs/cutout-lasso-entry/spec.md new file mode 100644 index 0000000..1523d6f --- /dev/null +++ b/openspec/specs/cutout-lasso-entry/spec.md @@ -0,0 +1,27 @@ +# cutout-lasso-entry Specification + +## Purpose +TBD - created by archiving change add-projected-depth-plane. Update Purpose after archive. +## Requirements +### Requirement: Cutout starts shape generation from a lasso +Cutout SHALL 在用户提交有效套索后打开 Shape Pie Menu,并将圈选路径传给 Shape 生成入口。 + +#### Scenario: Submit a lasso +- **WHEN** 用户在 active Image Empty 上完成有效 Cutout 套索 +- **THEN** 系统打开当前可用 Shape 菜单并传递该套索路径 + +### Requirement: Full-image double-click entry is removed +系统 MUST 删除 Cutout 的双击整图 keymap、`full_image` Property、整图 Selection 构造分支及专属调用代码。双击 MUST NOT 打开整图 Shape 菜单或提交整图生成任务。 + +#### Scenario: Double-click an image +- **WHEN** Cutout 已激活,用户在 Image Empty 上双击且未形成有效套索 +- **THEN** 不打开 Shape 菜单、不创建对象、不提交生成 Job + +#### Scenario: Double-click viewport space +- **WHEN** active Image Empty 存在,用户在图片外空白区域双击 +- **THEN** 不启动整图 Cutout,选择处理沿用共享图片手势规则 + +#### Scenario: Inspect registration +- **WHEN** 检查 Cutout 的 Operator Property 和 WorkspaceTool keymap +- **THEN** 不存在 `full_image` 或用于整图生成的 `DOUBLE_CLICK` 绑定 + diff --git a/openspec/specs/cutout-mesh-sampling/spec.md b/openspec/specs/cutout-mesh-sampling/spec.md new file mode 100644 index 0000000..e9b89e6 --- /dev/null +++ b/openspec/specs/cutout-mesh-sampling/spec.md @@ -0,0 +1,27 @@ +# cutout-mesh-sampling Specification + +## Purpose +TBD - created by archiving change fix-cutout-density-and-image-edit-names. Update Purpose after archive. +## Requirements +### Requirement: Mesh Detail preserves source-pixel spacing +系统 SHALL 将 Cutout 的 Low、Medium、High、Ultra 分别解释为相邻网格采样点之间 32、16、8、4 个源图像素,并在未触及网格上限时保持该间距。 + +#### Scenario: Low samples every 32 source pixels +- **WHEN** 用户以 Low 创建一个未触及网格上限的 Cutout +- **THEN** 系统按每 32 个源图像素一次的间距构建网格 + +#### Scenario: Upscale increases linear mesh density +- **WHEN** 同一图片内容在线性宽高各 Upscale 4 倍后,以相同 Mesh Detail、相同相对 Selection 和相同显示尺寸创建 Cutout,且两次均未触及网格上限 +- **THEN** Upscale 结果在同一线性内容范围内获得约 4 倍网格采样密度 + +### Requirement: Cutout mesh limit caps actual sampling resolution +系统 SHALL 仅在 Cutout 实际几何范围的预期采样长边超过 **Maximum Cutout Mesh Resolution** 时增大采样间距,并 SHALL NOT 按 Selection bounds 的原始像素长边直接缩放已经由 Mesh Detail 得到的密度。 + +#### Scenario: Large pixel bounds remain below the sampling limit +- **WHEN** Selection bounds 的像素长边超过配置值,但按当前 Mesh Detail 计算的实际采样长边没有超过配置值 +- **THEN** 系统保持该 Mesh Detail 对应的源像素采样间距 + +#### Scenario: Actual sampling reaches the configured limit +- **WHEN** Cutout 实际几何范围按当前 Mesh Detail 计算的采样长边超过配置值 +- **THEN** 系统等比增大网格采样间距,使实际采样长边不超过配置值 + diff --git a/openspec/specs/cutout-normal-policy/spec.md b/openspec/specs/cutout-normal-policy/spec.md new file mode 100644 index 0000000..4d5bb49 --- /dev/null +++ b/openspec/specs/cutout-normal-policy/spec.md @@ -0,0 +1,49 @@ +# cutout-normal-policy Specification + +## Purpose +TBD - created by archiving change reorganize-cutout-shapes. Update Purpose after archive. +## Requirements +### Requirement: Normal Map 勾选仅控制本地形态 +Normal Map 勾选 SHALL 仅控制 Flat 和 Solid 的法线生成;两个 Depth 入口 SHALL 始终生成并使用法线图,且不改写用户保存的勾选值。工具说明 SHALL 明确该作用范围。 + +#### Scenario: Flat 与 Solid 关闭法线 +- **WHEN** AI 可用且勾选关闭,用户创建 Flat 或 Solid +- **THEN** 系统使用 NONE 法线模式 + +#### Scenario: Flat 与 Solid 开启法线 +- **WHEN** AI 可用且勾选开启,用户创建 Flat 或 Solid +- **THEN** 系统请求 TANGENT 法线并连接结果材质 + +#### Scenario: Depth Solid 自动法线 +- **WHEN** 用户创建 Depth Solid,无论勾选保存为真或假 +- **THEN** 系统请求 OBJECT 法线并正确加载、按输出轴向处理和连接材质 + +#### Scenario: Depth Balloon 自动法线 +- **WHEN** 用户创建 Depth Balloon,无论勾选保存为真或假 +- **THEN** 系统请求 TANGENT 法线并连接结果材质 + +### Requirement: 所有调用路径采用一致策略 +法线策略 SHALL 在 Operator 业务层统一解析,Job 参数与结果接收使用同一策略。 + +#### Scenario: 绕过 Pie 调用 +- **WHEN** 直接调用 Operator 创建任一 Depth 形态并传入 generate_normal 为 false +- **THEN** 法线仍按该深度入口生成和应用,与 Pie 调用结果一致 + +### Requirement: AI 可用性继续限制入口 +AI 未就绪时系统 SHALL 仅提供 Flat 与 Solid,忽略遗留的开启法线选项;AI 就绪时 SHALL 提供四个入口。 + +#### Scenario: 未就绪的本地创建 +- **WHEN** AI 未就绪、保存的 Normal Map 为真,用户创建 Flat 或 Solid +- **THEN** 创建过程不因该遗留值启动 AI Job,法线模式为 NONE + +### Requirement: 法线适配跟随当前几何 +Flat 与 Solid 共用节点组后的法线适配 SHALL 支持厚度变化,Depth 入口 SHALL 保持各自法线空间和轴向转换正确。 + +#### Scenario: Flat 增厚 +- **WHEN** 带法线的 Flat 调整为 Balloon 正厚度 +- **THEN** 材质按当前几何参与法线适配,不因初始 Flat 预设而缺失 Balloon 所需连接 + +#### Scenario: 深度入口轴向与双面 +- **WHEN** 分别检查两个 Depth 入口的 -Z、+X 输出,以及 Depth Balloon 的 Double Sided 开关 +- **THEN** 对应法线图被正确加载和连接,前后表面的着色方向与几何一致 + diff --git a/openspec/specs/cutout-polyline/spec.md b/openspec/specs/cutout-polyline/spec.md new file mode 100644 index 0000000..9949ac2 --- /dev/null +++ b/openspec/specs/cutout-polyline/spec.md @@ -0,0 +1,61 @@ +# cutout-polyline Specification + +## Purpose +TBD - created by archiving change add-cutout-polyline. Update Purpose after archive. +## Requirements +### Requirement: Cutout offers persistent gesture selection + +Cutout SHALL 提供 Lasso 和 Polyline 手势,默认 Lasso,并保存到 Scene 的独立 Cutout 设置。 + +#### Scenario: User selects Polyline +- **WHEN** 用户将 Cutout Gesture 切换为 Polyline 并开始剪出 +- **THEN** 工具以首次左键按下的位置为起点,随后通过点击添加顶点 + +#### Scenario: User returns to Cutout +- **WHEN** 用户选择 Polyline 后切换到其他工具再返回 +- **THEN** Cutout 继续使用 Polyline,Mask Gesture 保持自己的设置 + +#### Scenario: Default Lasso interaction +- **WHEN** 新 Scene 中用户拖动 Cutout 并释放左键 +- **THEN** 工具以 Lasso 收集轮廓并在有效选区完成后打开 Shape 饼菜单 + +### Requirement: Cutout reuses shared Polyline interaction + +Cutout MUST 复用 Mask 所用的共用 Polyline 手势,提供轮廓预览、起点闭合提示、左键添加顶点、Backspace 删除末点、点击起点或双击或 Enter 完成,以及 RMB、Esc 和切换工具取消。 + +#### Scenario: User completes a polygon +- **WHEN** 至少三个顶点后,用户点击起点闭合范围内、双击或按 Enter / Numpad Enter +- **THEN** 工具提交已确认的闭合 SelectionPath 并打开 Shape 饼菜单,闭合点击和双击第二击不添加重复顶点 + +#### Scenario: Too few points +- **WHEN** 少于三个顶点时用户尝试完成 +- **THEN** 工具继续收集顶点,不打开 Shape 菜单 + +#### Scenario: User edits or cancels a polygon +- **WHEN** 用户按 Backspace,或通过 RMB、Esc、切换工具取消 +- **THEN** Backspace 删除末点并至少保留起点;取消清理预览且不创建结果对象 + +#### Scenario: Empty click preserves the source +- **WHEN** 用户在空白处启动 Cutout 手势 +- **THEN** 工具保持现有源对象选择规则,不因空白点击清空源对象 + +### Requirement: Polyline initializes Depth Solid with Uniform mode + +通过 Polyline 创建 Depth Solid 时,系统 MUST 将新对象的 `O Image Depth Cutout` 修改器 Mode 初始化为 Uniform。手势 MUST 使用本次提交快照。 + +#### Scenario: Polyline depth result completes asynchronously +- **WHEN** 用户提交 Polyline 并选择 Depth Solid,在生成结束前将 Cutout Gesture 改为 Lasso +- **THEN** 本次结果的 Mode 仍为 Uniform,用户可在创建后手动调整 + +#### Scenario: Lasso or another shape is used +- **WHEN** 用户通过 Lasso 创建 Depth Solid,或通过 Polyline 创建 Flat、Solid、Depth Balloon +- **THEN** 对象沿用该 Shape 的现有 Mode 初始化规则 + +### Requirement: Uniform thickness defaults to half a unit + +`O Image Depth Cutout` 的 Uniform Thickness socket MUST 默认 0.5,保存资产中对应显示为 Thickness 的 socket 和新建修改器 MUST 使用该值。 + +#### Scenario: Node asset is built and loaded +- **WHEN** 完成节点资产构建并从资产加载节点组创建 Polyline Depth Solid +- **THEN** Mode 为 Uniform,Uniform 厚度值为 0.5,通用 Thickness 初始化不会覆盖该值 + diff --git a/openspec/specs/cutout-quality-meshing/spec.md b/openspec/specs/cutout-quality-meshing/spec.md new file mode 100644 index 0000000..8ad7bef --- /dev/null +++ b/openspec/specs/cutout-quality-meshing/spec.md @@ -0,0 +1,75 @@ +# cutout-quality-meshing Specification + +## Purpose +TBD - created by archiving change use-tangle-cutout-meshing. Update Purpose after archive. +## Requirements +### Requirement: Unified quality meshing +系统 SHALL 在 Fine Outline 两种状态下使用 Tangle Python 版构建最终基础三角网格,采用 10°质量目标及与有效世界边长 h 对应的最大面积 √3/4 × h²。系统 SHALL 保持现有世界 Edge Length、相对下限和非均匀缩放语义,允许细结构产生短于 h 的边。 + +#### Scenario: Shared density controls +- **WHEN** 相同物理主体更换分辨率、透明画布或对象旋转后创建 Cutout +- **THEN** 同一有效 h 驱动三角化;两种 Fine Outline 模式均使用 Tangle + +#### Scenario: Acute constrained feature +- **WHEN** 必须保留的细结构包含尖角或短约束 +- **THEN** 系统保留结构,报告实际质量统计,不以删除有效结构强行满足最短边或角度目标 + +### Requirement: Outline mode defines holes +系统 SHALL 在关闭 Fine Outline 时填充所有封闭内部孔洞并概括外轮廓;开启时仅在孔最长世界跨度 ≤ 0.25h 且面积 ≤ 0.04h² 时填孔,保留其他孔洞。系统 SHALL 从处理后区域重建组件,且不修改原图片和材质 Alpha。 + +#### Scenario: Fine mode keeps a moderately small hole +- **WHEN** 孔最长跨度为 0.9h,或细长孔的最长跨度大于 0.25h +- **THEN** 开启 Fine Outline 时孔被保留,即使孔面积很小 + +#### Scenario: Fine mode fills a micro hole +- **WHEN** 封闭孔满足两个微孔条件 +- **THEN** 开启 Fine Outline 也填充该孔,后续支撑和三角化使用填孔结果 + +#### Scenario: Coarse mode fills a nested hole +- **WHEN** 关闭 Fine Outline,内部孔包含独立前景岛 +- **THEN** 填孔后区域重新合并,不产生重叠面;与外部背景相通的凹口仍属于外轮廓 + +### Requirement: Continuous interior support +系统 SHALL 在两种模式下按实际质量网格补足内部支撑。系统 SHALL 拆分两端位于边界的内部弦,为孤立全边界三角形添加内部点,保持细分支经关节到主体的连续内部连接。 + +#### Scenario: Coarse branch requires support +- **WHEN** 低密度网格的细腿或根部缺少内部顶点 +- **THEN** 系统添加局部支撑,Poisson 求解后内部顶点高度为正,不产生全零高度三角形 + +#### Scenario: Dense region already has support +- **WHEN** 网格区域已经具有连续内部支撑 +- **THEN** 系统保持该区域,不生成额外全局中线约束,不重新三角化 + +### Requirement: Final constraints and topology are validated +系统 SHALL 验证面积、轮廓约束链、孔洞和组件数量、非退化性,且局部支撑拆分 SHALL 保持已验证的轮廓、拓扑与面朝向。像素角自接触 SHALL 做亚像素分离并重新验证。 + +#### Scenario: Pixel-corner hole contact +- **WHEN** 孔洞轮廓在像素角形成零宽自接触 +- **THEN** 系统分离接触点,生成拓扑有效且保留孔洞的网格 + +#### Scenario: Mesher loses a boundary constraint +- **WHEN** 后端遗漏边界约束或产生错误面积 +- **THEN** 系统明确失败,不创建残缺对象 + +### Requirement: Height support preserves mesh quality +系统 SHALL 在单次局部拆分中补足支撑,并限制累计顶点数。最终角度允许低于基础三角化的 10°目标,验收 SHALL 同时检查角度、连续正高度与压平面积。 + +#### Scenario: Connected depth samples collapse +- **WHEN** 深度投影将相邻顶点映射到数值上重合的位置 +- **THEN** 系统按当前平均边长的百万分之一合并已连接重合点,壳体厚度保持正确方向 + +### Requirement: Blender distribution and bounded execution +系统 SHALL 分发固定版本的 Tangle Python 源码和 MIT 许可,通过内存数值接口调用,仅使用标准库作为 Tangle 自身依赖。系统 SHALL 隔离调用状态,保持 Blender 数据创建在主线程,并对资源超限明确终止。 + +#### Scenario: Clean Blender installation +- **WHEN** 在项目支持的 Blender 环境加载插件并创建 Cutout +- **THEN** Tangle 无需用户 pip 安装或外部编译器即可调用,源码和许可被分发规则收录 + +#### Scenario: Repeated and failed calls +- **WHEN** 连续构建不同输入,包含一次无效输入或资源超限 +- **THEN** 后续调用不受先前状态影响,Decimal 上下文保持隔离,失败不创建残缺对象 + +#### Scenario: Local and AI return paths +- **WHEN** 同步创建或 AI 返回后创建 Cutout +- **THEN** 两者采用捕获的 Fine Outline 设置及同一有效世界边长与网格校验规则 + diff --git a/openspec/specs/cutout-shape-presets/spec.md b/openspec/specs/cutout-shape-presets/spec.md new file mode 100644 index 0000000..98d1c87 --- /dev/null +++ b/openspec/specs/cutout-shape-presets/spec.md @@ -0,0 +1,60 @@ +# cutout-shape-presets Specification + +## Purpose +TBD - created by archiving change reorganize-cutout-shapes. Update Purpose after archive. +## Requirements +### Requirement: 四个创建入口映射三个节点组 +系统 SHALL 提供 Flat、Solid、Depth Solid、Depth Balloon 四个入口,并默认使用 Solid。Flat 与 Solid SHALL 共用 O Image Cutout;Depth Solid SHALL 使用 O Image Depth Cutout;Depth Balloon SHALL 使用 O Image Depth Balloon。 + +#### Scenario: 普通默认创建 +- **WHEN** 用户以默认形态创建 Cutout +- **THEN** 对象使用 O Image Cutout,Thickness Mode 为 Balloon,Thickness 为 1 + +#### Scenario: 显式选择入口 +- **WHEN** 用户分别选择四个入口 +- **THEN** 系统按上述映射加载三个资产,Flat 的 Thickness 为 0,另外三个入口的 Thickness 为 1 + +### Requirement: 三个节点组直接输出最终几何 +三个 Cutout 节点组 SHALL 可直接作为 Geometry Nodes 修改器使用,并承担各自的清理、塑形、平滑与轴向输出。发布资产 MUST 移除旧 Shape 总切换器及旧 Surface、Balloon、Depth Surface 子组。 + +#### Scenario: 检查新建修改器与发布资产 +- **WHEN** 新建任一 Cutout 并检查其修改器与资产 +- **THEN** 修改器直接引用对应节点组,不存在跨四种形态的 Shape 输入;Cutout 资产集合恰为三个指定节点组 + +#### Scenario: 输出轴向 +- **WHEN** 对同一输入分别使用 -Z 与 +X 的 Inward Axis +- **THEN** 几何与对象矩阵配对转换,保持相同世界空间位置及正确朝向 + +### Requirement: 普通与深度 Cutout 具有双厚度方式 +O Image Cutout 与 O Image Depth Cutout SHALL 提供 Balloon、Uniform 两种 Thickness Mode,默认 Balloon。Balloon 厚度 SHALL 表示倍率,Uniform 厚度 SHALL 表示距离,输入语义与单位必须明确区分。 + +#### Scenario: 普通平面与体积转换 +- **WHEN** O Image Cutout 的 Balloon Thickness 从 0 调整到 1 +- **THEN** 相同基础网格由单层平面变为当前 Balloon 的正背面体积,无需重算 AI 或重建对象 + +#### Scenario: 等厚板片 +- **WHEN** O Image Cutout 选择 Uniform 并设置非零厚度 +- **THEN** 输出当前 Surface 的等厚板片形态;厚度为零时输出单层平面 + +#### Scenario: 厚度单位 +- **WHEN** 用户检查或切换两种 Thickness Mode +- **THEN** Balloon 的输入明确为倍率,Uniform 的输入明确为距离,不将一种单位的值隐式解释为另一种单位 + +### Requirement: Depth Balloon 默认双面 +O Image Depth Balloon SHALL 保留原 Depth Balloon 的参考平面塑形行为,并默认开启 Double Sided。 + +#### Scenario: 默认与单面输出 +- **WHEN** 用户创建 Depth Balloon,再关闭 Double Sided +- **THEN** 初始输出采用原算法的双面塑形,关闭后采用原算法的单面塑形,其余参数语义保持一致 + +### Requirement: 公共网格属性与前置清理 +所有入口 SHALL 准备 o_balloon;三个节点组 SHALL 在塑形前复用按原平面岛总面积清理小区域的 Cleanup 规则,并在输出保留 UVMap、o_balloon 与用户属性。 + +#### Scenario: Flat 后续增厚 +- **WHEN** 用户将 Flat 的 Thickness 调为正值 +- **THEN** 已有 o_balloon 可直接参与塑形,UV 与材质映射保持有效 + +#### Scenario: 小岛清理 +- **WHEN** 输入包含总面积低于 Cleanup 阈值的独立平面岛 +- **THEN** 三个节点组均在塑形前删除该岛,保留大于阈值的岛 + diff --git a/openspec/specs/cutout-split-boundary-shaping/spec.md b/openspec/specs/cutout-split-boundary-shaping/spec.md new file mode 100644 index 0000000..d96b390 --- /dev/null +++ b/openspec/specs/cutout-split-boundary-shaping/spec.md @@ -0,0 +1,73 @@ +# cutout-split-boundary-shaping Specification + +## Purpose +TBD - created by archiving change refine-cutout-split-boundaries. Update Purpose after archive. +## Requirements +### Requirement: Split boundary triangle cleanup + +Depth Cutout SHALL 在 Split 后、投影与边界平滑前,一次删除三个顶点均在当前边界上的三角面。 + +#### Scenario: Remove a boundary ear +- **WHEN** Split 输出包含三个边界顶点的三角面及包含内部顶点的相邻三角面 +- **THEN** 前者被删除,后者保留,清理不递归传播 + +### Requirement: Boundary smoothing after cleanup + +Depth Cutout SHALL 在三角面清理后的几何上沿用现有 Boundary Smooth,保留其参数语义。 + +#### Scenario: Smooth the cleaned boundary +- **WHEN** 全边界三角面清理完成且 Boundary Smooth 大于 0 +- **THEN** 现有平滑在保留几何上求值,已删除三角面不参与平滑 + +#### Scenario: Disable boundary smoothing +- **WHEN** Boundary Smooth 为 0 +- **THEN** 深度及网格平滑步骤保持各自输入值,其他切口形状处理继续按当前参数求值 + +### Requirement: Rounded split thickness profile + +Depth Cutout SHALL 使新切口的 Balloon 厚度轮廓在边界为零,在邻近区域连续恢复,并使 Edge Round 影响新切口的圆润过渡。 + +#### Scenario: Form a new meeting boundary +- **WHEN** Split 产生内部切口并执行 Balloon 厚度成形 +- **THEN** 新切口前后表面在零厚度边界交汇,影响带外保留原厚度轮廓 + +#### Scenario: Adjust edge rounding +- **WHEN** 用户增加 Edge Round +- **THEN** 新切口的过渡带随之加宽,最终几何呈现圆润过渡的变化,原 Alpha 轮廓继续使用既有规则 + +#### Scenario: No new boundary +- **WHEN** 当前 Split 结果没有新增边界 +- **THEN** 切口轮廓步骤保留原厚度字段 + +### Requirement: Thickness weighted normal smoothing + +Depth Cutout SHALL 始终按归一化厚度的 smoothstep 权重混合原法向与平滑法向,再归一化;保留 Normal Smooth 对平滑迭代数的控制。 + +#### Scenario: Thin thickness +- **WHEN** Balloon Thickness 趋于 0,或 Uniform Thickness 与 Reference Depth 的比例趋于 0 +- **THEN** 法向平滑引入的方向偏差连续趋于 0,结果保持有限 + +#### Scenario: Full smoothing strength +- **WHEN** 归一化厚度达到或超过 1 +- **THEN** 使用完整的既有法向平滑结果 + +#### Scenario: Normal smoothing disabled +- **WHEN** Normal Smooth 为 0 +- **THEN** 混合结果等于对应消费几何上的原法向归一化结果 + +### Requirement: Live cutout controls + +Depth Cutout SHALL 在每次参数变更后重新求值 Split、边界平滑和厚度成形,保留现有参数接口。 + +#### Scenario: Edit the loaded modifier +- **WHEN** 用户加载节点资产后连续修改 Split Threshold、Boundary Smooth 和 Edge Round +- **THEN** 几何按新参数实时更新,恢复相同参数可重现相同结果,不依赖冻结的中间网格 + +### Requirement: Loadable node asset + +本功能 SHALL 随正式节点资产交付,所有相关节点组及测试辅助图保持 Capture Attribute 为零,并保留用户属性。 + +#### Scenario: Independently load built asset +- **WHEN** 在独立 Blender 进程加载重建资产 +- **THEN** 接口、动态形状求值和必要属性均正确,输出不含实验诊断或已结束生命周期的内部临时属性 + diff --git a/openspec/specs/cutout-symmetry-seam/spec.md b/openspec/specs/cutout-symmetry-seam/spec.md new file mode 100644 index 0000000..3922b21 --- /dev/null +++ b/openspec/specs/cutout-symmetry-seam/spec.md @@ -0,0 +1,108 @@ +# cutout-symmetry-seam Specification + +## Purpose +TBD - created by archiving change refine-cutout-symmetry-seam. Update Purpose after archive. +## Requirements +### Requirement: 切口落在对称面上 + +对称节点 SHALL 让切口边缘落在对称面上,零厚度与非零厚度输入走同一条折叠路径:越过对称面的顶点 SHALL 拉回对称面,只删除因此整体落在对称面上的面,并删除折叠后仍邻接两个面的内部贴平边及其邻接面。保留几何中不得存在越过对称面的顶点,也不得留下整体落在对称面上的面或四面共边。 + +#### Scenario: 折叠过界部分 + +- **WHEN** 输入几何的一部分穿过对称面 +- **THEN** 跨界面的面保留,越界顶点对称轴坐标归零,界内顶点位置不变 +- **AND** 保留几何中不存在越过对称面的顶点 + +#### Scenario: 清理内部贴平边 + +- **WHEN** 折叠后出现落在对称面上、且仍邻接两个面的边 +- **THEN** 这条边及其邻接面被删除,切口重新成为边界边 +- **AND** 输出中不存在四面共边 + +#### Scenario: 不同厚度得到同一切边位置 + +- **WHEN** 同一 Cutout 分别以零厚度与非零厚度连接到对称节点 +- **THEN** 两种情况都折叠并把切口边缘归零,切边落在对称面上 +- **AND** 两侧顶点关于对称面对称,且都不越过对称面的几何尺度派生容差 + +#### Scenario: 裁切后不留平面面片 + +- **WHEN** 切口边缘归零使某个面完全落到对称面上,或输入本身存在这类面 +- **THEN** 该面不出现在输出中 + +#### Scenario: 几何全部位于界外 + +- **WHEN** 输入几何整体越过对称面 +- **THEN** 输出为空几何,节点不报错 + +### Requirement: 接缝由镜像合并自焊 + +对称节点 SHALL 在镜像之前完成切口归零,使两侧切口顶点重合,并 SHALL 在合并后形成共享边。开启 Fill Sides 时输出网格 SHALL 封闭,且边与顶点流形。 + +#### Scenario: 开启补面的封闭结果 + +- **WHEN** Fill Sides 开启且输入包含跨对称面的几何 +- **THEN** 输出的每条边恰好邻接两个面,顶点与边满足流形性,面绕序一致 +- **AND** 两侧顶点关于对称面对称 + +#### Scenario: 关闭补面 + +- **WHEN** Fill Sides 关闭 +- **THEN** 切口仍吸附到对称面并完成自焊 +- **AND** 开边只出现在没有侧壁的外轮廓处 + +### Requirement: 补面只覆盖不落在对称面上的边界边 + +Fill Sides 生成的侧壁 SHALL 只连接不落在对称面上的边界边,落在对称面上的切口环 SHALL 不生成侧壁几何。补面选择 SHALL 按顶点是否越过对称面容差判断。 + +#### Scenario: 切口处不生成侧壁 + +- **WHEN** Fill Sides 开启且切口环顶点归零后落在对称面上 +- **THEN** 输出中不存在顶点全部落在对称面上的补面面片 +- **AND** 切口环在合并后为共享边 + +#### Scenario: 外轮廓仍生成侧壁 + +- **WHEN** 输入外轮廓的边界顶点不落在对称面上 +- **THEN** 这些边界按现有直壁或平滑侧壁构成连接 +- **AND** Fill Sides 关闭时不生成侧壁 + +### Requirement: 切边平滑独立控制 + +`Seam Smooth` SHALL 控制切口环的面内平滑次数,`Fill Smooth` SHALL 控制 front 衔接带的面内平滑次数。两者都只移动面内分量并锁定对称轴坐标。侧壁细分 SHALL 始终存在,不以 `Fill Smooth > 0` 为开关。`Seam Smooth` 为 0 SHALL 保持原始切口,`Fill Smooth` 为 0 SHALL 保持原始轮廓;两个参数 SHALL 互不影响,且都与 Fill Sides 独立。 + +`Seam Smooth` 按切口环的两圈渐变衰减,切口环按 `pinned-geometry-smoothing` 的 `Pin Boundary` 与 `Pin Sharp` 处理。`Fill Smooth` 的锚点 SHALL 是 front 的完整边界带,即界外轮廓与对称面上的切缝,SHALL 按四圈渐变衰减并覆盖对称轴上的点;衔接处不是网格边界,因此 MUST NOT 套用 `Pin Boundary`,只保留 `Pin Sharp`。 + +#### Scenario: 切边平滑保持切口落在对称面 + +- **WHEN** Seam Smooth 大于 0 +- **THEN** 切口环顶点的对称轴坐标保持在容差内,镜像对称关系成立 +- **AND** 平滑位移从切口环起按两圈渐变衰减,两圈之外保留表面顶点位置不变 + +#### Scenario: 两个平滑参数互不影响 + +- **WHEN** 只改变 Fill Smooth 或只改变 Seam Smooth +- **THEN** 前者只改变衔接带及其侧壁,后者只改变切口环及其两圈渐变范围 +- **AND** Seam Smooth 为 0 时切口保持原始形态,Fill Smooth 为 0 时轮廓保持原始形态且侧壁仍保持细分 + +#### Scenario: 衔接带覆盖对称轴 + +- **WHEN** Fill Smooth 大于 0 +- **THEN** 界外轮廓与对称轴上的点都产生面内位移 +- **AND** 位移从完整边界带向内按四圈渐变衰减 + +#### Scenario: 关闭补面仍可平滑切边 + +- **WHEN** Fill Sides 关闭且 Seam Smooth 大于 0 +- **THEN** 切口环仍按该参数平滑,侧壁不生成,Fill Smooth 无可作用的轮廓 + +### Requirement: 节点不引入布尔与裁切 + +对称节点 SHALL 沿用现有节点集合完成切口,不新增布尔、裁切或细分节点。 + +#### Scenario: 检查构建结果 + +- **WHEN** 检查构建出的 `O Image Cutout Symmetry` 节点组 +- **THEN** 节点图中不存在布尔类节点 +- **AND** 接口在原有输入之外只新增 Seam Smooth + diff --git a/openspec/specs/depth-alpha-semantics/spec.md b/openspec/specs/depth-alpha-semantics/spec.md new file mode 100644 index 0000000..a702f60 --- /dev/null +++ b/openspec/specs/depth-alpha-semantics/spec.md @@ -0,0 +1,65 @@ +# depth-alpha-semantics Specification + +## Purpose +TBD - created by archiving change separate-depth-alpha-and-model-input. Update Purpose after archive. +## Requirements +### Requirement: Model input preserves source RGB + +系统 SHALL 使用输入图 RGB 准备 MoGe2 输入,缩放 SHALL 基于 RGB 执行,alpha MUST 保持为独立信号。 + +#### Scenario: Same RGB with different alpha +- **WHEN** 两张尺寸、RGB 相同的图仅 alpha 不同,并使用相同输入尺寸上限 +- **THEN** 它们生成的模型 RGB 输入相同,包括触发缩放的情况 + +#### Scenario: Transparent sky retains color context +- **WHEN** Remove BG 输出保留天空 RGB 并将其 alpha 设为零 +- **THEN** MoGe2 输入保留天空 RGB,输出深度产物对应像素的 Alpha 为零 + +### Requirement: Alpha silhouette determines depth extension + +系统 SHALL 根据原图 alpha 确定可见分量和轮廓修补范围,使用同一分量的可靠深度延伸轮廓。模型有效性 MUST 用于排除不可靠来源,修补范围 SHALL 独立于模型 mask 孔洞。延伸 SHALL 保持原始 alpha、模型有效性与未修补区域的深度。 + +#### Scenario: Repair the alpha edge +- **WHEN** alpha 轮廓内侧保护带及轮廓外采样带存在不稳定深度,且分量内部存在可靠来源 +- **THEN** 系统使用该分量可靠深度修补边界,并保持目标像素的相机投影射线 + +#### Scenario: Internal mask hole +- **WHEN** 一个低 mask 区域完全位于 alpha 轮廓保护带以外的内部 +- **THEN** 该区域不会仅因 mask 偏低而成为延伸目标,其模型有效性保持原值 + +#### Scenario: Fully opaque input +- **WHEN** 输入 alpha 全为 1 +- **THEN** 系统保留原始深度场,包括低 mask 区域 + +#### Scenario: Component lacks reliable depth +- **WHEN** 某个 alpha 分量没有可靠深度来源 +- **THEN** 该分量保留原始深度,不从其他分量借用深度 + +### Requirement: Depth artifact alpha combines coverage and model validity + +`depth.exr` SHALL 在 RGB 保存相机空间 XYZ,在 Alpha 保存对齐预测尺寸的原图 alpha 与原始模型有效性的逐像素乘积。两个信号 MUST 保留连续值,补边 MUST 保持乘积所用的原始信号。 + +#### Scenario: Continuous values +- **WHEN** 某像素原图 alpha 为 0.8,模型有效性为 0.75 +- **THEN** 导出的 Alpha 为 0.6,XYZ 保留该像素的深度处理结果 + +#### Scenario: Invisible or model-invalid pixel +- **WHEN** 原图 alpha 或模型有效性任一为零 +- **THEN** EXR Alpha 为零,XYZ 仍保存可供采样的数值场 + +### Requirement: Geometry consumers use depth channels by purpose + +Depth Plane SHALL 通过现有 Valid Only 和 Validity Threshold 按 EXR Alpha 剔除。Cutout SHALL 根据自身轮廓生成曲面并读取 XYZ,EXR Alpha 不得引入额外曲面剔除。 + +#### Scenario: Depth plane culling +- **WHEN** Valid Only 开启且原图 alpha 或模型有效性使乘积低于当前阈值 +- **THEN** 该采样位置按现有节点的面域规则参与剔除 + +#### Scenario: Culling disabled +- **WHEN** 用户关闭 Valid Only +- **THEN** 完整细分网格继续进入深度投影与厚度处理 + +#### Scenario: Cutout keeps its contour +- **WHEN** Cutout 轮廓内含低 EXR Alpha 区域 +- **THEN** 曲面拓扑保持由 Cutout 轮廓决定,深度采样使用导出的 XYZ + diff --git a/openspec/specs/depth-artifact-contract/spec.md b/openspec/specs/depth-artifact-contract/spec.md new file mode 100644 index 0000000..5df36df --- /dev/null +++ b/openspec/specs/depth-artifact-contract/spec.md @@ -0,0 +1,42 @@ +# depth-artifact-contract Specification + +## Purpose +TBD - created by archiving change unify-depth-plane-and-artifact. Update Purpose after archive. +## Requirements +### Requirement: Depth has one file contract + +系统 SHALL 将所有深度图片写为 `depth.exr`,通过结果键 `depth` 返回。文件 MUST 使用 RGBA float32,RGB 为相机 XYZ,Alpha 为有效性,并沿用相机坐标、像素方向和非有限值处理约定。内部标量深度和 `depth.json` SHALL 保留。 + +#### Scenario: Generate depth artifacts +- **WHEN** 整图转换、Depth Solid 或 Depth Balloon 请求深度 +- **THEN** 结果包含 `depth` 与 `depth_metadata`,对应 `depth.exr` 和 `depth.json` +- **AND** 不生成 `z-depth.exr`、`vector-depth.exr` 或旧结果键 + +#### Scenario: Preserve independent channels +- **WHEN** 输出合成数据中 X、Y、Z 不同的深度图 +- **THEN** 解码后的 RGB 分别保持 XYZ 数值和有效性,不将 Z 复制到 RGB + +### Requirement: Depth generation is a boolean request + +共享产物生成 SHALL 使用布尔 `generate_depth` 决定是否生成深度,法线请求保持独立;深度消费者 SHALL 请求保留 XYZ 的单次推理,并沿用现有边缘深度修正条件。 + +#### Scenario: Generate depth and normals together +- **WHEN** 同时请求深度和法线 +- **THEN** 只运行一次预测,生成统一深度、元数据和请求的法线产物 + +#### Scenario: Generate normals alone +- **WHEN** generate_depth 为 false 且请求法线 +- **THEN** 生成法线,不写出深度图片或深度元数据 + +### Requirement: All consumers use the unified channels + +整图与 Cutout 结果加载器 SHALL 从 `depth` 加载深度并沿用 Pack、数据色彩空间及尺度标定。Camera 与 Depth Cutout SHALL 读取 XYZ;Relief、Depth Balloon 和公共标量深度读取函数 SHALL 读取 B/Z。 + +#### Scenario: Scalar depth is distinct from X +- **WHEN** X 与 Z 明显不同的纹理用于 Relief 或 Depth Balloon +- **THEN** 几何位移由 Z 决定,效果与使用相同标量 Z 的现有结果一致 + +#### Scenario: Projected geometry keeps camera coordinates +- **WHEN** 统一深度纹理用于 Camera 或 Depth Cutout +- **THEN** 投影位置、有效性和尺度与更名前的 XYZ 输入一致 + diff --git a/openspec/specs/depth-cutout-volume/spec.md b/openspec/specs/depth-cutout-volume/spec.md new file mode 100644 index 0000000..be1822a --- /dev/null +++ b/openspec/specs/depth-cutout-volume/spec.md @@ -0,0 +1,49 @@ +# depth-cutout-volume Specification + +## Purpose +TBD - created by archiving change reorganize-cutout-shapes. Update Purpose after archive. +## Requirements +### Requirement: 平滑背面保留空间位置 +O Image Depth Cutout 的 Balloon 模式 SHALL 以投影正面减 o_balloon 估计中心位置,固定轮廓与切口并平滑内部中心场,再用该中心场与 Balloon 轮廓构造背面。关闭边缘与整体平滑时,正面 MUST 保持原深度表面位置。 + +#### Scenario: 带噪声的已知体积 +- **WHEN** 输入正面由线性变化的中心深度、Balloon 轮廓和内部高频噪声构成,Thickness 为 1,额外几何平滑关闭 +- **THEN** 背面接近中心深度减 Balloon 轮廓,背面噪声 RMS 小于正面噪声的 1%,正面顶点位置误差小于 0.00001 个局部单位 + +#### Scenario: 前后距离保留 +- **WHEN** 输入有宽厚主体和处于不同深度的细长结构 +- **THEN** 背面形成按轮廓变化的厚度,各部分保留局部前后位置,不统一回到同一个参考平面 + +### Requirement: 两种厚度方式保留深度表面 +深度 Cutout SHALL 在 Balloon 模式使用平滑背面,在 Uniform 模式保留原 Depth Surface 的等厚行为;两种方式的零厚度 SHALL 输出单层深度表面。 + +#### Scenario: 零厚度 +- **WHEN** 两种厚度方式分别设为零 +- **THEN** 输出相同投影与 Split 结果的单层深度表面,不生成重叠背面或零面积侧壁 + +#### Scenario: 等厚方式 +- **WHEN** 选择 Uniform 并设为正厚度 +- **THEN** 正背面保持原 Depth Surface 的沿内向轴等厚关系 + +### Requirement: Split 使用投影后的深度落差 +Split SHALL 以原始相邻面采样距离差乘 Depth Scale 与变形前跨度比较,Cleanup MUST 位于 Split 前;Depth Scale SHALL 决定切边的可见落差,因此也决定切边是否存在。 + +#### Scenario: Depth Scale 缩放切边 +- **WHEN** 固定输入和 Split,将 Depth Scale 从 0 增大到 1 +- **THEN** 零厚度输出在 Depth Scale 为 0 时没有切边,切边随 Depth Scale 增大出现,深度位移随之变化 + +#### Scenario: 保留分离产生的小片 +- **WHEN** 原平面岛面积通过 Cleanup,但 Split 后的部分小片低于该面积阈值 +- **THEN** 这些分离小片仍被保留并参与后续厚度构造 + +### Requirement: 厚度网格闭合且轮廓无固定厚边 +正厚度网格 SHALL 在原轮廓合并正背面,并独立封闭 Split 切口;SHALL 保留原型中的深凹比例厚度下限、边缘平滑和整体 Smooth,且不提供 Rim Thickness。 + +#### Scenario: 五张实图验收 +- **WHEN** 使用噬菌体、蜘蛛、幼虫、心脏、棕榈树缓存输入,分别求值无平滑、边缘与整体平滑、平滑加 Split +- **THEN** 15 个正厚度组合均为有限坐标、无非流形边、无法线连接错误和零面积面,输出保留 UVMap 与 o_balloon 并移除专用暂存属性 + +#### Scenario: 边缘与整体控制 +- **WHEN** 调整 Edge Smooth、Smooth 和 Smooth Iterations +- **THEN** 相应几何平滑生效,Smooth Iterations 为零时整体平滑不改变位置,原轮廓不产生固定厚度的边框 + diff --git a/openspec/specs/depth-node-options/spec.md b/openspec/specs/depth-node-options/spec.md new file mode 100644 index 0000000..32d928e --- /dev/null +++ b/openspec/specs/depth-node-options/spec.md @@ -0,0 +1,51 @@ +# depth-node-options Specification + +## Purpose +TBD - created by archiving change simplify-depth-group-options. Update Purpose after archive. +## Requirements +### Requirement: 深度节点组不公开 Smooth Weight + +`O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama` SHALL 不在公开接口中提供 `Smooth Weight`,边界平滑 MUST 按完整影响场混合顶点位置。 + +#### Scenario: 检查节点组接口 + +- **WHEN** 读取三个节点组的输入接口 +- **THEN** 接口不存在 `Smooth Weight`,且 `Boundary Smooth` 仍控制边界平滑迭代次数 + +#### Scenario: 默认边界平滑结果 + +- **WHEN** 用户保持默认参数并提高 `Boundary Smooth` +- **THEN** 边界带内的顶点按影响场强度移动到平滑位置,影响带外的顶点保持不变 + +### Requirement: 共享平滑构建只保留影响场形态 + +`expand_smooth()` SHALL 只接受迭代次数输入、影响场与几何,并 MUST 在迭代次数为零时原样返回输入几何。 + +#### Scenario: 迭代次数为零 + +- **WHEN** `Boundary Smooth` 为 0 +- **THEN** 输出几何与输入几何完全一致 + +### Requirement: Options 中的 Reference Depth 位于最后 + +包含 `Reference Depth` 的节点组 SHALL 在 Options 面板最后一项提供该输入,其余 Options 控制项 MUST 保持各自职责与默认值。 + +#### Scenario: 检查 Depth Plane 与 Depth Cutout 排列 + +- **WHEN** 读取 `O Image Depth Plane` 与 `O Image Depth Cutout` 的 Options 项顺序 +- **THEN** 最后一项为 `Reference Depth`,之前的项依次为各自的艺术调整参数 + +#### Scenario: 检查 Relief Plane 排列 + +- **WHEN** 读取 `O Image Relief Plane` 的 Options 项顺序 +- **THEN** 项顺序为 `Mask Threshold`、`Reference Depth` + +### Requirement: Reference Depth 保持既有几何含义 + +移动面板位置 SHALL 不改变 `Reference Depth` 的类型、子类型、默认值、范围与几何运算方式。 + +#### Scenario: 校验输入属性 + +- **WHEN** 读取 `Reference Depth` 输入 +- **THEN** 该输入使用 `DISTANCE` 子类型,并继续作为采样深度的运算基准 + diff --git a/openspec/specs/depth-plane-alpha-culling/spec.md b/openspec/specs/depth-plane-alpha-culling/spec.md new file mode 100644 index 0000000..3475218 --- /dev/null +++ b/openspec/specs/depth-plane-alpha-culling/spec.md @@ -0,0 +1,40 @@ +# depth-plane-alpha-culling Specification + +## Purpose +TBD - created by archiving change split-depth-and-relief-planes. Update Purpose after archive. +## Requirements +### Requirement: Optional alpha culling defaults to enabled +Depth Plane 修改器 SHALL 提供默认开启的 Valid Only 开关,按 depth.exr 有效性 Alpha 剔除几何。该处理 MUST 在基础网格细分之后、深度投影和厚度之前执行。 + +#### Scenario: Default conversion +- **WHEN** 用户转换包含无效深度区域的图片为 Depth Plane +- **THEN** Valid Only 默认开启,深度有效性 Alpha 参与基础网格的几何剔除 + +#### Scenario: Disable alpha culling +- **WHEN** 用户关闭 Valid Only +- **THEN** 完整细分网格进入相机投影与厚度流程 + +#### Scenario: Fully valid depth +- **WHEN** depth.exr 有效性 Alpha 全部为 1 +- **THEN** Alpha 剔除保留完整基础网格 + +### Requirement: Culling remains responsive to subdivision +系统 SHALL 在节点内重新求值 Alpha 剔除,允许用户继续通过 Subdivide 调节网格。剔除后的几何 MUST 保留正确 UV,并进入现有相机投影和曲面厚度计算。 + +#### Scenario: Change subdivision +- **WHEN** 用户修改 Subdivide +- **THEN** 节点重新生成网格并依据该网格重新检测 Alpha,无需重新运行 AI + +#### Scenario: Apply thickness after culling +- **WHEN** 用户对已剔除的曲面设置正 Thickness +- **THEN** 厚度从保留的曲面生成,无效区域已删除的基础面不会重新出现 + +### Requirement: Permissive culling follows the reference graph +Alpha 剔除 MUST 使用用户参考图中的宽松判定连接,且只公开 Valid Only 开关,网格精度使用已有 Subdivide。 + +深度图片 SHALL 使用 Linear / Extend 采样;Separate Color 的 Alpha SHALL 接 Float / Less Than(B = 1),结果通过 Boolean / Point 的 Evaluate on Domain 后接 Delete Geometry(Face、All)。一个面仅在所有顶点都满足 Alpha < 1 时删除;至少一个顶点 Alpha 为 1 的边界面保留。 + +#### Scenario: Verify the reference rule +- **WHEN** 实现 Alpha 判定 +- **THEN** 节点连接、运算、默认值和域设置与参考图一致,有效与无效区域交界网格的保留结果符合该图规则 + diff --git a/openspec/specs/depth-scaled-tangent-normals/spec.md b/openspec/specs/depth-scaled-tangent-normals/spec.md new file mode 100644 index 0000000..d23b553 --- /dev/null +++ b/openspec/specs/depth-scaled-tangent-normals/spec.md @@ -0,0 +1,31 @@ +# depth-scaled-tangent-normals Specification + +## Purpose +TBD - created by archiving change scale-tangent-normals-with-depth. Update Purpose after archive. +## Requirements +### Requirement: Normal strength follows geometry depth scale +When a Geometry Nodes group both exposes `Depth Scale` and is rendered with a normal map, it SHALL store `1 - Depth Scale` as the `o_normal_reduction` float attribute on its final geometry. `O Image Layer` SHALL calculate Tangent and Object Normal Map Strength as its `Normal Scale` input multiplied by `1 - o_normal_reduction`. + +#### Scenario: Reduced relief depth reduces Tangent normal strength +- **WHEN** an O Image Relief Plane uses a Tangent normal map and `Depth Scale` is set to 0.5 +- **THEN** the Tangent Normal Map Strength is one half of the value produced at `Depth Scale` 1 with the same `Normal Scale` input + +#### Scenario: Zero relief depth disables Tangent normal detail +- **WHEN** an O Image Relief Plane uses a Tangent normal map and `Depth Scale` is set to 0 +- **THEN** the Tangent Normal Map Strength is zero + +#### Scenario: Depth extrapolation is preserved +- **WHEN** an eligible Tangent-normal geometry group uses `Depth Scale` greater than 1 +- **THEN** `o_normal_reduction` is negative by the corresponding amount and the Tangent Normal Map Strength retains the extrapolated value without clamping + +### Requirement: Reduction attribute retains its material fallback +`O Image Layer` SHALL use a factor of 1 for its Tangent Normal Map Strength when `o_normal_reduction` is absent. The existing `o_normal_reduction` material contract SHALL remain unchanged. + +#### Scenario: Ordinary image material has no normal-scale attribute +- **WHEN** an object using O Image Layer has no `o_normal_reduction` attribute +- **THEN** its Tangent Normal Map Strength equals the `Normal Scale` input + +#### Scenario: Object Space normal strength follows the reduction +- **WHEN** O Image Layer is configured for an Object Space normal map and its geometry has any `o_normal_reduction` value +- **THEN** the Object Space Normal Map Strength equals the `Normal Scale` input multiplied by `1 - o_normal_reduction` + diff --git a/openspec/specs/depth-split-selection/spec.md b/openspec/specs/depth-split-selection/spec.md new file mode 100644 index 0000000..db7d864 --- /dev/null +++ b/openspec/specs/depth-split-selection/spec.md @@ -0,0 +1,27 @@ +# depth-split-selection Specification + +## Purpose +TBD - created by archiving change scale-depth-split-by-depth-scale. Update Purpose after archive. +## Requirements +### Requirement: Depth Split 使用投影后的几何落差 +Depth Split 的判定 SHALL 以原始相邻面采样距离差乘 Depth Scale 作为落差量,与变形前的采样跨度比较;平均射线距离 SHALL 继续作为把绝对差归一化为相对跳变的因子,Depth Scale MUST NOT 参与该归一化。参数说明 MUST 说明切边随 Depth Scale 缩放。 + +#### Scenario: Depth Scale 为零 +- **WHEN** 固定深度图与 Depth Split,把 Depth Scale 设为 0 +- **THEN** 输出拓扑与 Split 为 0 时相同,没有切边,也没有条带面被删除 + +#### Scenario: Depth Scale 放大落差 +- **WHEN** 同一输入在 Depth Scale 从 0 增大到 1 之间求值 +- **THEN** 选中的切边只增不减,Depth Scale 为 1 时与当前判据结果一致 + +#### Scenario: 深度数据整体缩放 +- **WHEN** 同一深度图的采样数值整体乘以任意正数 +- **THEN** 选中切边完全相同 + +### Requirement: 三个深度资产共用同一判据 +`O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama` SHALL 使用同一个 Depth Split 判据并各自传入自己的 Depth Scale。 + +#### Scenario: 资产一致性 +- **WHEN** 三个资产分别求值,Depth Scale 设为零或设为 1 +- **THEN** 三者都表现为零不切开、1 与改动前一致 + diff --git a/openspec/specs/depth-surface-face-separation/spec.md b/openspec/specs/depth-surface-face-separation/spec.md new file mode 100644 index 0000000..adbb0e1 --- /dev/null +++ b/openspec/specs/depth-surface-face-separation/spec.md @@ -0,0 +1,131 @@ +# depth-surface-face-separation Specification + +## Purpose +TBD - created by archiving change replace-depth-surface-stretch-with-split. Update Purpose after archive. +## Requirements +### Requirement: 分离控制替换拉伸剔除 + +系统 SHALL 在 Depth Surface 及外层 Cutout 的 Options 中以 `Split` 替代 `Stretch Limit`。Split SHALL 为范围 `0–1` 的比例输入,默认 `0`,增大时不得降低同一输入几何的切边选择强度。 + +#### Scenario: 默认关闭 +- **WHEN** 用户使用默认 Split `0` +- **THEN** 系统不增加切边,并在 Cleanup 保留的区域上使用原 Depth Surface 采样与投影行为 + +#### Scenario: 中点与最大强度 +- **WHEN** Split 分别设为 `0.5` 和 `1` +- **THEN** 内部深度坡度阈值分别为 `5` 和 `2.5`,且控制值不能超过 `1`;正值对应阈值为 `2.5 / Split` + +#### Scenario: 接口替换 +- **WHEN** 用户检查新 Cutout 的 Options +- **THEN** 可见 Split,且不存在 Stretch Limit 控制及其拉伸删几何行为 + +### Requirement: 使用相邻面深度与实际跨度选择共享边 + +系统 SHALL 以场景尺度的相邻面中心 Z 差除以变形前两面中心的实际距离作为坡度得分;场景深度 SHALL 包含图片标定的 Uniform Scale 和当前 Depth Scale 的幅度。系统 SHALL 只对恰有两个邻面的共享边执行此判断,并安全处理零跨度输入。 + +#### Scenario: 同一阈值两侧 +- **WHEN** Split 为 `0.5`,两个有效邻接的坡度得分分别为 `4` 和 `6` +- **THEN** 得分 `4` 的边不被选中,得分 `6` 的边被选中进行分离 + +#### Scenario: 等比尺度变化 +- **WHEN** 图片几何和经标定的深度同时等比缩放且 Split 不变 +- **THEN** 相应共享边的坡度得分和选择结果保持一致 + +#### Scenario: 深度归零与退化邻接 +- **WHEN** Depth Scale 为 `0` 或邻面中心距离为零 +- **THEN** 该条件不产生新增切边,且求值不产生非有限坐标 + +### Requirement: 分离保留表面及两侧独立位置 + +系统 SHALL 通过拓扑分离保留原始表面的覆盖,使用各自所属侧的数据确定拆后位置。仅设置选边而继续使用共享 UV 的同一个深度值 SHALL NOT 被视为实现两侧分离。 + +#### Scenario: 零厚度深度阶跃 +- **WHEN** Cleanup 为零,规则网格跨越深度阶跃且 Split 选中贯通边界 +- **THEN** 原始面全部保留,两侧具有独立顶点与不同场景深度,且没有拉伸剔除造成的缺面 + +#### Scenario: 开启控制但没有切边 +- **WHEN** Split 大于零且所有共享边均未达到分离阈值 +- **THEN** 顶点投影与 Split 为零时完全一致,轮廓不因面中心平均而收缩 + +#### Scenario: 切口保持原相机射线 +- **WHEN** 某些共享边被分离 +- **THEN** 仅关联顶点使用所属侧的深度,并保持原 XYZ 的相机射线方向;其他顶点保留原采样位置 + +### Requirement: Cleanup 在拆边后生成厚度前执行 + +Depth Surface SHALL 在 Split Edges 后、投影及厚度生成前执行一次 Cleanup,按拆分后平面区域的面积使用既有清理阈值。其他 Shape SHALL 保持原 Cleanup 顺序。 + +#### Scenario: 清理拆出的碎片 +- **WHEN** 拆边生成低于 Cleanup 面积阈值的独立区域 +- **THEN** 该区域在生成厚度前被清理,其余区域随后投影并加厚 + +#### Scenario: 厚度不改变清理结果 +- **WHEN** Split、基础网格和 Cleanup 保持不变,仅调整 Thickness +- **THEN** 被 Cleanup 保留的平面区域不变 + +#### Scenario: 关闭分离仍可清理 +- **WHEN** Split 为零且 Cleanup 为正 +- **THEN** Cleanup 对原平面区域执行一次,不因分离关闭而失效 + +### Requirement: 厚度侧壁与正背面完整连接 + +系统 SHALL 在加厚时使每个分离区域的侧壁与正面、背面及相邻厚度段共享连续边界。对定向正确的流形片状输入,各加厚区域 SHALL 形成闭合表面,不得跨区域重新焊接切口。 + +#### Scenario: 阶跃区域加厚 +- **WHEN** 对已拆开的深度阶跃区域设置正厚度 +- **THEN** 每个区域的侧壁准确连接其正背面,边界无开口、重叠但未连接的接缝或错位边界 + +#### Scenario: 分段厚度 +- **WHEN** 厚度足以生成多个侧壁段 +- **THEN** 各段相接且总厚度符合输入值,侧壁端点与对应表面边界一致 + +#### Scenario: 近距离独立区域 +- **WHEN** 两个独立分离区域的间隔小于既有几何焊接容差 +- **THEN** 厚度连接仍保持区域独立,不因全局合并恢复原连接 + +### Requirement: 正式 Cutout 流程共享分离能力 + +系统 SHALL 让多个物体共享同一套节点资产,并通过各自修改器传入数据。Split SHALL 仅影响 Depth Surface 分支;外层 Cleanup、Smooth 及其他三种 Shape SHALL 保持各自职责。 + +#### Scenario: 六物体共享节点 +- **WHEN** 三张图片分别创建 High 和 Low 的 Depth Surface +- **THEN** 六个物体引用共享节点组,同一图片的标定值一致,各物体控制值可以独立调整 + +#### Scenario: 完整修改器链 +- **WHEN** Depth Surface 同时启用分离、正厚度及外层 Smooth +- **THEN** 同一区域的正背面和侧壁保持连接,分离区域之间保持独立 + +#### Scenario: 切换其他 Shape +- **WHEN** 用户改变 Split 后选择其他三种基础 Shape +- **THEN** Split 不改变这些 Shape 的基础拓扑与形状结果 + +### Requirement: 节点布局清晰可读 + +资产中全部七个几何节点组 SHALL 遵循 AGENTS.md 的节点组规范:主链从左到右连续排列、辅助分支就近汇入、节点无重叠、保留默认节点标题并隐藏未使用输出。必要的向左引用 SHALL 直接连接,不使用 Reroute 绕行,允许回接连线交叉。 + +跨区域暂存值 SHALL 使用 `o_depth_*` 命名属性并就近读取,在输出前通过一个 Wildcard 模式的 Remove Named Attribute 统一清理。大步骤之间 SHALL 留白,不放置 String 注解。同一目的的计算 SHALL 聚在主轴同一侧,支线内部间距 SHALL 明显小于与其他部分的距离。独立用途可在两侧错落展开,长算式可超出所服务主链步骤的范围;必要的几何引用可回接。 + +#### Scenario: 暂存属性生命周期 +- **WHEN** Split 和 Thickness 分别开启或关闭 +- **THEN** 面、面角和顶点暂存值在正确域传递,输出不残留暂存属性,UVMap 与用户属性保持可用,关闭分支不出现移除缺失属性的警告 + +#### Scenario: 查看所有几何资产 +- **WHEN** 用户打开任一几何节点组 +- **THEN** 计算分支按用途聚拢并利用附近空位,长算式横排,局部输入靠近消费节点,布局调整保持原计算结果 + +#### Scenario: 查看同级分支汇合 +- **WHEN** 多个独立同级分支输入 Join Geometry、Menu Switch 或 Combine XYZ 等汇合节点 +- **THEN** 分支末端排成一列并对齐输出端,XYZ 三轴按同级输入组织,菜单分支按输入顺序上下排列,共享上游留在公共位置 + +#### Scenario: 查看同一用途的计算 +- **WHEN** 用户查看 Depth Plane 的置换计算或 Depth Surface 的 Split 选边判别 +- **THEN** 同一用途的计算聚在主轴同一侧,内部节点间距紧凑,与其他部分之间保留更大的空白,主轴可适当宽松 + +#### Scenario: 查看分离与厚度流程 +- **WHEN** 用户在节点编辑器查看新 Depth Surface +- **THEN** 能沿主链依次辨认采样判别、拆边、投影、厚度和输出,辅助连线不横跨无关区域,节点文字不被遮挡 + +#### Scenario: 查看成对区域 +- **WHEN** 用户检查厚度 Repeat 区域 +- **THEN** 成对节点位置正确,循环内部节点清晰排列,区域不遮挡其他节点,Frame 仅用于确有包裹语义的部分 + diff --git a/openspec/specs/depth-surface-stretch-culling/spec.md b/openspec/specs/depth-surface-stretch-culling/spec.md new file mode 100644 index 0000000..dd341e6 --- /dev/null +++ b/openspec/specs/depth-surface-stretch-culling/spec.md @@ -0,0 +1,57 @@ +# depth-surface-stretch-culling Specification + +## Purpose +TBD - created by archiving change restore-depth-surface-stretch-culling. Update Purpose after archive. +## Requirements +### Requirement: Depth Surface exposes a stretch multiplier limit +系统 SHALL 仅为 Depth Surface 提供 **Stretch Limit** Factor 输入,范围为 `0–1`、默认值为 `0`,并将它放入 Options Panel。系统 SHALL 以原始边长除以投影后边长得到剩余比例;`0` SHALL 关闭拉伸剔面,正值越大 SHALL 越严格地清理拉伸面。 + +#### Scenario: Default value preserves the complete surface +- **WHEN** 用户创建 Depth Surface 且保持 Stretch Limit 为 `0` +- **THEN** 系统 SHALL 不因边长比例删除任何 Edge 或 Face + +#### Scenario: Other Cutout shapes do not consume the limit +- **WHEN** 当前 Shape 为 Surface、Balloon 或 Depth Balloon +- **THEN** Stretch Limit SHALL 不显示为该 Shape 的有效控制,也 SHALL 不改变其几何结果 + +### Requirement: Stretch is measured on matching edges around depth projection +系统 SHALL 在 Depth Surface 的 `Set Position` 之前以 Point Domain 捕获 Edge Vertices Distance Field 的原始值,并将同一个未捕获 Field 直接用于 Set Position 后的当前距离。系统 SHALL 在 `原始距离 / 当前距离 < Stretch Limit` 时以 Point Domain 删除对应位置及其相邻 Edge 和 Face。 + +#### Scenario: Point remains within the threshold +- **WHEN** 一个 Point 上适配后的边长比例不低于 Stretch Limit +- **THEN** 系统 SHALL 保留该 Point 及其相邻拓扑 + +#### Scenario: Point exceeds the threshold +- **WHEN** 一个 Point 上适配后的边长比例低于 Stretch Limit +- **THEN** 系统 SHALL 删除该 Point 及相邻 Edge 和 Face + +#### Scenario: Depth Scale changes the evaluated stretch +- **WHEN** 用户调整 Depth Scale,使同一条 Edge 的投影后长度跨过当前 Stretch Limit +- **THEN** Geometry Nodes SHALL 实时更新该 Edge 及关联 Face 的保留状态 + +#### Scenario: Lower threshold permits more stretch +- **WHEN** 同一个 Point 的剩余比例不低于 Stretch Limit +- **THEN** 系统 SHALL 保留该 Point 及其相邻拓扑 + +### Requirement: Stretch culling removes loose topology +系统 SHALL 在删除拉伸 Point 后删除 `Edge Neighbors.Face Count == 0` 的 Edge,并随后删除 `Vertex Neighbors.Face Count == 0` 的 Point。该清理 SHALL 位于 Depth Surface Thickness 与 Smooth 之前,并 SHALL 保留仍属于至少一个 Face 的开放边界。 + +#### Scenario: Face deletion leaves a single line +- **WHEN** 拉伸剔面后有 Edge 不再属于任何 Face +- **THEN** 系统 SHALL 删除该 Edge,最终 Depth Surface 不包含纯线拓扑 + +#### Scenario: Edge deletion leaves isolated points +- **WHEN** 松散 Edge 清理后有 Point 不再属于任何 Face +- **THEN** 系统 SHALL 删除该 Point,最终 Depth Surface 不包含孤立点 + +#### Scenario: Culling creates an open surface boundary +- **WHEN** 一条 Edge 在拉伸剔面后仍属于一个保留的 Face +- **THEN** 系统 SHALL 保留该 Edge,使后续 Thickness 能围绕最终开放边界构建侧壁 + +### Requirement: Stretch culling preserves Depth Artifact semantics +系统 SHALL 只使用 Geometry Nodes 中投影前后的几何边长完成拉伸判断,并 SHALL 保持 Vector Depth EXR 的 RGB Camera XYZ、Validity Alpha、Depth Metadata 和 Server Job 协议不变。 + +#### Scenario: Depth Surface evaluates stretch data +- **WHEN** Depth Surface 使用生成的 Vector Depth EXR 求值 +- **THEN** 系统 SHALL 从 RGB Camera XYZ 形成投影并比较几何边长,且 SHALL NOT 要求额外 Depth Continuity 通道或贴图 + diff --git a/openspec/specs/depth-surface-strip-cleanup/spec.md b/openspec/specs/depth-surface-strip-cleanup/spec.md new file mode 100644 index 0000000..03d29e7 --- /dev/null +++ b/openspec/specs/depth-surface-strip-cleanup/spec.md @@ -0,0 +1,73 @@ +# depth-surface-strip-cleanup Specification + +## Purpose +TBD - created by archiving change clean-depth-surface-strip-faces. Update Purpose after archive. +## Requirements +### Requirement: 拆边后默认执行单次条带清理 + +`O Image Depth Plane` 与 `O Image Depth Cutout` SHALL 在 Split 正值分支内、Split Edges 之后且投影和厚度生成之前,通过几何节点执行一次 Face / All 删除。两个节点组 SHALL 共用该逻辑,正式接口 SHALL NOT 暴露 `Remove Strip Faces` 开关。 + +#### Scenario: 开启 Split +- **WHEN** Split Threshold 大于 0 +- **THEN** 系统先按当前规则拆边,再根据拆后拓扑执行一次条带删面 + +#### Scenario: 关闭 Split +- **WHEN** Split Threshold 为 0 +- **THEN** 系统旁路拆边及新增条带删面,保持当前零值分支的几何与投影行为 + +#### Scenario: 单次删除 +- **WHEN** 首次删除使另一张面新出现一组边界对边 +- **THEN** 该面不会因新边界在本次求值中被继续删除 + +### Requirement: 四边面边界对边判定 + +系统 SHALL 仅选择四边面,并按面角顺序取得四条边。若至少一组对边的 Face Count 均等于 1,该面 SHALL 被选中;其余面 SHALL 被保留。 + +#### Scenario: 条带及孤立四边面 +- **WHEN** 四边面具有两条相对的边界边,或具有三条、四条边界边 +- **THEN** 该面被删除 + +#### Scenario: 普通边缘及角落 +- **WHEN** 四边面没有边界边、只有一条边界边,或仅有两条相邻边界边 +- **THEN** 该面保留 + +#### Scenario: 面角起点与绕序 +- **WHEN** 同一四边面的面角起点改变或绕序反转 +- **THEN** 选择结果不变 + +#### Scenario: 非四边面 +- **WHEN** 输入面为三角面或其他非四边面 +- **THEN** 新增清理规则不删除该面 + +#### Scenario: 非流形边 +- **WHEN** 一条边的 Face Count 不等于 1 +- **THEN** 该边不能作为匹配条件中的边界边 + +### Requirement: 清理结果接入原投影和厚度流程 + +系统 SHALL 在剩余几何上固定相机坐标并执行现有投影及加厚,保留 UV 和用户属性,保持切口区域独立。清理选择 SHALL 只依据拆后拓扑,不依赖 Thickness 或后续 Smooth。 + +#### Scenario: 剩余区域加厚 +- **WHEN** 清理后的定向正确的流形片状区域设置正厚度 +- **THEN** 每个保留区域的正背面与侧壁保持完整连接,区域之间保持独立 + +#### Scenario: 改变显示形状参数 +- **WHEN** 基础几何、深度输入与 Split Threshold 不变,仅修改 Thickness 或 Smooth +- **THEN** 被条带清理选中的基础面集合不变 + +#### Scenario: 属性与空结果 +- **WHEN** 清理保留部分面或删除全部面 +- **THEN** 求值正常完成且坐标有限;有剩余几何时 UV 与用户属性可用,临时属性依当前流程清理 + +### Requirement: Split Threshold 位于 Smooth 前 + +两个节点组 SHALL 将唯一的 `Split Threshold` 输入放在主输入区,紧邻并位于 `Smooth` 之前。该输入 SHALL 保持原名称、FACTOR 类型、0–1 范围、默认 0 和当前阈值映射。 + +#### Scenario: 检查接口 +- **WHEN** 检查任一节点组或其修改器输入 +- **THEN** Split Threshold 位于 Smooth 前且不属于 Options,Options 内没有重复输入,接口没有 Remove Strip Faces + +#### Scenario: 参数读取 +- **WHEN** Operator 为修改器赋值或用户调整 Split Threshold +- **THEN** 新位置的输入正常控制原拆边强度及默认条带清理 + diff --git a/openspec/specs/depth-symmetry-orientation/spec.md b/openspec/specs/depth-symmetry-orientation/spec.md new file mode 100644 index 0000000..2eef3ac --- /dev/null +++ b/openspec/specs/depth-symmetry-orientation/spec.md @@ -0,0 +1,47 @@ +# depth-symmetry-orientation Specification + +## Purpose +TBD - created by archiving change standardize-blender-object-coordinates. Update Purpose after archive. +## Requirements +### Requirement: Depth Symmetry mirrors across the canonical Y axis + +`O Image Cutout Symmetry` SHALL 在 canonical Blender 对象坐标中沿 Y 轴镜像,即镜像面为 `Y=0` 的 XZ 平面。镜像前后的几何 MUST 在最终旋转前保持 `(x, y, z)` 与 `(x, -y, z)` 的对应关系。 + +#### Scenario: Canonical mirroring + +- **WHEN** 上游 Depth Cutout 输出 canonical 几何并连接 `O Image Cutout Symmetry` +- **THEN** 对称节点先按 Y 轴删除越界半侧并镜像到另一侧 +- **AND** Fill Sides 开启时沿 Y 方向连接对应边界 + +### Requirement: Final symmetry axis is X with Z up + +`O Image Cutout Symmetry` SHALL 在 canonical Y 镜像完成后执行绕 Z 的固定旋转,使对称轴从 +Y 变为 +X,并保持 +Z 为 up。最终结果 MUST 在本地坐标中关于 `X=0` 对称。 + +#### Scenario: Default Depth Symmetry result + +- **WHEN** 用户通过 Depth Symmetry 入口生成对象 +- **THEN** 最终 evaluated local geometry 关于 `X=0` 对称 +- **AND** 最终 Z 范围反映图片 up 范围 +- **AND** 对象保持 up=Z + +### Requirement: Depth Symmetry final front direction may change + +Depth Symmetry 的最终朝向 SHALL 允许 canonical front `-Y` 随最终 yaw 旋转变为 `-X`。系统 MUST 不要求 Depth Symmetry 最终继续满足 front=-Y。 + +#### Scenario: Final front direction + +- **WHEN** 使用把 +Y 映射到 +X 的最终 yaw 旋转 +- **THEN** canonical front 方向 -Y 映射为 -X +- **AND** up 方向 +Z 保持不变 + +### Requirement: Depth Symmetry path has no Cutout Depth Axis + +Depth Symmetry 创建流程 SHALL 不设置或依赖 `O Image Depth Cutout` 的 `Depth Axis`。所有最终轴向语义 MUST 只由 `O Image Cutout Symmetry` 承担。 + +#### Scenario: Create Depth Symmetry modifiers + +- **WHEN** 用户通过 Depth Symmetry 入口创建对象 +- **THEN** 上游 `O Image Depth Cutout` 不包含 `Depth Axis` 输入 +- **AND** 上游 Cutout 输出 canonical +Y 深度结果 +- **AND** `O Image Cutout Symmetry` 决定最终 X 朝向 + diff --git a/openspec/specs/developer-tool-cli/spec.md b/openspec/specs/developer-tool-cli/spec.md new file mode 100644 index 0000000..a392fd8 --- /dev/null +++ b/openspec/specs/developer-tool-cli/spec.md @@ -0,0 +1,81 @@ +# developer-tool-cli Specification + +## Purpose +TBD - created by archiving change organize-tool-cli. Update Purpose after archive. +## Requirements +### Requirement: Grouped command entry points + +系统 SHALL 提供 `uv run node-group` 和 `uv run model` 入口,使用小写子命令,并移除旧脚本入口 `build-nodes`、`sync-models`。 + +#### Scenario: Display help +- **WHEN** 用户未指定子命令或传入 `--help` +- **THEN** 命令显示帮助并成功退出,不启动 Blender、模型准备或上传 + +#### Scenario: Invalid arguments +- **WHEN** 用户传入未知子命令或参数 +- **THEN** 命令显示参数错误并非零退出,业务动作不执行 + +### Requirement: Build and check node groups + +`node-group build` SHALL 依次构建资产、校验保存的资产、运行全量 pytest;`node-group check` SHALL 仅执行后两步。两者 SHALL 支持 `--blender` 选择 Blender,支持 `--skip-tests` 仅跳过 pytest。 + +#### Scenario: Build assets +- **WHEN** 用户运行 `uv run node-group build` +- **THEN** 更新 O_AnyImage.blend,并在资产校验和全量测试成功后成功退出 + +#### Scenario: Check existing assets +- **WHEN** 用户运行 `uv run node-group check` +- **THEN** 检查已有资产并运行全量测试,资产文件内容保持原样 + +#### Scenario: Skip Python tests +- **WHEN** build 或 check 指定 `--skip-tests` +- **THEN** 仍执行资产校验,跳过 pytest + +#### Scenario: Stop after failure +- **WHEN** 构建、校验或测试任一步失败 +- **THEN** 命令非零退出且不执行后续步骤 + +### Requirement: Preview node groups + +`node-group preview` SHALL 从当前源码在内存中构建并排列几何节点组,生成 HTML,输出绝对路径并默认打开浏览器,保持资产文件内容原样。默认文件 SHALL 保存于系统临时目录并在命令退出后可供查看。命令 SHALL 支持 `--blender`、`--output PATH`、`--no-open` 和 `--fragment`。 + +#### Scenario: Preview current source +- **WHEN** 用户运行 `uv run node-group preview` +- **THEN** 生成当前源码的可查看 HTML 并尝试打开浏览器 + +#### Scenario: Save without opening +- **WHEN** 用户指定 `--output PATH --no-open` +- **THEN** HTML 写入指定路径且不启动浏览器 + +#### Scenario: Export fragment +- **WHEN** 用户指定 `--fragment` +- **THEN** 输出 HTML 片段及其路径,不自动打开浏览器 + +### Requirement: Prepare and sync models + +模型命令 SHALL 提供 `prepare` 和 `sync` 两个子命令。`model prepare` SHALL 准备并校验目录中全部模型,复用有效缓存,按模型来源下载现成 ONNX 或准备源权重并导出(包含 PiSA-SR);`model sync` SHALL 在全部准备成功后上传 R2。`prepare` SHALL 不上传且不要求上传工具可用。 + +#### Scenario: Prepare locally +- **WHEN** 用户运行 `uv run model prepare` +- **THEN** 全部模型在本地通过既有大小和 SHA-256 校验,不执行上传 + +#### Scenario: Sync prepared models +- **WHEN** 用户运行 `uv run model sync` 且全部准备成功 +- **THEN** 上传全部模型到既定 R2 位置 + +#### Scenario: Preparation fails +- **WHEN** 任一模型准备或校验失败 +- **THEN** 命令非零退出,sync 不开始上传 + +#### Scenario: Download and export models +- **WHEN** 用户运行 `uv run model prepare` 且本地缺少有效模型 +- **THEN** 根据模型来源下载现成文件或准备源权重并导出,产物保存到既有本地目标并通过校验 + +#### Scenario: Reuse valid cache +- **WHEN** 本地模型已通过大小和 SHA-256 校验 +- **THEN** prepare 复用该产物,跳过对应下载和导出 + +#### Scenario: Export fails +- **WHEN** 源权重准备、转换或产物校验失败 +- **THEN** 命令非零退出,失败产物不替换正式目标 + diff --git a/openspec/specs/empty-image-edit-isolation/spec.md b/openspec/specs/empty-image-edit-isolation/spec.md new file mode 100644 index 0000000..897ab18 --- /dev/null +++ b/openspec/specs/empty-image-edit-isolation/spec.md @@ -0,0 +1,76 @@ +# empty-image-edit-isolation Specification + +## Purpose +TBD - created by archiving change isolate-shared-empty-image-edits. Update Purpose after archive. +## Requirements +### Requirement: Sharing is determined by other image Empty objects +系统 SHALL 在编辑结果提交时,以整个文件中是否存在其他保留的 Empty Image 引用同一源 Image 判断共享,覆盖隐藏物体及其他场景中的物体。判断 SHALL 使用物体与数据块身份,且 SHALL NOT 使用总引用数、Fake User 或界面计数显示作为分离依据。 + +#### Scenario: A hidden object shares the source +- **WHEN** 当前物体与一个隐藏的 Empty Image 引用同一 Image,且界面没有显示引用数量 +- **THEN** 系统识别为共享图片 + +#### Scenario: An object in another scene shares the source +- **WHEN** 另一场景中的独立 Empty Image 引用当前源 Image +- **THEN** 系统识别为共享图片 + +#### Scenario: One object is linked to multiple collections +- **WHEN** 仅当前 Empty 引用源 Image,且该物体链接到多个集合 +- **THEN** 系统按单独使用处理 + +#### Scenario: Non-Empty references exist +- **WHEN** 仅当前 Empty Image 引用源图,源图另有 Fake User 或用户手动建立的材质引用 +- **THEN** 系统按单独使用处理 + +#### Scenario: Sharing changes during a job +- **WHEN** Job 提交后、结果应用前,另一个 Empty Image 开始引用源图 +- **THEN** 最终提交按共享图片处理 + +### Requirement: Shared edits update only the current Empty +存在其他保留的 Empty Image 共用源图时,系统 SHALL 仅将当前物体绑定到独立编辑结果,并保持其他 Empty 的 Image 绑定、原图内容和显示状态。结果 SHALL 请求源 Image 的完整名称,由 Blender 处理唯一性后缀。 + +#### Scenario: Two Empty objects share one image +- **WHEN** A 与 B 共用同一 Image,用户编辑 A +- **THEN** A 使用编辑后的独立 Image,B 仍引用原 Image,原图像素、Packed 内容、名称及 B 的显示和动画设置保持原样 + +### Requirement: A sole image Empty retains its Image identity +没有其他保留的 Empty Image 共用源图时,系统 SHALL 将编辑结果提交到源 Image,保留数据块身份、完整名称与 Fake User 设置,且成功后 SHALL 释放本次临时结果数据。用户手动建立的其他引用 SHALL 保持原绑定。 + +#### Scenario: A sole Empty is edited +- **WHEN** 唯一使用源图的 Empty 完成图片编辑 +- **THEN** 其 Image 身份和完整名称保持不变,内容及尺寸更新为结果,保存重开后内容正确,且不保留额外结果 Image + +#### Scenario: A user manually references the source in a material +- **WHEN** 唯一 Empty 的源图另被用户手动用于材质 +- **THEN** 编辑按单独使用提交,材质引用仍绑定同一 Image + +#### Scenario: An animated job result is committed +- **WHEN** 单独使用的 Empty 收到动画图片编辑结果 +- **THEN** 源 Image 身份和名称保持不变,帧来源及打包结果更新正确,物体动画播放设置按现有工具语义配置 + +### Requirement: Frame considers surviving Empty objects +Frame SHALL 按成功合并后仍保留的 Empty Image 判断活动源图是否共享,并在结果提交成功后执行合并源物体删除。 + +#### Scenario: All other sharing objects are merged +- **WHEN** A 与 B 共用源图,Frame 将 B 合并到活动物体 A,且没有其他 Empty 使用源图 +- **THEN** A 的源 Image 原位更新,B 按合并规则删除 + +#### Scenario: A sharing object survives outside the merge +- **WHEN** Frame 将 B 合并到 A,合并之外的 C 仍引用 A 的源图 +- **THEN** A 使用独立结果,C 及其源 Image 保持原样 + +### Requirement: Failed or cancelled edits preserve source state +统一提交入口 SHALL 覆盖本地编辑与 Job 图片编辑。取消或提交失败时,系统 SHALL 保留或恢复源 Image 内容与身份、物体绑定、显示及动画设置,并释放本次临时资源。 + +#### Scenario: Sole-image commit fails +- **WHEN** 原位提交在写入内容或配置显示期间失败 +- **THEN** 源图内容、尺寸、Packed 数据及物体状态恢复到提交前状态,临时结果被清理 + +#### Scenario: Shared-image commit fails +- **WHEN** 共用图片的当前 Empty 提交失败 +- **THEN** 当前物体恢复原绑定,其他 Empty 及原图保持原样,临时结果被清理 + +#### Scenario: An edit is cancelled +- **WHEN** 用户预览编辑后取消 +- **THEN** 原图和所有 Empty 的绑定及显示状态保持原样 + diff --git a/openspec/specs/frame-edge-antialiasing/spec.md b/openspec/specs/frame-edge-antialiasing/spec.md new file mode 100644 index 0000000..e6229c8 --- /dev/null +++ b/openspec/specs/frame-edge-antialiasing/spec.md @@ -0,0 +1,85 @@ +# frame-edge-antialiasing Specification + +## Purpose +TBD - created by archiving change enable-image-alpha-blending-and-premul. Update Purpose after archive. +## Requirements +### Requirement: Frame samples geometric boundary coverage + +Frame SHALL 在输出像素与源投射轮廓或前后层级交界相交时使用固定 4×4 均匀子像素中心样本,分别判断有效覆盖及深度顺序。每个样本 SHALL 沿用现有底层 RGB 打底、前景预乘采样与独立 Alpha 合成公式。 + +#### Scenario: An opaque tilted edge covers half a pixel + +- **WHEN** 单张不透明图片仅覆盖该像素的 8 个子样本,其余 8 个没有源覆盖 +- **THEN** 输出 Alpha SHALL 为 0.5 +- **AND** 该像素 SHALL 不因中心点位于边缘某一侧而变为全有或全无 + +#### Scenario: The center misses a narrow projection + +- **WHEN** 像素中心未命中投射,但 4×4 网格存在有效命中 +- **THEN** 有效子样本 SHALL 参与边界合成 + +#### Scenario: Depth order changes inside a pixel + +- **WHEN** 两个源平面在像素覆盖区域内交换前后关系 +- **THEN** 每个子样本 SHALL 使用自己的深度顺序 +- **AND** 同深度 SHALL 沿用 active 置顶及现有稳定顺序 + +### Requirement: Frame resolves RGB and alpha with independent coverage weights + +边界像素 Alpha SHALL 为全部 16 个子样本 Alpha 的算术平均,无源样本贡献零。RGB SHALL 为具有几何源覆盖的子样本合成 RGB 的等权平均,SHALL NOT 以源 Alpha 作为 RGB 平均权重。全无覆盖像素 SHALL 保持透明黑色,不向 Frame Canvas 外扩展 RGB。 + +#### Scenario: Mask Add reveals the canvas outside every source + +- **WHEN** Frame 范围大于所有源投射,随后 Mask Add 将完全无覆盖区域的 Alpha 恢复为 1 +- **THEN** 该区域 RGB SHALL 保持黑色 +- **AND** 源边缘颜色 SHALL NOT 形成一像素外框 + +#### Scenario: A half-transparent foreground partially covers a transparent bottom image + +- **WHEN** 透明蓝色底图覆盖全部 16 个样本,Alpha 为 0.5 的红色前图覆盖其中 8 个 +- **THEN** 输出 RGB SHALL 为 (0.25, 0, 0.75) +- **AND** 输出 Alpha SHALL 为 0.25 + +#### Scenario: A white outer edge meets empty canvas + +- **WHEN** 不透明白色源覆盖 8 个子样本,其余没有源覆盖 +- **THEN** 输出 RGB SHALL 保持 (1, 1, 1),Alpha SHALL 为 0.5 +- **AND** 无源区域的黑色 SHALL 不混入白色边缘 + +#### Scenario: Transparent layers meet along an oblique boundary + +- **WHEN** 子样本合成结果 Alpha 均为零,但各自底层 RGB 不同 +- **THEN** 输出 SHALL 保留这些有效 RGB 的覆盖平均 +- **AND** Alpha SHALL 保持零 + +### Requirement: Restoring alpha preserves antialiased layer boundaries + +Frame SHALL 将边界颜色过渡存入结果 RGB,使实际 Packed Image 经 Mask Add 后仍保留图层间的抗锯齿过渡。 + +#### Scenario: Mask Add reveals a tilted foreground over a background + +- **WHEN** 倾斜图片与黑色或白色底图合成,经 Pack 后执行 Mask Add 恢复 Alpha +- **THEN** 边界 RGB SHALL 保留子像素覆盖混合的中间颜色 +- **AND** Mask Add SHALL 不将边缘恢复为单点覆盖产生的两色跳变 + +### Requirement: Frame keeps interior detail and bounded output processing + +Frame SHALL 对覆盖与层级稳定的内部像素保留现有中心双线性采样。结果 Canvas、输出尺寸限制和对象放置 SHALL 保持原约定;子样本处理 SHALL 按块执行并保持跨块结果一致。 + +#### Scenario: Pixel-aligned interior contains fine detail + +- **WHEN** 原尺寸对齐的图片内部包含交替明暗像素,且该区域没有几何层级边界 +- **THEN** 内部 RGB 与 Alpha SHALL 保持现有采样结果 + +#### Scenario: A boundary crosses processing chunks + +- **WHEN** 相同斜边使用不同的行块尺寸执行 Frame +- **THEN** 输出 SHALL 在浮点误差范围内一致 +- **AND** 边界 SHALL 不出现行块接缝 + +#### Scenario: Frame uses the maximum output resolution + +- **WHEN** 输出达到配置的最大分辨率 +- **THEN** 抗锯齿 SHALL 保持原输出尺寸 +- **AND** 系统 SHALL 不为全部源分配完整 4 倍宽、4 倍高的输出副本 + diff --git a/openspec/specs/frame-projection-clipping/spec.md b/openspec/specs/frame-projection-clipping/spec.md new file mode 100644 index 0000000..185c8fa --- /dev/null +++ b/openspec/specs/frame-projection-clipping/spec.md @@ -0,0 +1,36 @@ +# frame-projection-clipping Specification + +## Purpose +TBD - created by archiving change repair-image-edit-pipelines. Update Purpose after archive. +## Requirements +### Requirement: Frame accepts a finite visible intersection +Frame SHALL 依据画框内的连续有效投射判断正面积交叠。完整源图片的角点接近无穷远 SHALL NOT 导致画框内稳定有效的交叠被拒绝。 + +#### Scenario: Tilted source crosses the perspective origin +- **WHEN** 30° 倾斜源平面位于视图深度 0.2 且部分跨越观察平面,透视 X/Y 焦距系数为 1.7、2 或 4,Frame 内有正面积前方交叠 +- **THEN** Frame 接受交叠并生成结果 +- **AND** 对应有效源坐标与实际采样保持一致 + +#### Scenario: Source lies extremely close in front +- **WHEN** 源平面非常接近观察原点但画框内仍存在数值可稳定表达的正面积前方交叠 +- **THEN** Frame 接受交叠,不因源角点的远距离屏幕投射取消操作 + +#### Scenario: Orthographic source has non-positive depth +- **WHEN** 正交 Frame 与具有有限非正视图深度的源平面存在稳定正面积交叠 +- **THEN** Frame 正常完成 + +### Requirement: Invalid active overlap preserves inputs +Frame SHALL 在 active 图片与画框没有稳定正面积交叠时以 warning 取消,并保留全部参与对象的图片、变换和存在状态。 + +#### Scenario: Frame only touches an edge or misses the source +- **WHEN** Frame 仅接触 active 图片投射边缘,或没有有效交叠 +- **THEN** 操作取消,源对象与源图片均保持不变 + +### Requirement: Frame preserves independent color and alpha output +Frame SHALL 保留底层原色与前景混合得到的 RGB、独立累积的 Alpha 及边界子像素覆盖。源内透明 RGB SHALL 保留,完全未覆盖 Canvas SHALL 为 RGBA 全零。 + +#### Scenario: Frame result is revealed by Mask Add +- **WHEN** 多层 Frame 含透明底色与半透明前景,随后执行 Mask Add +- **THEN** 恢复区域显示原合成 RGB,源外透明 Canvas 的 RGB 保持为零 +- **AND** 分块高度变化不会改变合成结果 + diff --git a/openspec/specs/frame-tool/spec.md b/openspec/specs/frame-tool/spec.md new file mode 100644 index 0000000..764ea4d --- /dev/null +++ b/openspec/specs/frame-tool/spec.md @@ -0,0 +1,244 @@ +# frame-tool Specification + +## Purpose +TBD - created by archiving change support-frame-view-plane-intersections. Update Purpose after archive. +## Requirements +### Requirement: Frame samples every valid view-plane intersection +Frame SHALL 根据当前投影模式,以 Frame 屏幕位置对应的观察射线与每个参与 Image Empty 平面求交,并 SHALL 以有限且位于图片显示边界内的交点计算源像素坐标和几何深度。系统 SHALL NOT 仅因 Image Empty 的一个或多个角位于观察平面另一侧而取消整个操作。 + +#### Scenario: User finishes a frame in orthographic view at a non-positive view depth +- **WHEN** active Image Empty 在正交视图中具有有限稳定的屏幕投射,但其视图深度不为正 +- **AND** Frame 与 active 图片的有效投射存在正面积交叠 +- **THEN** 系统完成 Frame +- **AND** 结果保持操作时的正交屏幕图案 + +#### Scenario: Orthographic rays are parallel to the image plane +- **WHEN** 正交观察射线与 active Image Empty 平面平行,无法形成稳定的有限交点 +- **THEN** 系统取消 Frame +- **AND** 所有参与对象保持不变 + +#### Scenario: A perspective image partially crosses the view plane +- **WHEN** Image Empty 的一部分位于透视观察方向前方,另一部分越过观察原点 +- **AND** Frame 与 active 图片的有效前方投射存在正面积交叠 +- **THEN** 系统采样 Frame 内观察方向前方的稳定平面交点 +- **AND** 位于观察方向后方、与射线平行或超出图片边界的源样本为透明 +- **AND** 系统不因完整图片无法形成有限凸四边形而取消操作 + +#### Scenario: A perspective image is extremely close but remains in front +- **WHEN** Image Empty 的有效交点位于透视观察方向前方且非常接近观察原点 +- **AND** 屏幕到图片平面的映射仍可稳定求解 +- **THEN** 系统允许该投射参与 Frame +- **AND** 系统不使用固定世界距离阈值拒绝整张图片 + +#### Scenario: A frame contains a ray parallel to one source plane +- **WHEN** Frame 中部分屏幕位置的观察射线与某个参与 Image Empty 平面平行 +- **THEN** 该源在对应位置贡献透明样本 +- **AND** 其他稳定位置及其他参与源继续正常采样与合成 + +### Requirement: Frame validates active overlap from the valid projection region +Frame SHALL 使用 active Image Empty 在当前投影模式下可稳定求交的连续几何区域判断 Frame 交叠。交叠 SHALL 具有正面积,且 SHALL 独立于 active 图片的 Alpha 和离散输出像素是否恰好命中该区域。 + +#### Scenario: The complete active projection is unbounded but its valid part overlaps the frame +- **WHEN** active Image Empty 跨越透视观察原点,其完整角点投影无法表示为有限凸 Quad +- **AND** 其观察方向前方的有效投射与 Frame 存在正面积交叠 +- **THEN** 系统接受该 active 交叠并继续 Frame + +#### Scenario: Only invalid or rear-facing intersections overlap the frame +- **WHEN** Frame 内不存在 active Image Empty 的稳定前方透视交点,或不存在稳定的正交交点 +- **THEN** 系统以 warning 提醒用户 Frame 没有与 active Image Empty 的有效投射交叠 +- **AND** 系统取消操作并保持所有参与对象不变 + +#### Scenario: A narrow valid overlap falls between output pixel centers +- **WHEN** Frame 与 active 有正面积有效几何交叠,但当前输出尺寸的像素中心没有命中该区域 +- **THEN** 系统仍将该 Frame 视为具有 active 交叠 +- **AND** 离散输出像素可以按实际采样结果保持透明 + +### Requirement: Perspective Frame uses a visible result depth +Frame SHALL 在透视视图中优先使用 active Image Empty 世界原点的正视图深度放置 view-facing 结果。当 active 原点不在观察方向前方、但 active 图片存在有效前方交叠时,系统 SHALL 使用 active 有效交叠区域屏幕几何中心对应的稳定正交点深度放置结果。系统 SHALL NOT 以任意常量深度替代无法求解的结果位置。 + +#### Scenario: Active origin remains in front of the perspective view +- **WHEN** active Image Empty 世界原点位于透视观察方向前方 +- **THEN** 结果平面继续使用 active 原点的视图深度 + +#### Scenario: Active origin is behind while part of the image remains visible +- **WHEN** active Image Empty 世界原点不在透视观察方向前方 +- **AND** active 图片的有效前方投射与 Frame 存在正面积交叠 +- **THEN** 系统使用有效交叠区域屏幕几何中心与 active 图片平面的交点深度 +- **AND** 结果 Image Empty 位于观察方向前方并覆盖用户所画 Frame + +#### Scenario: No stable representative depth exists +- **WHEN** active 原点不能提供正深度,且有效交叠区域的代表射线无法稳定取得正交点深度 +- **THEN** 系统取消 Frame +- **AND** 所有参与对象保持不变 + +### Requirement: Frame replaces Crop Box +系统 SHALL 以 **Frame** 替换 **Crop Box**,并 SHALL 在 Crop Tool 组中按 Frame、Crop Lasso、Crop Polyline、Crop Perspective 的顺序注册四个子工具。Frame 的显示名称、Tool ID、Operator、激活入口、源码边界和文档 SHALL 统一使用 Frame 术语,不得保留 Crop Box 的公开入口或兼容名称。 + +#### Scenario: User opens the Crop Tool group +- **WHEN** 用户展开 Crop Tool 组 +- **THEN** 首个子工具显示为 Frame +- **AND** 其后依次为 Crop Lasso、Crop Polyline 和 Crop Perspective +- **AND** 工具组中不再显示 Crop Box + +#### Scenario: User activates the crop entry +- **WHEN** 用户从 AnyImage 菜单激活 Crop Tool +- **THEN** 系统激活 Frame + +### Requirement: Frame defines the complete output canvas +Frame SHALL 将用户在当前 3D Viewport 中拖出的矩形定义为结果图片的完整矩形 Canvas。该矩形可以小于或大于选中 Image Empty 的当前屏幕投射范围;Canvas 位于所有参与源投射范围外的像素 SHALL 使用透明 RGBA,系统 SHALL NOT 根据可见 Alpha 再次紧裁结果。 + +#### Scenario: User draws a smaller frame +- **WHEN** 用户围绕选中 Image Empty 当前投射的一部分拖出 Frame +- **THEN** 结果图片只包含 Frame 内采样到的投射图案 +- **AND** 结果 Canvas 严格对应用户所画 Frame + +#### Scenario: User draws a larger frame +- **WHEN** 用户拖出的 Frame 超出所有参与 Image Empty 的当前投射范围 +- **THEN** 结果图片保留 Frame 的完整范围 +- **AND** 未被源投射覆盖的区域为透明 +- **AND** 系统不移除这些透明边缘 + +### Requirement: Frame bakes and composites selected viewport projections +Frame SHALL 使用完成矩形手势时的 Viewport 投影、视图朝向、区域尺寸和每个参与 Image Empty 的世界变换,把所有参与图片的当前投射重采样为结果 RGBA。每个输出像素 SHALL 按观察射线上的几何深度将有效样本从远到近排序,再使用预乘 Alpha source-over 合成;active Image Empty SHALL 在相同深度的样本中位于其他参与对象之上。采样 SHALL 使用预乘 Alpha 插值,源图片范围外 SHALL 采样为透明。 + +#### Scenario: One selected image is oblique to the view +- **WHEN** 倾斜的选中 Image Empty 在当前透视视图中呈现为梯形投射,用户完成 Frame +- **THEN** 结果图片烘焙该梯形投射在 Frame 内的当前可见外观 +- **AND** 操作完成瞬间的屏幕图案与操作前一致 + +#### Scenario: Selected images overlap in depth +- **WHEN** 多个参与 Image Empty 的投射在 Frame 内重叠 +- **THEN** 系统逐像素按空间深度从远到近合成 RGBA +- **AND** 较近图片的透明区域仍显示后方图片 + +#### Scenario: Active image overlaps another image at the same depth +- **WHEN** active Image Empty 与另一参与对象在同一深度产生重叠样本 +- **THEN** active Image Empty 的样本合成在另一对象之上 + +#### Scenario: Frame extends beyond the projected source +- **WHEN** Frame 同时覆盖参与源投射内部和外部区域 +- **THEN** 系统重采样并合成内部的源 RGBA +- **AND** 系统为外部区域写入透明 RGBA + +#### Scenario: Frame has no active projection overlap +- **WHEN** 完成的 Frame 与 active Image Empty 的屏幕投射没有几何重叠 +- **THEN** 系统以 warning 提醒用户 +- **AND** 系统取消操作并保留所有选中对象不变 + +### Requirement: Viewport defines output resolution +Frame SHALL 使用所画矩形在当前 Region 中的屏幕像素宽高作为结果尺寸依据,并 SHALL 保持 Frame 的屏幕宽高比。结果宽高 SHALL NOT 超过当前 Region 对应维度,最长边 SHALL NOT 超过 Preferences 中可配置的 Frame 最大长边。active 或其他参与图片的原始分辨率、对象距离和透视缩放 SHALL NOT 直接放大结果尺寸。 + +#### Scenario: Other selected image has a higher resolution +- **WHEN** 非 active 参与图片的分辨率高于 active 图片 +- **THEN** 结果尺寸仍只由当前 Viewport 中的 Frame 像素尺寸决定 +- **AND** 非 active 图片重采样到该结果 Canvas + +#### Scenario: Frame is drawn around a distant image +- **WHEN** active Image Empty 距离视角很远或投射尺度很小 +- **THEN** 结果尺寸仍对应 Frame 的当前屏幕像素尺寸 +- **AND** 对象距离不会产生超出 Viewport 分辨率的图片 + +#### Scenario: Frame exceeds the configured maximum +- **WHEN** Frame 屏幕像素最长边超过 Preferences 配置上限 +- **THEN** 系统等比限制结果尺寸 +- **AND** 结果宽高比继续对应 Frame + +### Requirement: Transparent output edges avoid black interpolation +Frame SHALL 保持未覆盖区域的 Alpha 为 0,并 SHALL 使用相邻可见颜色扩展紧邻可见内容的透明像素 RGB。颜色扩展 SHALL NOT 改变任何像素的 Alpha 或 Frame Canvas 范围。 + +#### Scenario: Projected image ends inside the frame +- **WHEN** 不透明或半透明图片边缘外侧是 Frame 的透明区域 +- **THEN** 外侧像素 Alpha 保持为 0 +- **AND** 线性纹理插值不会因外侧 RGB 0 形成明显黑边 + +### Requirement: Frame creates a view-facing Image Empty +Frame SHALL 使用 active Image Empty 的原 Blender Object 承载结果 Image,并更新其世界变换和 Image Empty 显示范围,使本地 XY 图片平面平行于操作完成时的 Viewport 视平面、本地正面朝向该视角,且结果矩形在该视角中覆盖用户所画 Frame。结果平面 SHALL 位于穿过 active Image Empty 世界原点、并与视平面平行的深度平面上。 + +#### Scenario: User finishes a frame in perspective view +- **WHEN** 用户在透视 Viewport 中完成 Frame +- **THEN** 结果 Image Empty 朝向操作时的视角 +- **AND** 结果四边在该视角中对应 Frame 的四边 +- **AND** 结果使用 active Image Empty 世界原点所在的视图深度 + +#### Scenario: User finishes a frame in orthographic view +- **WHEN** 用户在正交 Viewport 中完成 Frame +- **THEN** 结果 Image Empty 平行于该正交视平面并覆盖 Frame +- **AND** 烘焙后的图案保持操作时的屏幕外观 + +### Requirement: Frame operates on selected still Image Empties +Frame SHALL 以当前 Selection 内的 active still Image Empty 为基准对象,并将所有选中的 still Image Empty 作为参与源;选中的非 Image Empty 对象 SHALL 被忽略。Frame SHALL 允许矩形手势从 Viewport 任意位置开始。active object 未被选中、不是 still Image Empty、任一选中 Image Empty 不是 still image、Frame 尺寸无效或任一参与投影无法稳定求解时,系统 SHALL 取消整个操作并保持所有对象不变。 + +#### Scenario: User starts outside the source projection +- **WHEN** active object 是 still Image Empty,且用户从源投射范围外开始拖出一个与源投射相交的 Frame +- **THEN** 系统使用 active Image Empty 及其他选中的 still Image Empty 完成 Frame 操作 + +#### Scenario: Active object is unsupported +- **WHEN** active object 不是 still Image Empty +- **THEN** Frame 不开始图片重采样、合成或对象替换 +- **AND** 系统以 warning 提醒用户选择一个 active Image Empty + +#### Scenario: Active image is not selected +- **WHEN** active object 是 still Image Empty,但它不属于当前 Selection +- **THEN** Frame 不进入矩形操作 +- **AND** 系统以 warning 提醒用户选择 active Image Empty + +#### Scenario: Selection contains images but active object is not an image +- **WHEN** Selection 含有 still Image Empty,但 active object 是非图片对象 +- **THEN** Frame 不使用 Selection 中的其他图片作为隐式基准 +- **AND** 系统以 warning 取消操作 + +#### Scenario: Selection contains a non-image object +- **WHEN** active object 是 still Image Empty,且 Selection 还包含非 Image Empty 对象 +- **THEN** Frame 忽略非 Image Empty 对象 +- **AND** 非 Image Empty 对象不参与合成或删除 + +#### Scenario: A selected Image Empty is animated +- **WHEN** 任一选中的 Image Empty 使用视频或图片序列 +- **THEN** 系统取消整个 Frame 操作 +- **AND** 所有选中对象保持不变 + +### Requirement: Frame has no edit options +Frame SHALL 始终让 active Image Empty 承载结果,并在结果完整创建和放置后删除其他参与合并的选中 Image Empty。整个替换与删除 SHALL 属于同一个 Undo 操作;失败时 SHALL 保留全部输入对象。Frame SHALL NOT 提供 Refine Selection、Invert 或 Keep Original。系统 SHALL 删除 Crop Box 对应的 `crop_box_refine_selection`、`crop_box_invert` 和 `crop_box_keep_original` Scene Properties;Crop Lasso、Crop Polyline 和 Crop Perspective 的独立选项 SHALL 保持不变。 + +#### Scenario: User opens Frame settings +- **WHEN** 用户激活 Frame +- **THEN** 工具设置不显示 Refine Selection、Invert 或 Keep Original + +#### Scenario: User completes a frame +- **WHEN** Frame 成功生成结果 +- **THEN** active Blender Object 改为承载合成结果 Image 和新的视平面对齐变换 +- **AND** 其他参与合并的 Image Empty 被删除 +- **AND** 被忽略的非 Image Empty 对象保持不变 + +#### Scenario: Multi-image merge is undone +- **WHEN** 用户撤销一次成功的多图 Frame +- **THEN** active Image Empty 恢复操作前的 Image 与变换 +- **AND** 其他被删除的参与 Image Empty 恢复 + +#### Scenario: Existing file contains removed Crop Box properties +- **WHEN** 扩展加载曾保存 Crop Box 选项的 `.blend` 文件 +- **THEN** 运行时不注册或读取这些已删除属性 +- **AND** 其他 Crop 子工具的设置仍按各自属性工作 + +### Requirement: Image tools preserve ordinary click selection +Frame、Crop Lasso 与 Cutout Lasso SHALL 只在鼠标发生拖动后启动图片手势,普通点击 SHALL 使用 Viewport 对象选择行为。Polyline 与 Perspective 首次点击另一对象时 SHALL 只切换 active selection,不开始图片编辑。图片工具 SHALL NOT 启用或显示 Blender fallback selection 的 Drag 设置。 + +所有图片编辑手势 SHALL 只在 active object 是当前 Selection 内的 Image Empty 时启动;否则系统 SHALL 以 warning 取消该次手势。 + +#### Scenario: User clicks another Image Empty while Frame is active +- **WHEN** 已有一个 active Image Empty,用户普通点击另一 Image Empty +- **THEN** Blender 按原生规则选择该对象并使其 active +- **AND** Frame 不开始、不显示矩形 Overlay,也不生成图片 + +#### Scenario: User shift-clicks another object +- **WHEN** 任一图片工具处于 active,用户 Shift 单击另一对象 +- **THEN** Blender 保留原生追加或切换选择行为 + +#### Scenario: User drags while Frame is active +- **WHEN** 用户按住左键并移动到 Blender 的 drag threshold 之外 +- **THEN** Frame 才收集当前 Selection 并进入矩形操作 + +#### Scenario: User opens image tool settings +- **WHEN** 用户激活 Frame、Crop Lasso、Crop Polyline、Crop Perspective 或 Cutout Lasso +- **THEN** Tool Settings 不显示 `Drag: Select Box/Lasso` 等 fallback selection 设置 + diff --git a/openspec/specs/frame-transparent-rgb/spec.md b/openspec/specs/frame-transparent-rgb/spec.md new file mode 100644 index 0000000..220a8bb --- /dev/null +++ b/openspec/specs/frame-transparent-rgb/spec.md @@ -0,0 +1,95 @@ +# frame-transparent-rgb Specification + +## Purpose +TBD - created by archiving change preserve-frame-bottom-layer-rgb. Update Purpose after archive. +## Requirements +### Requirement: Frame selects the bottom valid source for hidden RGB + +Frame SHALL 在每个输出像素复用当前有效投影和从远到近的稳定深度顺序,以第一个几何有效样本作为底层 RGB 来源。底层判定 SHALL 独立于 Alpha,并 SHALL 沿用相同深度时 active 位于最上层、其他对象按名称稳定排序的规则。 + +#### Scenario: Transparent foreground overlaps a transparent background +- **WHEN** 蓝色底层和红色前景覆盖同一像素,且两者 Alpha 都为零 +- **THEN** 输出 RGB SHALL 为底层蓝色 +- **AND** 输出 Alpha SHALL 为零 + +#### Scenario: The farthest image does not cover a pixel +- **WHEN** 最远图片在某像素没有有效投影,而其他图片具有有效投影 +- **THEN** 该像素的底层来源 SHALL 为实际覆盖它的最远有效图片 + +#### Scenario: Transparent images share the same depth +- **WHEN** active 与另一透明图片在相同深度覆盖同一像素 +- **THEN** 底层 RGB SHALL 来自非 active 图片 +- **AND** 多个非 active 同深度图片 SHALL 沿用现有名称排序确定底层 + +#### Scenario: Source planes cross +- **WHEN** 两张透明图片的平面相交,并在不同像素交换前后关系 +- **THEN** 各像素的隐藏 RGB SHALL 分别来自该像素处最底层的有效图片 + +#### Scenario: Projection validity depends on the view mode +- **WHEN** 一个候选底层在透视视图中位于观察方向后方,或无法取得有限且位于源边界内的交点 +- **THEN** 该样本 SHALL 不作为底层来源 +- **AND** 正交视图中有限且位于源边界内的非正深度交点 SHALL 继续具有底层资格 + +### Requirement: Frame preserves bottom RGB in transparent output + +Frame SHALL 在最终 straight RGBA 转换后 Alpha 为零且具有有效源覆盖的像素中,保存底层源未乘 Alpha 的 RGB 双线性采样结果。采样 SHALL 使用现有 Frame 投影坐标和边界约定;系统 SHALL 保持当前 Alpha 可见性数值判定与最终 Alpha 结果。 + +#### Scenario: A masked single image is framed without resampling displacement +- **WHEN** 单张图片经 Mask 剔除 Alpha 后,以原尺寸和像素中心对齐的映射执行 Frame +- **THEN** 透明区域的 RGB SHALL 与源图片对应像素一致 +- **AND** Alpha SHALL 保持剔除后的结果 + +#### Scenario: Transparent source colors require interpolation +- **WHEN** Frame 缩放或透视映射在底层完全透明区域取得非整数像素采样位置 +- **THEN** RGB SHALL 为底层原始 RGB 的双线性插值结果 +- **AND** Alpha SHALL 保持零 + +#### Scenario: Mask Add follows a packed Frame result +- **WHEN** 用户对 Mask 剔除后经 Frame 生成的单图 Packed Image 执行 Mask Add +- **THEN** 提升 Alpha 的区域 SHALL 显示 Frame 保留的底层颜色 +- **AND** 该颜色 SHALL 在 Packed Image 像素中实际存在 + +### Requirement: Frame blends foreground RGB over bottom color with independent alpha + +Frame SHALL 以最底层有效源的原始 RGB 双线性样本初始化颜色,随后从远到近对每个前景计算 C = P_front + C × (1 - A_front),其中 P_front 为预乘后插值的 RGB。Alpha SHALL 从零开始对包括底层在内的全部有效层独立执行 source-over,并沿用现有数值判定。最终 RGB SHALL 不除以合成 Alpha。 + +#### Scenario: Both layers are partially transparent +- **WHEN** 蓝色底层 Alpha 为 0.5,红色前景 Alpha 为 0.25 +- **THEN** 输出 Alpha SHALL 为 0.625 +- **AND** straight RGB SHALL 为红色 0.25、绿色 0、蓝色 0.75 + +#### Scenario: A visible foreground overlaps a transparent bottom layer +- **WHEN** 蓝色底层 Alpha 为零,红色前景 Alpha 为 0.25 +- **THEN** 输出 SHALL 为 RGB (0.25, 0, 0.75) 和 Alpha 0.25 +- **AND** 恢复 Alpha 后 SHALL 保留相同 RGB + +#### Scenario: Hidden neighbor colors meet a visible sampling edge +- **WHEN** 前景插值邻域同时含有可见红色像素和完全透明绿色像素 +- **THEN** 前景采样 SHALL 沿用预乘插值结果并按采样 Alpha 混入底色 +- **AND** 隐藏绿色 SHALL 不污染可见红色 + +#### Scenario: Restoring alpha reveals a continuous transition into white + +- **WHEN** 前景原图的黑色背景被 Alpha 隐藏,前景边缘 Alpha 逐渐下降,且底层原始 RGB 为白色、Alpha 为零 +- **THEN** RGB SHALL 随前景 Alpha 连续过渡到白色 +- **AND** Packed Image 经 Mask Add 恢复 Alpha 后 SHALL 保留该过渡 + +### Requirement: Source coverage takes priority over edge color extension + +Frame SHALL 保持完整 Canvas 和源外透明边缘扩色。存在有效源覆盖且最终透明的像素 SHALL 使用底层 RGB;没有有效源覆盖的像素 SHALL 保持 Alpha 零,并沿用紧邻可见内容的一圈 RGB 扩色,其余区域保持透明黑色。 + +#### Scenario: Transparent source pixels border visible content +- **WHEN** 源内部透明像素紧邻可见像素,且隐藏 RGB 与相邻可见颜色不同 +- **THEN** 源内部透明像素 SHALL 保留底层原始 RGB +- **AND** 该 RGB SHALL 不被边缘扩色覆盖 + +#### Scenario: The frame extends beyond all source images +- **WHEN** Frame 包含所有源投影之外的区域 +- **THEN** 外部像素 Alpha SHALL 为零 +- **AND** 一圈扩色之外的无覆盖像素 SHALL 为透明黑色 +- **AND** Canvas 尺寸 SHALL 保持原有 Frame 规则 + +#### Scenario: A visible edge crosses a processing chunk boundary +- **WHEN** 可见边缘跨越 Frame 行块边界 +- **THEN** 透明边缘颜色 SHALL 与使用同一规则处理完整画布的结果一致 + diff --git a/openspec/specs/geometry-depth-controls/spec.md b/openspec/specs/geometry-depth-controls/spec.md new file mode 100644 index 0000000..58ba71f --- /dev/null +++ b/openspec/specs/geometry-depth-controls/spec.md @@ -0,0 +1,27 @@ +# geometry-depth-controls Specification + +## Purpose +TBD - created by archiving change refine-image-output-semantics. Update Purpose after archive. +## Requirements +### Requirement: Geometry depth groups expose Reference Depth +系统 SHALL 在 `O Image Depth Plane`、`O Image Cutout`、Depth Balloon 与 Depth Surface 的 Geometry Nodes 接口中使用 `Reference Depth` 表示深度纹理的参考距离,并且 MUST 不再公开 `Base Plane Depth`。 + +#### Scenario: Depth Plane initializes its reference +- **WHEN** 系统根据 `depth.json` 创建 Depth Plane Modifier +- **THEN** `reference_depth × uniform_scale` SHALL 写入 `Reference Depth` + +#### Scenario: Depth Cutout initializes its reference +- **WHEN** 系统根据最终网格内容标定 Depth Balloon 或 Depth Surface +- **THEN** 标定结果 SHALL 写入主 Cutout 与对应子组的 `Reference Depth` + +#### Scenario: Node asset exposes the current interface only +- **WHEN** 系统构建并验证 `O_AnyImage.blend` +- **THEN** 所有相关节点组、面板、依赖连线和 Modifier 输入 SHALL 只包含 `Reference Depth`,不得保留旧名称或兼容 socket + +### Requirement: Reference Depth keeps existing geometry meaning +`Reference Depth` SHALL 继续作为采样 Depth 的运算基准,不改变 Depth Plane、Depth Balloon 或 Depth Surface 的几何方向、尺度、厚度和位移公式。 + +#### Scenario: Reference control changes relative depth +- **WHEN** 用户修改 `Reference Depth` +- **THEN** Geometry Nodes SHALL 使用 `Reference Depth - sampled depth` 的既有相对深度关系更新几何 + diff --git a/openspec/specs/geometry-field-flow/spec.md b/openspec/specs/geometry-field-flow/spec.md new file mode 100644 index 0000000..0d69177 --- /dev/null +++ b/openspec/specs/geometry-field-flow/spec.md @@ -0,0 +1,61 @@ +# geometry-field-flow Specification + +## Purpose +TBD - created by archiving change remove-geometry-capture-attributes. Update Purpose after archive. +## Requirements +### Requirement: Geometry graphs prohibit Capture Attribute + +全部生产几何节点定义、嵌套几何组、保存资产及相关测试辅助图 MUST 不使用 GeometryNodeCaptureAttribute。 + +#### Scenario: Inspect definitions and saved assets +- **WHEN** 分别构建节点定义并加载保存资产,递归检查全部几何组及嵌套组 +- **THEN** 每个已访问几何组的 Capture Attribute 数量均为零 + +### Requirement: Fields are consumed directly when evaluation is equivalent + +字段 SHALL 在消费位置直接连接,明确必要域转换;跨几何变化时 SHALL 优先审查调整消费顺序及源几何采样,以减少中间存储。多个消费者或跨区域连线 MUST 不作为存值的充分理由。 + +#### Scenario: Reuse a field without geometry changes +- **WHEN** 多个消费者所在几何状态、域、类型与输入属性版本允许等价求值 +- **THEN** 消费者直接使用字段及必要域转换,保持原结果而不新增存储 + +#### Scenario: Consume a field after topology changes +- **WHEN** 切分、删面、挤出或合并改变源字段求值上下文 +- **THEN** 实现明确源几何与映射,验证结果等价,并仅保存无法通过合理调整消费或采样消除的必要数据 + +### Requirement: Named storage has an explicit purpose and lifetime + +每处 Store Named Attribute SHALL 有明确的实际消费者与存储必要性,记录名称、类型、域、Selection 和阶段。必要命名协议 SHALL 保留;临时属性 SHALL 在最后消费者后清理,并保留已有用户属性。 + +#### Scenario: Preserve material protocols +- **WHEN** 输出几何供材质消费 UVMap 或 o_normal_scale +- **THEN** 属性域与数值保持正确,包括角 UV 以及正背面和侧壁掩码 + +#### Scenario: Finish consuming temporary data +- **WHEN** 几何处理完成临时命名数据的最后一次消费 +- **THEN** 最终输出不携带新增内部临时属性,且输入已有用户属性的名称、类型和值按原行为保留 + +### Requirement: Geometry behavior and graph readability are preserved + +重构 SHALL 保持修改器接口、几何结果、UV 和材质行为,并逐组检查字段消费关系与完整节点布局的可读性。 + +#### Scenario: Validate depth processing +- **WHEN** 使用非均匀深度测试 Split 开关、零与正厚度、独立重叠表面、Balloon/Uniform、圆化、膨胀及法线平滑 +- **THEN** 实际几何与实施前行为基线等价,索引重排按拓扑和对应几何数据比较 + +#### Scenario: Validate cut boundary smoothing and panorama +- **WHEN** 对原轮廓、切分边界、平滑迭代和 Panorama 接缝、极点、无效像素及 Valid Only 求值 +- **THEN** 原轮廓保护、影响范围、固定映射、球面采样与 UV 保持预期行为 + +#### Scenario: Inspect material inheritance +- **WHEN** 输入为空或带有多个材质槽,执行 Image Plane 或 Panorama 的材质继承 +- **THEN** 材质槽及面材质索引符合原行为,生成几何保留且输入来源几何正确处理 + +### Requirement: Rules tests and saved assets agree + +项目规范及当前节点资产说明 SHALL 明确禁止 Capture 并采用一致的字段传递规则。测试 SHALL 覆盖结构约束与真实行为;节点资产 SHALL 重建并经过独立加载验证,相关测试及全量测试正常退出后完成交付。 + +#### Scenario: Deliver a geometry graph change +- **WHEN** 本次节点整理完成 +- **THEN** 源码、规范、测试和 O_AnyImage.blend 同步,Capture 旧结构断言已替换,uv run node-group build 与 uv run pytest 成功且正常退出 + diff --git a/openspec/specs/geometry-node-smoothing/spec.md b/openspec/specs/geometry-node-smoothing/spec.md new file mode 100644 index 0000000..3425da5 --- /dev/null +++ b/openspec/specs/geometry-node-smoothing/spec.md @@ -0,0 +1,38 @@ +# geometry-node-smoothing Specification + +## Purpose +TBD - created by archiving change remove-geometry-node-smoothing. Update Purpose after archive. +## Requirements +### Requirement: 主节点组不得执行末端整体 mesh smooth +系统 SHALL 让 `O Image Cutout`、`O Image Depth Cutout`、`O Image Depth Plane`、`O Image Depth Panorama` 与 `O Image Relief Plane` 在主体几何构建完成后直接进入着色或输出阶段,不得追加由通用 `Smooth` 控制的整体顶点位置平滑。 + +#### Scenario: 节点组接口不再提供整体 smooth +- **WHEN** 检查任一受影响节点组的公开输入接口 +- **THEN** 该接口不包含 `Smooth` + +#### Scenario: 输出路径不经过整体 smooth +- **WHEN** 构建任一受影响节点组并追踪其最终业务几何到输出的连接 +- **THEN** 输出路径不包含原先由 `Smooth` 和 `Smooth Weight` 控制的整体 `expand_smooth()` 后处理 + +### Requirement: Smooth Weight 按独立消费者保留 +系统 MUST 在 `Smooth Weight` 仍控制 `Boundary Smooth` 时保留该输入,并在它仅服务已删除的整体 smooth 时删除该输入。 + +#### Scenario: 深度节点组保留共享权重 +- **WHEN** 检查 `O Image Depth Cutout`、`O Image Depth Plane` 或 `O Image Depth Panorama` +- **THEN** 节点组仍提供 `Smooth Weight`,且该值继续参与边界平滑计算 + +#### Scenario: 无独立消费者的权重被删除 +- **WHEN** 检查 `O Image Cutout` 或 `O Image Relief Plane` +- **THEN** 节点组接口不包含 `Smooth Weight` + +### Requirement: 专用平滑行为保持可用 +系统 SHALL 保留与末端整体 mesh smooth 不同职责的边界平滑、法线平滑、填充墙体平滑和 smooth shading。 + +#### Scenario: 深度边界和法线控制仍存在 +- **WHEN** 检查受影响深度节点组的接口与计算路径 +- **THEN** 既有 `Boundary Smooth` 与适用的 `Normal Smooth` 仍按原职责工作 + +#### Scenario: 填充与着色平滑不受影响 +- **WHEN** 构建相关节点组并检查输出 +- **THEN** `Fill Smooth` 仍仅用于生成墙体内部,且既有 smooth shading 仍应用于对应表面 + diff --git a/openspec/specs/hat-upscale/spec.md b/openspec/specs/hat-upscale/spec.md new file mode 100644 index 0000000..26379c9 --- /dev/null +++ b/openspec/specs/hat-upscale/spec.md @@ -0,0 +1,49 @@ +# hat-upscale Specification + +## Purpose +TBD - created by archiving change simplify-ai-model-tiers. Update Purpose after archive. +## Requirements +### Requirement: HAT Sharper 提供可验证的小体积资源 +系统 SHALL 提供官方 Real HAT GAN x4 Sharper 对应的模型资产,完整记录上游来源、文件大小及校验信息。发布文件 SHALL 小于 60 MB,保持 FP32 输入输出,并通过 CPU / DirectML 与原始 FP32 参考的数值对照。 + +#### Scenario: 校验发布模型 +- **WHEN** 对预先固定的样图执行正式导出资产 +- **THEN** 输出全部有限,未裁剪的 `[0,1]` 标度输出误差满足 MAE ≤ 0.0005、P99 ≤ 0.005、最大误差 ≤ 0.02 + +#### Scenario: 实验精度路径失败 +- **WHEN** 导出或运行路径在验收中产生 NaN、Inf 或超过预设误差 +- **THEN** 该资产不能登记为可发布模型,也不能标记验收完成 + +### Requirement: 三款超分均保持最终两倍输出 +系统 SHALL 对限制后的分析输入保持宽高各 2 倍的最终输出;HAT 与现有两款 Real-ESRGAN SHALL 保持相同的透明度合成和帧输出约定。 + +#### Scenario: 普通与透明静态图片 +- **WHEN** 用户用 HAT Sharper 放大宽 W、高 H 的分析输入 +- **THEN** 最终图片尺寸为 `2W×2H`,原始 Alpha 按现有超分规则缩放并合成 + +#### Scenario: 多帧输入 +- **WHEN** 使用 HAT Sharper 处理支持的多帧输入 +- **THEN** 每帧输出保持两倍尺寸及既有编号,帧间复用模型资源 + +### Requirement: 分块支持合法图片尺寸且控制边界误差 +HAT SHALL 支持小图、奇数尺寸和跨多个分块的输入,输出覆盖全部像素;分块配置 SHALL 通过纹理、细线和透明边缘样图检查,并相对完整参考记录接缝误差。 + +#### Scenario: 奇数尺寸与边缘块 +- **WHEN** 输入宽高不是分块步长的整数倍 +- **THEN** 结果保持准确的两倍尺寸,边缘完整,没有空白条带或重复像素区 + +#### Scenario: 自然纹理跨越接缝 +- **WHEN** 对固定跨缝样图检查最终 2× 输出 +- **THEN** 没有由拼接造成的可见直线接缝,并记录相对整图参考的接缝和内部误差 + +### Requirement: 推理取消和资源错误可处理 +系统 SHALL 沿用超分任务的取消、会话复用和资源释放机制;资源不足时 SHALL 报告当前模型及可操作的调整建议。 + +#### Scenario: 分块间取消 +- **WHEN** 用户取消正在处理多块图片的 HAT 任务 +- **THEN** 后续块停止推理,未完成产物按现有任务规则清理 + +#### Scenario: 显存或内存不足 +- **WHEN** 当前设备无法完成 HAT 推理 +- **THEN** 返回包含模型和设备上下文的资源错误,提示减小分析输入或选择更轻量模型 + diff --git a/openspec/specs/hdr-background-removal/spec.md b/openspec/specs/hdr-background-removal/spec.md new file mode 100644 index 0000000..3f2c8b3 --- /dev/null +++ b/openspec/specs/hdr-background-removal/spec.md @@ -0,0 +1,93 @@ +# hdr-background-removal Specification + +## Purpose +TBD - created by archiving change add-ben2-hdr-support. Update Purpose after archive. +## Requirements +### Requirement: HDR still image preparation + +系统 SHALL 为可读的浮点静态图片提供 Remove Background,覆盖 Image Empty 与材质 Image Texture 的外部、packed 和 generated 图片。输入 SHALL 采用任务开始时的当前像素快照。 + +#### Scenario: An HDR still is submitted +- **WHEN** 用户对可读的 EXR、HDR 或其他浮点静态图片执行 Remove Background +- **THEN** 系统捕获当前 RGBA、尺寸与颜色解释,并提交普通 RGB 识别预览 + +#### Scenario: Unsaved pixels are present +- **WHEN** 源浮点图包含未保存的像素编辑 +- **THEN** 识别预览与最终 RGB 均使用当前编辑后的快照 + +#### Scenario: An HDR animation is submitted +- **WHEN** 用户对 HDR 序列或浮点动画执行 Remove Background +- **THEN** 系统在创建推理任务前提示本次 HDR 功能支持静态图,并保持源图状态 + +### Requirement: Deterministic recognition preview + +系统 SHALL 通过确定性的曝光范围压缩及色彩编码生成 BEN2 的普通 RGB 预览,原始 RGB SHALL 独立保留。预览 SHALL 正确解释源 alpha,且不受用户显示变换设置影响。 + +#### Scenario: Highlights exceed one +- **WHEN** 源图包含多个大于 1 的不同亮度值 +- **THEN** 预览先进行色调映射再编码,原始快照保留这些数值 + +#### Scenario: Display settings change +- **WHEN** 相同源像素分别在不同 View Transform、Look 或 Exposure 下提交 +- **THEN** 识别预览一致 + +#### Scenario: The image is black or contains negative values +- **WHEN** 可读源像素为全黑或包含有限负颜色值 +- **THEN** 预览数值有限,源 RGB 不因预览映射改变 + +### Requirement: Float alpha result + +HDR 去背景 Job SHALL 返回原始尺寸的 float32 alpha 数组,数值有限且位于 0–1。系统 SHALL 在使用前验证产物。 + +#### Scenario: BEN2 predicts a soft edge +- **WHEN** 模型预测连续透明度边缘 +- **THEN** alpha 经过浮点缩放返回,合成前不量化为 8-bit + +#### Scenario: Alpha is invalid +- **WHEN** alpha 文件损坏、尺寸不符或包含非有限及越界数值 +- **THEN** 系统报告结果错误,保持源图片与目标绑定 + +### Requirement: HDR color and transparency persistence + +系统 SHALL 保留源 RGB 的场景线性色彩及动态范围,并以源 alpha 与预测 alpha 的乘积作为结果透明度。结果 SHALL 为同尺寸浮点图像并采用 32-bit float RGBA EXR 持久化,颜色误差 SHALL 限于浮点运算与必要颜色转换的容差。 + +#### Scenario: Straight alpha is present +- **WHEN** 源图为 straight alpha 且存在大于 1 的 RGB 和部分透明像素 +- **THEN** 结果 RGB 保留,alpha 等于源 alpha 乘预测 alpha + +#### Scenario: Premultiplied alpha is present +- **WHEN** 源图采用 PREMUL 且包含部分透明及完全透明像素 +- **THEN** 系统按 alpha 关联语义合成,边缘颜色正确且结果没有 NaN 或 Inf + +#### Scenario: The project is saved and reopened +- **WHEN** 用户保存含处理结果的 blend 文件后重新打开 +- **THEN** 结果尺寸、HDR 数值、颜色解释和透明度保持一致 + +### Requirement: Transactional target update + +系统 SHALL 复用既有图片目标提交规则,保护共享使用者并支持撤销、重做与错误恢复。Blender 数据访问 SHALL 发生于主线程,任务通信 SHALL 使用可序列化普通参数。 + +#### Scenario: Another owner shares the image +- **WHEN** 源图片存在其他实际使用者 +- **THEN** 仅发起操作的目标取得独立 HDR 结果,其他使用者仍引用原图 + +#### Scenario: The source changes during inference +- **WHEN** 原目标失效、改绑图片或源像素与颜色解释发生修改 +- **THEN** 系统拒绝覆盖当前状态,提示重新执行并清理本次临时资源 + +#### Scenario: The operation is cancelled or fails +- **WHEN** 准备、推理、合成或提交取消或失败 +- **THEN** 系统保持或恢复源图及目标绑定,释放本次快照和临时数据 + +#### Scenario: The user undoes and redoes +- **WHEN** 用户撤销并重做一次成功的 HDR 去背景 +- **THEN** 原始和处理后的浮点内容及绑定正确恢复,重做无需重新推理 + +### Requirement: Existing media behavior + +系统 SHALL 保留普通图片、视频和普通序列的现有去背景行为,所有 BEN2 消费者 SHALL 适配统一的数值 alpha 核心。 + +#### Scenario: An existing regular image or animation is processed +- **WHEN** 用户提交现有支持的普通图片或动画 +- **THEN** 系统按既有帧选择、透明度合成和输出规则完成操作 + diff --git a/openspec/specs/image-alpha-presentation/spec.md b/openspec/specs/image-alpha-presentation/spec.md new file mode 100644 index 0000000..f88e5b9 --- /dev/null +++ b/openspec/specs/image-alpha-presentation/spec.md @@ -0,0 +1,98 @@ +# image-alpha-presentation Specification + +## Purpose +TBD - created by archiving change enable-image-alpha-blending-and-premul. Update Purpose after archive. +## Requirements +### Requirement: Image Empty results enable alpha blending + +AnyImage SHALL 为新建的 Reference Image Empty 及成功提交图片编辑结果的 Image Empty 设置 `use_empty_image_alpha = True`,并保留对象原有整体不透明度与深度设置。 + +#### Scenario: An alpha editing operation succeeds + +- **WHEN** Mask、Frame、Rectify 或 Remove Background 成功替换 Image Empty 图片 +- **THEN** 承载结果的 Empty SHALL 开启 Alpha Blending +- **AND** 对象 `color[3]` 与 `empty_image_depth` SHALL 保持原值 + +#### Scenario: A reference image is pasted + +- **WHEN** AnyImage 新建剪贴板 Reference Image Empty +- **THEN** 对象 SHALL 默认开启 Alpha Blending + +#### Scenario: Editing is cancelled or fails + +- **WHEN** 用户取消手势或结果提交失败 +- **THEN** 原 Image 和 Empty Alpha Blending 状态 SHALL 保持或恢复到操作前 + +### Requirement: Edited color images default to PREMUL + +AnyImage SHALL 将本地图片编辑结果及公共图片编辑 Job 加载结果的最终 `Image.alpha_mode` 设置为 `PREMUL`。该默认值 SHALL 在成功交付后生效,SHALL NOT 依赖源图片原模式。 + +#### Scenario: Local alpha editing creates a packed result + +- **WHEN** Mask、Frame 或 Rectify 从 STRAIGHT、CHANNEL_PACKED 或 PREMUL 源创建结果 +- **THEN** 结果 Image SHALL 使用 PREMUL 并保留现有 Pack 规则 + +#### Scenario: A background removal result is loaded + +- **WHEN** Remove Background 或共享图片编辑结果加载入口的 Upscale 返回 RGBA 图片 +- **THEN** 加载结果 SHALL 使用 PREMUL +- **AND** 序列结果 SHALL 保留原有帧映射、播放范围及逐帧像素内容 + +### Requirement: Alpha presentation preserves editable pixel content + +Alpha 呈现默认值 SHALL 保留既有业务编辑算法产出的 RGB 与 Alpha 数值,包括完全透明 RGB,并 SHALL 保证 Pack、保存重载和重复编辑后仍可读取与恢复这些内容。系统 SHALL NOT 为设置 PREMUL 而永久清除透明 RGB 或重复乘入 Alpha。 + +#### Scenario: Mask Add restores hidden color after reload + +- **WHEN** 图片经过 Mask Subtract、结果 Pack、blend 保存重开,再执行 Mask Add +- **THEN** 恢复区域 SHALL 保留原隐藏 RGB,误差仅限既有编码精度 +- **AND** 结果 SHALL 继续使用 PREMUL + +#### Scenario: A PREMUL result is framed again + +- **WHEN** PREMUL 结果以像素对齐映射再次执行 Frame +- **THEN** RGB 和 Alpha SHALL 符合 Frame 图层合成与边界覆盖采样规则 +- **AND** 模式设置 SHALL 不导致额外 Alpha 相乘或颜色衰减 + +#### Scenario: Foreground alpha is restored over a white bottom image + +- **WHEN** 当前 Frame 合成包含透明白色底图及 Alpha 渐变前景,随后恢复结果 Alpha +- **THEN** RGB SHALL 保留前景到白色的连续混合 +- **AND** Alpha SHALL 沿用当前独立合成与编辑公式 + +#### Scenario: Packed pixels and exported PNG are inspected + +- **WHEN** 检查 byte buffer 编辑结果、浮点源派生结果及加载的 PNG 结果 +- **THEN** 操作前后、Pack 和导出重载后的业务 RGB/Alpha SHALL 在约定编码精度内一致 + +### Requirement: Mesh Color images use PREMUL independently of data textures + +AnyImage 为 Plane、Depth Plane 及 Cutout 创建的材质 SHALL 将 Color Image 配置为 PREMUL,并保持独立 Color/Alpha 连线及当前材质透明策略。Depth 和 Normal Image SHALL 保留各自数据通道、Alpha 模式与颜色空间约定。 + +#### Scenario: A local mesh uses copied or cropped color + +- **WHEN** Plane 使用源图片副本或 Cutout 使用裁剪 Color 图片构建材质 +- **THEN** Color Image SHALL 为 PREMUL +- **AND** 构建过程 SHALL 保留既有 Color 像素及颜色空间策略 + +#### Scenario: AI geometry uses generated textures + +- **WHEN** Depth Plane 或 Cutout 加载 AI Color、Depth 和 Normal 结果 +- **THEN** Color Image SHALL 为 PREMUL +- **AND** Depth 与 Normal SHALL 保留原有数据解释方式 + +#### Scenario: Source images have other users + +- **WHEN** 被编辑或转换的源 Image 同时被未参与的对象引用 +- **THEN** 模式设置 SHALL 作用于结果 Image 或独立 Color 副本 +- **AND** 未参与对象引用的源 Image 模式与像素 SHALL 保持不变 + +### Requirement: Defaults are applied at operation boundaries + +AnyImage SHALL 在新建或成功交付范围内应用本策略,SHALL NOT 在注册、加载场景或普通重绘时批量修改已有图片。 + +#### Scenario: An existing scene is opened + +- **WHEN** 用户打开含有已有 Image Empty 和材质的场景 +- **THEN** 未参与 AnyImage 新操作的图片及对象设置 SHALL 保持原样 + diff --git a/openspec/specs/image-depth-symmetry/spec.md b/openspec/specs/image-depth-symmetry/spec.md new file mode 100644 index 0000000..fb7f5bd --- /dev/null +++ b/openspec/specs/image-depth-symmetry/spec.md @@ -0,0 +1,78 @@ +# image-depth-symmetry Specification + +## Purpose +TBD - created by archiving change add-projected-depth-symmetry. Update Purpose after archive. +## Requirements +### Requirement: Unified depth symmetry identity +系统 SHALL 使用菜单名称 `Depth Symmetry`、节点组名称 `O Image Depth Symmetry` 和 Shape 标识 `DEPTH_SYMMETRY`,节点 Mode SHALL 按 `Balloon / Projected` 顺序提供选择。 + +#### Scenario: Load the node asset +- **WHEN** 用户创建 Depth Symmetry 对象 +- **THEN** 系统从节点资产加载 `O Image Depth Symmetry`,Mode 包含 Balloon 和 Projected + +### Requirement: Gesture selects the initial mode +系统 SHALL 为 Lasso 创建结果选择 Balloon,为 Polyline 创建结果选择 Projected,并允许用户随后手动切换 Mode。 + +#### Scenario: Lasso preset +- **WHEN** 用户通过 Lasso 选择区域并创建 Depth Symmetry +- **THEN** Mode 初始值为 Balloon + +#### Scenario: Polyline preset +- **WHEN** 用户通过 Polyline 选择区域并创建 Depth Symmetry +- **THEN** Mode 初始值为 Projected,Depth Cutout 自身的 Polyline → Shell 关系保持不变 + +#### Scenario: Switch the generated object's mode +- **WHEN** 用户修改已创建对象的 Mode +- **THEN** 同一份输入网格和深度数据按所选模式重新求值,不受创建手势锁定 + +### Requirement: Preserve balloon behavior +Balloon 模式 SHALL 保持原 Depth Balloon 的几何与参数行为,在对称平面向原 Cutout 剪影收拢。 + +#### Scenario: Compare the original mode +- **WHEN** 同一输入、深度图和原有参数分别进入旧基准与新节点的 Balloon 模式 +- **THEN** 输出拓扑和位置在浮点容差内一致,包括 Double Sided、Thickness、平滑及 Depth Axis 的行为 + +### Requirement: Projected positive surface +Projected 模式 SHALL 独立读取深度,使用 Reference Depth 和 Depth Direction 调整正面的置换与投射方向。成形坐标中的正面 MUST 保持在 `Z≥0`,越界面 SHALL 被删除。 + +#### Scenario: Adjust both depth controls +- **WHEN** 用户修改 Reference Depth 或 Depth Direction +- **THEN** 正面形体与投射轮廓重新求值,镜像平面保持在成形坐标的 `Z=0` + +#### Scenario: A face crosses the plane +- **WHEN** 正面某个面含有负 Z 顶点 +- **THEN** 该面及失去面支撑的边线、孤点被删除,剩余结果不包含越界正面或外围压平裙边 + +#### Scenario: Everything is outside +- **WHEN** 参数使全部输入面越界 +- **THEN** 输出为空几何,求值正常结束 + +### Requirement: Close the projected symmetric volume with straight walls +Projected 开启 Double Sided 时 SHALL 关于成形坐标 `Z=0` 镜像,并沿 Z 轴直线连接对应外边界,生成封闭网格。正面平滑 SHALL 在镜像补壁之前完成。 + +#### Scenario: Complete a car volume +- **WHEN** 合法正面存在外边界且 Double Sided 打开 +- **THEN** 输出网格没有开放边、异常面邻接或顶点非流形,面绕序一致;每个 `(x,y,z)` 存在镜像对应点 `(x,y,-z)` + +#### Scenario: Check straight walls after smoothing +- **WHEN** 用户开启 Projected 平滑后生成双面网格 +- **THEN** 外边界仍沿 Z 轴直线连接,侧壁法线与 Z 轴垂直,平滑不使侧壁弯曲 + +#### Scenario: Preview only the front +- **WHEN** 用户关闭 Projected 的 Double Sided +- **THEN** 仅输出非负 Z 正面,用于检查投射和裁去区域 + +### Requirement: Direct node construction and shared builders +Projected 分支 MUST 直接使用基础节点与公共构建函数,不得使用 Mesh Boolean、Capture Attribute 或依赖 Depth Plane、Depth Cutout 节点组。插件运行时 SHALL 从资产加载节点组。 + +#### Scenario: Inspect the built asset +- **WHEN** 构建与检查 `O Image Depth Symmetry` +- **THEN** 新模式不存在布尔、Capture Attribute 或上述节点组依赖,Depth Symmetry 源码与资产接口一致 + +### Requirement: Verify performance on equivalent inputs +实现 SHALL 在同一机器、同输入、同平滑和同参数序列下,对比直接构建版本与已验证的布尔原型,分别记录 Reference Depth 和 Depth Direction 更新耗时。 + +#### Scenario: Compare ordinary and dense meshes +- **WHEN** 对普通与加密网格执行预热后的重复求值 +- **THEN** 报告两种更新的中位数、输入规模与测试条件,并确认正式双模式 Projected 分支的求值收益,而非以固定毫秒值作为跨机器测试门槛 + diff --git a/openspec/specs/image-edit-results/spec.md b/openspec/specs/image-edit-results/spec.md new file mode 100644 index 0000000..0750749 --- /dev/null +++ b/openspec/specs/image-edit-results/spec.md @@ -0,0 +1,42 @@ +# image-edit-results Specification + +## Purpose +TBD - created by archiving change fix-cutout-density-and-image-edit-names. Update Purpose after archive. +## Requirements +### Requirement: Direct image edits preserve the complete source name +Remove Background、Upscale、Crop Box、Crop Lasso、Crop Polyline 和 Crop Perspective SHALL 使用源 Image 数据块的完整名称请求结果 Image 名称,包括名称中的 `.png` 等格式文字,并 SHALL NOT 主动添加材质贴图后缀。 + +#### Scenario: Remove Background edits a shared image +- **WHEN** Remove Background 编辑一个仍被其他对象引用的 Image +- **THEN** 新结果以源 Image 的完整名称请求命名,唯一性数字后缀仅由 Blender 自动追加 + +#### Scenario: Upscale edits a shared image +- **WHEN** Upscale 编辑一个仍被其他对象引用的 Image +- **THEN** 新结果保留源 Image 的完整名称且不添加 `_color` + +#### Scenario: Selection Crop edits a shared image +- **WHEN** 任一 Selection Crop 路径编辑一个仍被其他对象引用的 Image,无论是否启用 Refine Selection 或 Keep Original +- **THEN** 新结果保留源 Image 的完整名称且不移除名称中的格式文字 + +#### Scenario: Perspective Crop edits a shared image +- **WHEN** Crop Perspective 编辑一个仍被其他对象引用的 Image,无论是否启用 Keep Original +- **THEN** 新结果保留源 Image 的完整名称且不移除名称中的格式文字 + +### Requirement: Image replacement follows Blender data-block identity +系统 SHALL 在源 Image 只由被编辑对象使用时以结果替换并清理旧数据,使结果最终恢复源 Image 的准确名称;源 Image 仍被共享或被 Keep Original 保留时,系统 SHALL 保留源数据并接受 Blender 自动生成的数字唯一性后缀。 + +#### Scenario: Unshared source is replaced +- **WHEN** 直接图片编辑替换一个没有其他用户且没有 Fake User 的源 Image +- **THEN** 旧 Image 被清理且结果 Image 最终使用源 Image 的准确名称 + +#### Scenario: Shared source is split +- **WHEN** 直接图片编辑替换一个仍有其他用户的源 Image +- **THEN** 其他用户继续引用源 Image,被编辑对象引用新结果,两个数据块的重名冲突由 Blender 自动解决 + +### Requirement: Material texture naming remains separate +系统 SHALL 仅对 Plane、Depth Plane、Cutout Shape 等生成材质贴图的工作流使用 `_color.png`、`_depth.exr`、`_normal.png` 等业务后缀,直接图片编辑 SHALL NOT 复用这些派生贴图名称。 + +#### Scenario: Plane conversion creates material textures +- **WHEN** 图片被转换为需要材质贴图的 Plane 或 Cutout 结果 +- **THEN** 其 Color、Depth 和 Normal 数据继续按对应材质贴图规则命名 + diff --git a/openspec/specs/image-edit-tools/spec.md b/openspec/specs/image-edit-tools/spec.md new file mode 100644 index 0000000..076f873 --- /dev/null +++ b/openspec/specs/image-edit-tools/spec.md @@ -0,0 +1,128 @@ +# image-edit-tools Specification + +## Purpose +TBD - created by archiving change replace-crop-with-mask-and-rectify. Update Purpose after archive. +## Requirements +### Requirement: Image Edit group uses Frame Mask and Rectify +系统 SHALL 将图片编辑工具组依次注册为 **Frame**、**Mask** 和 **Rectify**,并 SHALL 将 Cutout 作为其后的独立工具。系统 MUST NOT 注册旧 Crop Lasso、Crop Polyline 或 Crop Perspective Tool ID,也 MUST NOT 提供旧 ID 的兼容入口。 + +#### Scenario: Image Edit tools are registered +- **WHEN** AnyImage 扩展完成注册 +- **THEN** 3D Viewport 图片编辑工具组按 Frame、Mask、Rectify 顺序显示,Cutout 紧随其后 + +#### Scenario: Legacy Crop tools are absent +- **WHEN** 系统枚举已注册的 AnyImage WorkspaceTool 和 Operator +- **THEN** 不存在 Crop Lasso、Crop Polyline、Crop Perspective 或其旧 ID + +### Requirement: Mask combines Lasso and Brush gestures +Mask SHALL 是一个 WorkSpaceTool 和一个图片 Alpha 编辑 Operator,并 SHALL 提供 `LASSO` 与 `BRUSH` Gesture。两种 Gesture MUST 生成相同的 SelectionMask 产物并进入相同的 Alpha 合成入口。 + +#### Scenario: Lasso edits Alpha +- **WHEN** 用户在 Mask 中选择 Lasso 并完成一个有效闭合手势 +- **THEN** 系统将该 Lasso 栅格化一次并按当前 Mode 编辑 active Image Empty 的 Alpha + +#### Scenario: Brush edits Alpha +- **WHEN** 用户在 Mask 中选择 Brush 并完成一次点击或拖动 +- **THEN** 系统将连续圆形笔画栅格化一次并按当前 Mode 编辑 active Image Empty 的 Alpha + +### Requirement: Mask provides three Alpha modes +Mask SHALL 提供 `SET`、`ADD`、`SUBTRACT` 三种互斥 Mode,并 SHALL 在 Tool Settings 中使用 Set、Add、Subtract 图标展开显示。设当前 Alpha 为 `A`、本次 SelectionMask 权重为 `M`,系统 MUST 分别计算 `A × M`、`max(A, M)`、`A × (1 - M)`,同时保持 RGB 不变。 + +#### Scenario: Set keeps only current content inside the gesture +- **WHEN** 用户以 Set 应用一次 Mask 手势 +- **THEN** 手势外 Alpha 变为零,手势内 Alpha 等于当前 Alpha 与 SelectionMask 权重的乘积 + +#### Scenario: Add extends current Alpha +- **WHEN** 用户以 Add 应用一次 Mask 手势 +- **THEN** 每个像素 Alpha 等于当前 Alpha 与 SelectionMask 权重的较大值 + +#### Scenario: Subtract removes current Alpha +- **WHEN** 用户以 Subtract 应用一次 Mask 手势 +- **THEN** 每个像素 Alpha 等于当前 Alpha 与 SelectionMask 补集的乘积 + +#### Scenario: Fractional edge coverage is preserved +- **WHEN** SelectionMask 边缘包含 `0` 与 `1` 之间的权重 +- **THEN** 系统使用该权重执行对应公式而不是先转为布尔值 + +### Requirement: Mask settings persist per scene +系统 SHALL 在 Scene 中保存 Mask 的 Gesture、Mode 和 Radius。Radius SHALL 是正整数 Region 屏幕像素,默认 25 px,并 MUST 仅在 Gesture 为 Brush 时显示和生效;每次手势开始后 MUST 冻结本次使用的设置。 + +#### Scenario: Mask settings are reused +- **WHEN** 用户设置 Brush、Add 和一个 Radius 后完成一次操作并再次使用 Mask +- **THEN** Tool Settings 保持 Brush、Add 和该 Radius + +#### Scenario: Lasso hides Radius +- **WHEN** Mask Gesture 为 Lasso +- **THEN** Tool Settings 不显示 Radius 且 Lasso 栅格化不读取 Radius + +### Requirement: Brush produces continuous screen-space coverage +Brush SHALL 以当前 Region 中的屏幕像素显示 Radius 圆环,并 MUST 让单击产生圆形覆盖、拖动产生带圆角端点的连续覆盖。快速移动时相邻事件点之间 MUST NOT 留下未覆盖间隙,图片范围外的笔画 MUST 被裁到图片画布。 + +#### Scenario: Single click stamps a circle +- **WHEN** 用户以 Brush 在图片上单击而不拖动 +- **THEN** 系统以点击位置和当前 Radius 生成一个圆形 SelectionMask + +#### Scenario: Sparse motion events remain connected +- **WHEN** 一次 Brush 拖动的相邻采样点距离大于 Radius +- **THEN** 两点之间仍由连续笔画覆盖 + +#### Scenario: Viewport scale does not change displayed Radius +- **WHEN** 用户改变 Viewport 缩放后以同一 Radius 使用 Brush +- **THEN** 笔刷圆环在 Region 中继续使用相同屏幕像素半径 + +### Requirement: Mask preserves the rectangular image canvas +Mask SHALL 创建与当前图片相同宽高的 packed Image,并 SHALL 保持 active Image Empty 的对象身份、matrix、display size 和 image offset。Mask MUST NOT 按 Alpha 紧裁切或传入 placement bounds,且 SHALL 允许产生全透明结果。 + +#### Scenario: Lasso keeps the canvas +- **WHEN** Lasso 手势只覆盖图片的一小部分 +- **THEN** 结果图片尺寸和 Image Empty 矩形画框与操作前完全相同 + +#### Scenario: Brush keeps the canvas +- **WHEN** Brush 手势只覆盖图片的一小部分 +- **THEN** 结果图片尺寸和 Image Empty 矩形画框与操作前完全相同 + +#### Scenario: Entire Alpha can be removed +- **WHEN** Subtract 覆盖当前全部非零 Alpha +- **THEN** 操作成功并产生相同尺寸的全透明结果 + +#### Scenario: Shared source remains unchanged +- **WHEN** active Image Empty 与其他对象共享同一个源 Image +- **THEN** active 对象改为引用新的编辑结果,其他对象继续引用未修改的源 Image + +### Requirement: Mask and Rectify replace the active image +Mask 与 Rectify SHALL 直接替换 active Image Empty 承载的 Image,并 MUST NOT 提供 Keep Original 属性、Operator 参数或复制结果对象的路径。Frame SHALL 继续让 active Image Empty 承载合成结果。 + +#### Scenario: Mask replaces the active result +- **WHEN** 用户完成一次 Mask 操作 +- **THEN** active Image Empty 本身承载结果且系统不创建其副本 + +#### Scenario: Rectify replaces the active result +- **WHEN** 用户完成一次 Rectify 操作 +- **THEN** active Image Empty 本身承载结果且系统不创建其副本 + +### Requirement: Rectify performs four-point perspective correction +Rectify SHALL 接受四个图片点,规范化为有效凸 Quad,并通过 Homography 生成目标宽高比的矩形图片。系统 SHALL 保留现有预览、输出分辨率限制、可见 Alpha trim 和结果 placement 语义,同时 SHALL 在类名、Tool/Operator ID、标签、状态、warning 与 Undo 中统一使用 Rectify 术语。 + +#### Scenario: Four points create a rectified image +- **WHEN** 用户为 still Image Empty 提交四个构成有效凸 Quad 的点 +- **THEN** 系统生成透视校正后的矩形图片并替换 active Image Empty 的 Image + +#### Scenario: Rectify retains output limits +- **WHEN** 四点区域对应的原始输出超过长边或总像素限制 +- **THEN** 系统按既有 Rectify 输出限制等比约束结果 + +### Requirement: Refine Selection is absent from Mask and Cutout +Mask 与 Cutout MUST NOT 声明、显示、保存或传递 Refine Selection。Server MUST NOT 注册 `refine-image-selection` Job,Cutout Job MUST NOT 接收或执行 `refine_selection` 与 `refine_model`;BEN2 Remove Background 与 Debug 能力 SHALL 保持可用。 + +#### Scenario: Mask has no AI refinement +- **WHEN** 用户查看 Mask Tool Settings 或执行 Mask +- **THEN** 不存在 Refine Selection 选项、AI readiness 门控或 BEN2 Job + +#### Scenario: Cutout runs without refinement parameters +- **WHEN** 用户创建本地、Depth 或 Normal Cutout +- **THEN** Blender 和 Server 请求中不包含 Refine Selection 参数,最终 Shape 仍由本地几何 SelectionMask 约束 + +#### Scenario: BEN2 remains available elsewhere +- **WHEN** 用户运行 Remove Background 或 BEN2 Debug +- **THEN** 系统继续使用现有 BEN2 模型与缓存完成请求 + diff --git a/openspec/specs/image-layer-alpha-adjustment/spec.md b/openspec/specs/image-layer-alpha-adjustment/spec.md new file mode 100644 index 0000000..6ae14c5 --- /dev/null +++ b/openspec/specs/image-layer-alpha-adjustment/spec.md @@ -0,0 +1,60 @@ +# image-layer-alpha-adjustment Specification + +## Purpose +TBD - created by archiving change add-image-layer-alpha-adjustment. Update Purpose after archive. +## Requirements +### Requirement: Image layer exposes alpha adjustment strength + +O Image Layer SHALL 在默认折叠的 `Options` 面板中依次提供 `Alpha Fix`、`Normal Scale`、`Object Space`。Alpha Fix SHALL 为浮点输入,范围 0–1,subtype 为 FACTOR,默认 0,并以该值控制内部 Alpha Float Curve 的 Factor。 + +#### Scenario: Options are initially collapsed +- **WHEN** 创建 O Image Layer 节点实例 +- **THEN** Options 面板 SHALL 默认折叠,包含 Alpha Fix、Normal Scale、Object Space +- **AND** Color、Alpha、Normal、Bump Scale SHALL 位于面板外 + +#### Scenario: Adjustment is disabled +- **WHEN** Alpha Fix 为 0 +- **THEN** 组输出 Alpha SHALL 等于输入 Alpha + +#### Scenario: Adjustment is fully applied +- **WHEN** Alpha Fix 为 1 +- **THEN** 组输出 Alpha SHALL 等于内部 Float Curve 对输入 Alpha 的求值 + +#### Scenario: Adjustment is partially applied +- **WHEN** Alpha Fix 为 0.5 +- **THEN** 输出 SHALL 等于原始 Alpha 与完整曲线结果的平均值 + +### Requirement: Alpha curve follows the reference profile + +Alpha 曲线 SHALL 使用四个点:起点 `(0, 0)`、低端控制点 `(0.8, 0)`、靠近右上方的平滑控制点和终点 `(1, 1)`,形成参考图中的快速上升轮廓。对输入范围 0–1,完整调整后的输出 SHALL 保持在 0–1,保持单调不减,并保持完全透明与完全不透明端值。 + +#### Scenario: Low alpha is suppressed +- **WHEN** 输入 Alpha 在 0–0.8 且调整强度为 1 +- **THEN** 输出 Alpha SHALL 为 0,允许浮点求值误差 + +#### Scenario: Opaque alpha is preserved +- **WHEN** 输入 Alpha 为 1 +- **THEN** 任意调整强度下输出 Alpha SHALL 为 1 + +### Requirement: Material creation initializes adjustment from preferences + +创建使用 O Image Layer 的材质时,系统 SHALL 通过现有偏好读取入口,将 Alpha Fix 初始化为 `Adapt to Scene View Transform` 开启时的 1、关闭时的 0。 + +#### Scenario: Adaptation is enabled +- **WHEN** 开启适配后创建普通着色或 Shadeless 材质 +- **THEN** O Image Layer 实例的 Alpha Fix SHALL 为 1 +- **AND** 对应着色器 SHALL 使用该组调整后的 Alpha 输出 + +#### Scenario: Adaptation is disabled +- **WHEN** 关闭适配后创建材质 +- **THEN** O Image Layer 实例的 Alpha Fix SHALL 为 0 + +#### Scenario: Color space falls back +- **WHEN** 适配开启且颜色空间解析回退到 sRGB +- **THEN** 新建 O Image Layer 实例的 Alpha Fix SHALL 仍为 1 + +#### Scenario: Existing material is manually adjusted +- **WHEN** 用户修改现有材质的 Alpha Fix 后切换偏好 +- **THEN** 已有实例的调整值 SHALL 保留 +- **AND** 后续新建材质 SHALL 使用当前偏好初始化 + diff --git a/openspec/specs/image-pixel-read-integrity/spec.md b/openspec/specs/image-pixel-read-integrity/spec.md new file mode 100644 index 0000000..b3fc9d4 --- /dev/null +++ b/openspec/specs/image-pixel-read-integrity/spec.md @@ -0,0 +1,37 @@ +# image-pixel-read-integrity Specification + +## Purpose +TBD - created by archiving change repair-image-edit-pipelines. Update Purpose after archive. +## Requirements +### Requirement: Reading business pixels preserves source state +系统 SHALL 在读取业务 RGBA 时保留源 Image 的像素缓存、Alpha 模式、脏状态、Packed 内容和对象引用。读取 SHALL NOT 通过重载源图片覆盖当前有效内容。 + +#### Scenario: Dirty packed image is read +- **WHEN** 用户修改 Packed Image 像素但尚未重新 Pack,图片编辑工具读取该图片 +- **THEN** 读取结果包含最近修改的 RGB/Alpha +- **AND** 源像素、脏状态及原 Packed 内容保持读取前状态 + +#### Scenario: Shared source is read before an edit is cancelled +- **WHEN** 两个对象共享同一 PREMUL Image,其中一个工具读取图片后取消或报错 +- **THEN** 两个对象仍引用相同源数据,源像素缓存与呈现设置保持不变 + +### Requirement: Business pixel interpretation survives persistence +系统 SHALL 保留既有 PREMUL 结果默认值,并在即时读取、Pack 和 blend 保存重开后取得相同业务 RGB/Alpha。误差 SHALL 仅来自既有编码精度,完全透明 RGB SHALL 可以通过 Mask Add 恢复。 + +#### Scenario: Edited image is reopened and read repeatedly +- **WHEN** 含透明及半透明像素的编辑结果保存重开并被多次读取 +- **THEN** 每次返回相同业务像素,结果 Alpha 模式保持 PREMUL +- **AND** 读取不会改变源缓存或产生累计颜色变化 + +#### Scenario: Floating point source is consumed +- **WHEN** 工具读取生成或加载的浮点图片,包括原生预乘 Alpha 数据 +- **THEN** 返回值符合源编码的业务 RGBA 语义并保留当前有效精度 +- **AND** 源图片的像素及模式保持不变 + +### Requirement: Temporary decoding data has bounded lifetime +系统 SHALL 在单次业务读取结束或失败时释放该读取拥有的临时图片数据。 + +#### Scenario: Repeated read or decode failure +- **WHEN** 用户重复触发读取,或读取中的解码发生错误 +- **THEN** 不留下额外 Image 数据块,原图片及共享引用保持不变 + diff --git a/openspec/specs/image-result-materials/spec.md b/openspec/specs/image-result-materials/spec.md new file mode 100644 index 0000000..11ff490 --- /dev/null +++ b/openspec/specs/image-result-materials/spec.md @@ -0,0 +1,20 @@ +# image-result-materials Specification + +## Purpose +TBD - created by archiving change refine-image-output-semantics. Update Purpose after archive. +## Requirements +### Requirement: Image color drives generated material IOR Level +系统 SHALL 将 Plane、Depth Plane 与 Cutout 的非 Shadeless 生成材质中 `O Image Layer` 的 Color 输出直接连接到 Principled BSDF 的 IOR Level,同时保留既有 IOR、Base Color、Roughness、Alpha、Normal 与 Displacement 连接。 + +#### Scenario: Dark image content suppresses highlights +- **WHEN** 生成材质采样到黑色或暗色图片内容 +- **THEN** Principled BSDF 的 IOR Level SHALL 接收对应的低颜色值,从而降低该区域的高亮 + +#### Scenario: Bright image content preserves direct color response +- **WHEN** 生成材质采样到白色或亮色图片内容 +- **THEN** Principled BSDF 的 IOR Level SHALL 接收 `O Image Layer` Color 的直接转换值,不经过额外缩放或固定上限 + +#### Scenario: Generated material keeps its IOR value +- **WHEN** Cutout 或 Depth Plane 以 IOR `1.2` 创建材质 +- **THEN** 系统 SHALL 保留 IOR `1.2`,并由 Color 独立驱动 IOR Level + diff --git a/openspec/specs/image-selection-processing/spec.md b/openspec/specs/image-selection-processing/spec.md new file mode 100644 index 0000000..3da4a5b --- /dev/null +++ b/openspec/specs/image-selection-processing/spec.md @@ -0,0 +1,75 @@ +# image-selection-processing Specification + +## Purpose +TBD - created by archiving change process-image-selection-once. Update Purpose after archive. +## Requirements +### Requirement: Image editing prepares one Selection +系统 SHALL 在一次 Image Tool 编辑中只读取一次源图 RGBA 并只将 Selection Path 栅格化为一次 Selection Mask,后续本地编辑或 AI 输入准备 MUST 复用该次处理结果。 + +#### Scenario: User commits a local image edit +- **WHEN** 用户完成 Image Tool 手势且未启用 Refine Selection +- **THEN** 系统使用同一次源图读取和同一个 Selection Mask 完成 Alpha 编辑、裁剪与结果创建 + +#### Scenario: User commits a refined image edit +- **WHEN** 用户完成 Image Tool 手势且启用 Refine Selection +- **THEN** 系统使用同一次源图读取和同一个 Selection Mask 准备 AI 输入,不为输入导出重复读取或栅格化 + +### Requirement: Tools share Selection Path construction +系统 SHALL 让 Image Tool 与 Cutout Tool 复用同一条手势主链构建一次内存中的 Selection Path,用户圈选阶段不得为了工具执行而构建 Selection Mask。 + +#### Scenario: Image Tool gesture completes +- **WHEN** 用户完成任一 Image Tool 手势 +- **THEN** 同一个 Operator 将内存中的 Selection Path 直接交给图片编辑执行阶段,不执行 JSON 编解码 + +#### Scenario: Cutout gesture completes +- **WHEN** 用户完成 Cutout Lasso 或双击全选 +- **THEN** 手势主链将内存中的 Selection Path 交给 Shape Pie Menu,且尚未构建 Selection Mask + +### Requirement: Selection Path serialization is limited to the Cutout operator boundary +系统 SHALL 仅在 Shape Pie Menu 向 `CutoutSelectionToShape` 传递任意长度 Selection Path 时,将路径编码为一次可由 Blender Operator Property 保存的 JSON 值。 + +#### Scenario: Cutout Shape buttons are built +- **WHEN** Shape Pie Menu 为当前 Selection Path 创建 Shape 按钮 +- **THEN** 菜单将该 Path 编码一次,并把同一个值直接设置给各 `CutoutSelectionToShape` 按钮 + +#### Scenario: Cutout Shape operation starts +- **WHEN** 用户点击 Shape 按钮 +- **THEN** `CutoutSelectionToShape` 从自身 Property 取得并解析 Selection Path,且不依赖令牌、全局状态或 Selection 缓存 + +### Requirement: Cutout defers Selection Mask construction until Shape selection +系统 SHALL 在 Cutout Shape Pie Menu 打开前只构建并传递轻量的 Selection Path,不得读取源图像素、栅格化 Selection Mask 或扫描可见 Alpha。 + +#### Scenario: User finishes a Cutout lasso +- **WHEN** 用户松开 Cutout Lasso 并形成有效路径 +- **THEN** 系统立即打开 Shape Pie Menu,且尚未执行全分辨率 Selection 像素处理 + +#### Scenario: User double-clicks a Cutout image +- **WHEN** 用户双击 Image Empty 以选择完整图片 +- **THEN** 系统以完整图片的 Selection Path 打开 Shape Pie Menu,且尚未读取或扫描完整图片像素 + +#### Scenario: User abandons the Shape menu +- **WHEN** 用户关闭 Shape Pie Menu 而未选择 Shape +- **THEN** 系统不读取源图像素、不构建 Selection Mask,也不执行可见 Alpha 扫描 + +### Requirement: Selected Cutout Shape consumes one Selection +系统 SHALL 让 `CutoutSelectionToShape` 直接接收 Selection Path 的序列化值,并在用户选定 Cutout Shape 后只将其转换为一次可供本地或 AI 主链复用的 Selection Mask。 + +#### Scenario: User chooses a local Cutout Shape +- **WHEN** 用户选择不需要 AI 的 Surface 或 Balloon +- **THEN** 系统只读取一次源图 RGBA、栅格化一次 Selection Mask,并复用结果完成验证、颜色图和几何创建 + +#### Scenario: User chooses an AI Cutout Shape +- **WHEN** 用户选择需要 Refine Selection、Depth 或 Normal Map 的 Shape +- **THEN** 系统只读取一次源图 RGBA、栅格化一次 Selection Mask,并复用结果准备持久化的选区输入供后续 Job 阶段消费 + +#### Scenario: Selected Cutout contains no visible pixels +- **WHEN** 用户选定 Shape 后,Selection 与源图可见 Alpha 没有交集 +- **THEN** 系统在该 Shape 操作中报告空 Selection,且不启动 AI Job 或创建 Cutout 对象 + +### Requirement: Selection optimization preserves results +系统 MUST 保持现有 Image Tool 输出边界、Cutout Shape 候选项、AI 参数和最终图像及几何语义不变。 + +#### Scenario: Same Selection is processed after optimization +- **WHEN** 相同源图、Selection Path 和工具设置在优化前后提交 +- **THEN** 系统产生等价的 Selection 边界、Alpha 结果、AI 输入与 Cutout 几何 + diff --git a/openspec/specs/image-selection/spec.md b/openspec/specs/image-selection/spec.md new file mode 100644 index 0000000..b76013f --- /dev/null +++ b/openspec/specs/image-selection/spec.md @@ -0,0 +1,37 @@ +# image-selection Specification + +## Purpose +TBD - created by archiving change unify-selection-terminology. Update Purpose after archive. +## Requirements +### Requirement: Image selection uses one business term +系统 SHALL 在 Image Tool、Cutout Tool 及其对外 Job 协议中使用 Selection 表示用户在图片上圈定并栅格化的范围。 + +#### Scenario: Selection passes through a tool workflow +- **WHEN** 用户使用图片手势圈定范围并提交工具操作 +- **THEN** 工具状态、Job 参数和 Job 结果中表示该范围的字段使用 Selection 术语 + +### Requirement: Selection refinement option is named Refine Selection +支持 AI 选区细化的工具 SHALL 将对应布尔选项显示为 **Refine Selection**,且开启后只细化当前 Selection 中的前景内容。 + +#### Scenario: User enables selection refinement +- **WHEN** 用户在 Image Box、Image Lasso 或 Cutout 中开启 **Refine Selection** +- **THEN** 系统使用 AI 将当前 Selection 收紧到其中的前景内容 + +### Requirement: Cutout normal option is named Normal Map +Cutout Tool SHALL 将控制法线贴图生成的布尔选项显示为 **Normal Map**,并保持现有 Normal Map 生成行为。 + +#### Scenario: User enables Normal Map +- **WHEN** 用户在 Cutout 中开启 **Normal Map** 并选择可执行该功能的 Shape +- **THEN** 系统按该 Shape 的既定 Normal Space 生成并应用 Normal Map + +### Requirement: Cutout mesh detail uses named presets +Cutout Tool SHALL 使用 **Mesh Detail** 档位选择控制网格采样密度,并将 Low、Medium、High、Ultra 分别映射为 32、16、8、4 px 的采样间距。 + +#### Scenario: User chooses mesh detail +- **WHEN** 用户选择任一 **Mesh Detail** 档位并创建 Cutout Shape +- **THEN** 系统使用该档位对应的采样间距构建网格,且从 Low 到 Ultra 逐档增加采样密度 + +#### Scenario: User keeps the default mesh detail +- **WHEN** 用户未修改 **Mesh Detail** +- **THEN** 系统使用 Low 对应的 32 px 采样间距 + diff --git a/openspec/specs/image-tool-ai-availability/spec.md b/openspec/specs/image-tool-ai-availability/spec.md new file mode 100644 index 0000000..4e1fcd9 --- /dev/null +++ b/openspec/specs/image-tool-ai-availability/spec.md @@ -0,0 +1,57 @@ +# image-tool-ai-availability Specification + +## Purpose +TBD - created by archiving change gate-cutout-ai-options. Update Purpose after archive. +## Requirements +### Requirement: AI setup entry precedes dependent tool settings +当 AI 环境或所需模型未就绪时,系统 SHALL 在 Cutout、Image Box 和 Image Lasso 的工具设置开头显示 AI 设置按钮,并使用当前 AI 状态对应的设置文案。 + +#### Scenario: Cutout AI setup entry is first +- **WHEN** AI 未就绪且用户激活 Cutout 工具 +- **THEN** 工具设置 SHALL 先显示 AI 设置按钮,再显示 `Fit to Foreground`、`Mesh Sampling Spacing` 和 `Generate Normal Map` + +#### Scenario: Image region tool AI setup entry is first +- **WHEN** AI 未就绪且用户激活 Image Box 或 Image Lasso +- **THEN** 工具设置 SHALL 先显示 AI 设置按钮,再显示 `Fit to Foreground` + +#### Scenario: AI setup entry is omitted when ready +- **WHEN** AI 环境和所需模型均已就绪 +- **THEN** Cutout、Image Box 和 Image Lasso 的工具设置 SHALL 不显示 AI 设置按钮 + +### Requirement: AI-dependent settings follow AI availability +系统 SHALL 仅在 AI 环境和所需模型均已就绪时允许启用图像区域工具的 AI 依赖设置。 + +#### Scenario: Dependent settings are disabled without AI +- **WHEN** AI 未就绪 +- **THEN** Cutout 的 `Fit to Foreground` 和 `Generate Normal Map` SHALL 不可编辑 +- **THEN** Image Box 和 Image Lasso 的 `Fit to Foreground` SHALL 不可编辑 +- **THEN** Cutout 的 `Mesh Sampling Spacing` SHALL 保持可编辑 + +#### Scenario: Dependent settings are enabled with AI +- **WHEN** AI 环境和所需模型均已就绪 +- **THEN** Cutout 的 `Fit to Foreground` 和 `Generate Normal Map` SHALL 可编辑 +- **THEN** Image Box 和 Image Lasso 的 `Fit to Foreground` SHALL 可编辑 + +### Requirement: Cutout shape choices follow AI availability +系统 SHALL 根据 AI 可用性限制 Cutout shape pie 中的可选 shape,同时保留四种 shape 的底层实现。 + +#### Scenario: Two local shapes without AI +- **WHEN** AI 未就绪且用户完成 Cutout 区域选择 +- **THEN** shape pie SHALL 只显示 `Surface` 和 `Balloon` + +#### Scenario: Four shapes with AI +- **WHEN** AI 环境和所需模型均已就绪且用户完成 Cutout 区域选择 +- **THEN** shape pie SHALL 显示 `Surface`、`Balloon`、`Depth Balloon` 和 `Depth Surface` + +#### Scenario: Runtime RNA menu shape assignment +- **WHEN** a displayed Cutout shape is selected on a Blender version that exposes modifier inputs through runtime RNA +- **THEN** the system SHALL assign the matching string enum identifier and create the shape successfully + +### Requirement: Stale AI settings do not trigger generation +系统 MUST 在区域交互读取设置时忽略 AI 未就绪状态下遗留为真的 AI 依赖选项。 + +#### Scenario: Local Cutout remains local with stale settings +- **WHEN** AI 未就绪、`Fit to Foreground` 或 `Generate Normal Map` 的保存值为真,且用户选择 `Surface` 或 `Balloon` +- **THEN** Cutout operator SHALL 接收关闭的 AI 依赖选项 +- **THEN** 操作 SHALL 不因这些遗留值启动 AI job 或打开 AI 设置流程 + diff --git a/openspec/specs/image-tool-interaction/spec.md b/openspec/specs/image-tool-interaction/spec.md new file mode 100644 index 0000000..e333c46 --- /dev/null +++ b/openspec/specs/image-tool-interaction/spec.md @@ -0,0 +1,64 @@ +# image-tool-interaction Specification + +## Purpose +TBD - created by archiving change preserve-image-tool-source-on-empty. Update Purpose after archive. +## Requirements +### Requirement: Image tools preserve Blender object picking +Mask、Rectify 与 Cutout SHALL 使用 Blender 原生 Viewport Picker 决定点击命中的对象和 active object。系统 MUST NOT 按对象类型过滤原生命中结果,也 MUST NOT 在命中非 Image Empty 后恢复先前的图片选择。 + +#### Scenario: Another Image Empty is clicked +- **WHEN** 一个 Image Empty 为 active,用户使用 Mask、Rectify 或 Cutout 点击另一可拾取的 Image Empty +- **THEN** Blender 原生选择 SHALL 激活被点击的 Image Empty,且本次事件不得启动图片操作 + +#### Scenario: A non-image object is clicked +- **WHEN** 一个 Image Empty 为 active,用户使用 Mask、Rectify 或 Cutout 点击可拾取的 Mesh、Light 或其他非 Image Empty +- **THEN** 系统 SHALL 保留 Blender 对该对象的原生选择结果,且本次事件不得启动图片操作 + +### Requirement: Drawing image tools preserve the active source on an empty hit +当已选择的 active object 是 Image Empty 时,Mask、Rectify 与 Cutout MUST 在原生 Picker 没有命中对象时保留该 active source,并 MUST NOT 因 `deselect_all` 清空选择。透明图片像素与 Viewport 空白 SHALL 使用相同的空命中语义。 + +#### Scenario: Mask starts on a transparent or empty location +- **WHEN** 已选择的 active Image Empty 存在,用户以 Mask 在没有原生对象命中的位置按下鼠标 +- **THEN** 系统 SHALL 保持该 Image Empty active 并以该位置开始当前 Mask Gesture + +#### Scenario: Rectify starts on a transparent or empty location +- **WHEN** 已选择的 active Image Empty 存在,用户以 Rectify 在没有原生对象命中的位置按下鼠标 +- **THEN** 系统 SHALL 保持该 Image Empty active 并把该位置作为第一个 Rectify 点 + +#### Scenario: Cutout lasso starts outside pickable image content +- **WHEN** 已选择的 active Image Empty 存在,用户以 Cutout 从没有原生对象命中的位置开始拖动 +- **THEN** 系统 SHALL 保持该 Image Empty active 并开始 Cutout Lasso + +#### Scenario: Cutout full image starts from an empty location +- **WHEN** 已选择的 active Image Empty 存在,用户以 Cutout 在没有原生对象命中的位置双击 +- **THEN** 系统 SHALL 保持该 Image Empty active 并打开整图 Cutout Shape 选择 + +#### Scenario: Cutout empty click keeps the source +- **WHEN** 已选择的 active Image Empty 存在,用户以 Cutout 单击没有原生对象命中的位置且没有形成拖动或双击 +- **THEN** 系统 SHALL 保持当前选择且不启动 Cutout Shape 选择 + +### Requirement: Image operation eligibility uses the active selected Image Empty +Mask、Rectify 与 Cutout SHALL 仅在当前 active object 是已选择的 Image Empty 时开始图片操作。系统 MUST NOT 在工具激活时保存隐藏 source,也 MUST NOT 使用先前 active 的图片绕过当前选择。 + +#### Scenario: Active object is not an Image Empty +- **WHEN** 当前 active object 是非 Image Empty 或没有已选择的 active object,用户尝试开始 Mask、Rectify 或 Cutout +- **THEN** 系统 SHALL 取消本次图片操作并提示选择 active Image Empty + +### Requirement: Gesture validity is decided after collection +Mask、Rectify 与 Cutout MUST NOT 根据起始点是否位于图片画布或是否命中非零 Alpha 决定能否开始手势。系统 SHALL 在手势提交阶段使用各工具现有的图片相交、可见内容和有效几何规则决定是否执行。 + +#### Scenario: Gesture starts outside and later overlaps the image +- **WHEN** Mask、Rectify 或 Cutout 的起始点位于图片外,但完成的手势与图片有效相交 +- **THEN** 系统 SHALL 按完整手势执行对应图片操作 + +#### Scenario: Completed gesture remains outside the image +- **WHEN** 完成的 Mask、Rectify 或 Cutout 手势与图片没有有效相交 +- **THEN** 系统 SHALL 在提交阶段取消操作并报告对应 warning + +### Requirement: Frame keeps its existing empty-click selection behavior +Frame SHALL 保持 Blender 风格的空白点击取消选择行为,不得因 Mask、Rectify 与 Cutout 的空命中策略而改变。 + +#### Scenario: Frame clicks an empty viewport location +- **WHEN** 用户在 Frame 激活时普通点击没有原生对象命中的 Viewport 位置 +- **THEN** Frame 的原生选择 keymap SHALL 继续请求 deselect all + diff --git a/openspec/specs/joint-geometry-node-layout/spec.md b/openspec/specs/joint-geometry-node-layout/spec.md new file mode 100644 index 0000000..ea198ca --- /dev/null +++ b/openspec/specs/joint-geometry-node-layout/spec.md @@ -0,0 +1,49 @@ +# joint-geometry-node-layout Specification + +## Purpose +TBD - created by archiving change joint-geometry-node-layout. Update Purpose after archive. +## Requirements +### Requirement: 按消费者形成计算块 + +排列器 SHALL 根据最近的几何消费者集合及字段连通关系划分计算块,并为不同计算块或直接消费节点提供局部接口读取。 + +#### Scenario: 一组字段服务于相同几何计算 +- **WHEN** 相连字段节点具有相同的最近几何消费者集合 +- **THEN** 它们作为一个计算块参与排列,复杂计算块用 Frame 表达归属 + +#### Scenario: 接口值被多个计算区域使用 +- **WHEN** 同一接口输出被多个计算区域消费 +- **THEN** 各区域通过局部 Group Input 读取相同接口值,接口定义与默认值保持一致 + +### Requirement: 联合规划计算块与连接通道 + +排列器 SHALL 在创建最终位置与 Reroute 前,将计算块及共享长连接通道纳入整体空间约束,保留横向计算层次及可区分的块间距。 + +#### Scenario: 支线之间存在跨列共享连接 +- **WHEN** 某个输出跨越多个列连接到其他计算块 +- **THEN** 其长连接通道与计算块共同参与空间规划,并根据规划结果创建共享中继 + +#### Scenario: 几何主线与大型支线共同排列 +- **WHEN** 几何处理节点消费大型字段计算块 +- **THEN** 两者根据依赖和连接位置共同确定纵向关系,真实节点矩形互不重叠 + +### Requirement: 布局保持节点行为 + +排列器 SHALL 保持运算设置、输入默认值、接口、成对区域关系以及去除 Reroute 后的实际连接和多输入顺序;排列前后的代表性几何求值 SHALL 一致。 + +#### Scenario: 对几何资产执行排列 +- **WHEN** 保存排列前后的程序结构及代表性模式的求值结果 +- **THEN** 程序结构一致,顶点坐标在测试容差内相等,面连接完全相同 + +#### Scenario: 再次排列已排列节点组 +- **WHEN** 对同一节点组再次执行排列 +- **THEN** 实际连接和接口保持一致,局部 Group Input 数量保持稳定 + +### Requirement: 统一更新全部几何资产 + +构建工具 SHALL 使用同一排列入口处理资产清单中的全部 GeometryNodeTree,并保存、重新加载及验证更新后的资产。 + +#### Scenario: 构建节点资产库 +- **WHEN** 执行节点资产构建 +- **THEN** Image Plane、Image Depth Plane、Image Relief Plane、Image Cutout、Image Depth Cutout、Image Depth Balloon、Image Depth Panorama 共 7 个几何节点组均使用联合规划布局,并通过保存后的资产检查 + diff --git a/openspec/specs/maintainable-project-structure/spec.md b/openspec/specs/maintainable-project-structure/spec.md new file mode 100644 index 0000000..7043091 --- /dev/null +++ b/openspec/specs/maintainable-project-structure/spec.md @@ -0,0 +1,41 @@ +# maintainable-project-structure Specification + +## Purpose +TBD - created by archiving change remove-debug-workflows. Update Purpose after archive. +## Requirements +### Requirement: Tests protect meaningful behavior + +维护者 MUST 审查全部测试文件、用例和辅助代码,按实际回归风险保留、合并、重写或删除。保留的测试 SHALL 验证可观察行为或关键协议,每项独有且仍适用的风险覆盖 MUST 保留。 + +#### Scenario: Audit completed +- **WHEN** 全部测试整理完成 +- **THEN** 失效功能测试和无独立价值的重复测试已删除,保留用例能说明所保护的行为,完整测试通过 + +### Requirement: Test names reflect tested functionality + +测试文件、类和函数 MUST 与其验证的功能、行为和关键条件相符;跨独立职责的测试集合 SHALL 按稳定功能拆分。 + +#### Scenario: Locate a behavior test +- **WHEN** 按测试路径、类名和函数名查找某项功能 +- **THEN** 名称与测试主体的实际断言对应,共用辅助代码拥有明确的复用职责 + +### Requirement: Node asset modules reflect responsibilities + +`tools/node_assets/` 的全部模块 MUST 按实际资产或业务职责审查命名与边界,所有重命名 MUST 同步导入、构建与验证入口及测试路径,并删除旧路径和转发层。 + +#### Scenario: Load reorganized asset tools +- **WHEN** 启动器解析脚本并加载节点构建与验证模块 +- **THEN** 所有路径指向最终模块,文件名与构建对象、几何处理、布局或验证职责对应 + +#### Scenario: Preserve node behavior +- **WHEN** 对重组后的节点构建函数运行针对性测试 +- **THEN** 普通图像平面统一使用 `O Image Plane` 名称,源码使用 `image_plane`;资产重命名前后接口、默认值、运算、连接及布局一致 + +### Requirement: Internal documentation matches implementation + +`docs/internals/` 全部页面 MUST 与最终源码中的功能、职责、参数、调用链和产物对应,页面名称和目录索引 MUST 与主题对应。实现流程 SHALL 使用从上到下的 Mermaid 图。 + +#### Scenario: Read final implementation documentation +- **WHEN** 按总览或 MkDocs 导航访问任一内部主题 +- **THEN** 链接有效,内容描述当前实现,源码路径和功能名称可核对,Debug 专页和过期 Debug 说明已清理 + diff --git a/openspec/specs/mask-cut-depth-sampling/spec.md b/openspec/specs/mask-cut-depth-sampling/spec.md new file mode 100644 index 0000000..5deaf1c --- /dev/null +++ b/openspec/specs/mask-cut-depth-sampling/spec.md @@ -0,0 +1,103 @@ +# mask-cut-depth-sampling Specification + +## Purpose +TBD - created by archiving change unify-mask-cut-depth. Update Purpose after archive. +## Requirements +### Requirement: 自由边界与切边取相邻面的深度采样 + +`O Image Depth Plane` 与 `O Image Depth Panorama` 上由 Depth Mask 删除产生的切口点,以及 `O Image Depth Plane` 原画幅外框上的点,其深度 MUST 取该点相邻保留面的采样值,而不是该点自己的纹理采样。切口点 MUST 保持自身的射线方向,只改变沿射线的深度分量。 + +#### Scenario: 无效侧深度尖刺不影响 Plane 切口 + +- **WHEN** Depth Mask 删除一片区域,且紧邻该区域的无效侧像素深度与有效侧相差一个数量级 +- **THEN** 切口点的深度等于相邻有效面中心的采样深度,切口不出现跟随无效侧像素的尖刺 + +#### Scenario: Panorama 切口跟随有效侧半径 + +- **WHEN** Panorama 的 Depth Mask 删除一片区域,且被删区域的距离值与有效侧相差一个数量级 +- **THEN** 切口圈各点的径向距离等于相邻有效面中心的采样距离 + +#### Scenario: 射线方向保持 + +- **WHEN** 切口点取相邻面深度 +- **THEN** Plane 切口点的相机横纵坐标不变,Panorama 切口点仍落在原经纬方向上 + +#### Scenario: 关闭 Depth Split 时仍然生效 + +- **WHEN** Depth Split 为 0 +- **THEN** 切口点仍取相邻面深度,Depth Split 切边不参与深度替换 + +#### Scenario: 外框与切口同规则 + +- **WHEN** `O Image Depth Plane` 用同一深度图求值,Depth Mask 打开或关闭 +- **THEN** 原画幅外框上的点与外框邻接面的采样深度一致,不保留自身采样 + +### Requirement: 切口规则内建在共享相机采样 + +切口规则 MUST 由 `build_surface_camera` 自身求值自由边界并应用,调用方 MUST NOT 传入「哪些点使用面深」的集合,也 MUST NOT 传入排除字段;Depth Split 切边 MUST 与切口点走同一条深度替换路径。 + +#### Scenario: 三个调用点行为一致 + +- **WHEN** 比较 `O Image Depth Cutout`、`O Image Depth Plane` 与 `O Image Depth Panorama` 的相机采样接线 +- **THEN** 三者都不传入点集合或排除字段,切口点与 Depth Split 切边由同一规则覆盖 + +### Requirement: Depth Mask 按面删除 + +Depth Mask 的删除 MUST 按面判定:面中心的采样低于 Mask Threshold 时删除该面,其余面连同其顶点一起保留。删除后 MUST NOT 再单独清理残留边。 + +#### Scenario: 切口不再多退一圈 + +- **WHEN** Depth Mask 打开,且无效区与有效区之间存在只跨过部分顶点的面 +- **THEN** 该面在面中心仍有效时保留,切口落在面中心轮廓上,而不是所有相邻顶点都有效的内圈 + +#### Scenario: 删除后没有悬空边 + +- **WHEN** Depth Mask 删除整片面 +- **THEN** 输出的边都至少属于一个保留面,没有只由删除残留形成的边 + +### Requirement: Panorama 的无效面落到 Dome Radius + +`O Image Depth Panorama` 的 `Dome Radius` MUST 同时承担两个作用:Depth Scale 为 0 时的球半径,以及 Depth Mask 关闭时被保留的无效面所落到的距离。无效判定 MUST 复用同一次 Mask Threshold 比较,MUST NOT 另行采样或按深度通道判断。输出半径 MUST 为 `Dome Radius + Depth Scale × (d − Dome Radius)`,`d` 为有效点的距离采样。 + +#### Scenario: 关闭 Depth Mask 保留无效区 + +- **WHEN** Depth Mask 关闭,且存在 alpha 低于 Mask Threshold 的面 +- **THEN** 这些面整片落在 Dome Radius,其余面保持自身距离 + +#### Scenario: Depth Scale 为 0 + +- **WHEN** Depth Scale 为 0 +- **THEN** 输出是半径为 Dome Radius 的球面 + +### Requirement: 原画幅外框的平滑保护独立于切口规则 + +`O Image Depth Plane` 的原画幅外框(UV 边距为 0 的点)MUST 继续作为 `smooth_boundary_camera` 与 `smooth_cut_boundary` 的保护范围,边界带平滑 MUST NOT 因此改变切口规则的覆盖范围。 + +#### Scenario: 外框保护不参与深度规则 + +- **WHEN** Boundary Smooth 大于 0 且存在切口 +- **THEN** 原画幅外框保持不被边界带平滑拖入,同时其深度仍取相邻面采样 + +#### Scenario: 关闭 Depth Mask 时几何逐位不变 + +- **WHEN** Depth Mask 关闭 +- **THEN** `O Image Depth Plane` 与 `O Image Depth Panorama` 的输出与改动前逐位一致 + +### Requirement: 切口深度先锚定后过渡 + +切口点的面深替换 MUST 发生在切口带深度模糊与位置平滑之前。切口点的最终几何 MUST 由「面深在切口带内模糊一次,再沿射线重建」得到,带外点的位置与深度 MUST 与改动前一致。 + +#### Scenario: 切口带保留平滑过渡 + +- **WHEN** Boundary Smooth 大于 0 且存在 mask 切口 +- **THEN** 切口带各点沿各自射线过渡到相邻面深,切口带以外的点与 Boundary Smooth 为 0 时逐位一致 + +### Requirement: 无相邻面的边界点不参与面深替换 + +切口规则 MUST 在删除无面边(wire edge)之后求值自由边界。没有相邻保留面的点 MUST NOT 进入面深替换,避免深度塌陷到原点。 + +#### Scenario: 孤立点保持自身深度 + +- **WHEN** 几何中存在没有相邻面的自由点 +- **THEN** 该点保持自身采样深度,输出坐标有限 + diff --git a/openspec/specs/mask-editing/spec.md b/openspec/specs/mask-editing/spec.md new file mode 100644 index 0000000..123df6f --- /dev/null +++ b/openspec/specs/mask-editing/spec.md @@ -0,0 +1,98 @@ +# mask-editing Specification + +## Purpose +TBD - created by archiving change add-live-mask-preview-and-polyline. Update Purpose after archive. +## Requirements +### Requirement: Mask exposes three gestures with Subtract by default +系统 SHALL 在单一 Mask Tool 中提供 Lasso、Brush 和 Polyline Gesture,并 SHALL 提供 Set、Add、Subtract Mode。新 Scene 的 Mask Mode MUST 默认为 Subtract;已有 Scene 中已保存的 Mode MUST 保持其值。Radius MUST 只在 Brush Gesture 下显示和生效。 + +#### Scenario: New scene uses Subtract +- **WHEN** 新 Scene 尚未保存 Mask Mode +- **THEN** Mask Tool Settings 与新启动的 Mask Operator 使用 Subtract + +#### Scenario: Gesture settings remain unified +- **WHEN** 用户在 Lasso、Brush 与 Polyline 之间切换 +- **THEN** 系统仍使用同一个 Mask Tool、Mode 设置和 Alpha 合成入口,并只在 Brush 下显示 Radius + +### Requirement: Brush defers Image work until release +Brush MUST 在手势完成前保持 active Image Empty 的 Image 不变。拖动期间 MUST NOT 读取源 RGBA、创建临时 Image、栅格化图片级 Mask、写入 Pixel Buffer、调用 `image.update()` 或 Pack;鼠标释放后 SHALL 一次性计算并提交结果。 + +#### Scenario: Dragging leaves the Image untouched +- **WHEN** 用户按住左键移动 Brush 且手势尚未完成 +- **THEN** active Image Empty 继续显示并引用操作开始时的 Image,只通过 Viewport Overlay 呈现待提交范围 + +#### Scenario: Shared source remains unchanged during the gesture +- **WHEN** active Image Empty 与另一个对象共享源 Image 并开始 Brush +- **THEN** 两个对象都继续引用且显示原始 Image,直到 active Image Empty 在释放后接收新结果 + +#### Scenario: Stationary hold performs no Image work +- **WHEN** 用户按住 Brush 且鼠标没有产生有效位移 +- **THEN** 系统不执行 Image 更新或图片级计算 + +### Requirement: Brush completion and cancellation are transactional +Brush 完成时 MUST 创建并 Pack 一个最终结果 Image,并 SHALL 通过公共替换入口让 active Image Empty 承载结果。取消、工具切换或错误 MUST 保持源 Image 不变且不产生完成的编辑;成功完成 MUST 保持对象 identity、matrix、display size、offset、共享源引用和单步 Undo 语义。 + +#### Scenario: Completing a Brush persists one result +- **WHEN** 用户释放左键完成 Brush +- **THEN** 系统计算完整覆盖、创建并 Pack 一个同尺寸结果,active Image Empty 的画框和对象身份保持不变 + +#### Scenario: Cancelling restores the source +- **WHEN** 用户在 Brush 期间按 RMB 或 Esc +- **THEN** active Image Empty 仍引用操作前 Image,且没有临时 Image 或完成的 Undo 编辑 + +#### Scenario: Tool switch cleans the preview +- **WHEN** Brush 运行期间 active WorkspaceTool 发生变化 +- **THEN** 系统只结束 Overlay 与 Modal,Image 保持不变 + +### Requirement: Brush uses one continuous primitive union +Brush MUST 从简化后的屏幕轨迹生成圆形采样点和等宽连接条。Overlay 与释放后的 Selection MUST 使用同一组 Primitive 和 union 语义;急转、折返、自交与重叠区域 MUST 保持连续,MUST NOT 出现超出 Radius 的尖角或未填充三角。图片空间 union MUST 以逐像素最大值累计到单个覆盖画布,不得保存逐 Primitive Mask 列表。 + +#### Scenario: Sparse events leave no gap +- **WHEN** 两个连续 Brush 采样点之间的距离大于 Radius +- **THEN** 两点之间的胶囊完整覆盖连线且不出现空隙 + +#### Scenario: Turning does not create a horn or hole +- **WHEN** Brush 中心轨迹发生方向变化 +- **THEN** 圆形采样点与连接条融合,Overlay 与最终 Selection 都不出现 miter 尖角或未填充三角 + +#### Scenario: Preview and result share the footprint +- **WHEN** 用户释放 Brush +- **THEN** 系统投影并栅格化拖动时显示的同一组 Brush Primitive,并使用相同的 union 规则 + +### Requirement: Brush matches the Lasso and Polyline overlay +Brush SHALL 在每个有效鼠标事件后绘制整次未提交笔画的 footprint。Overlay MUST 复用 Lasso、Polyline 的虚线边界、non-zero winding 三角化和半透明填充;自交区域 MUST 融合为一个填充区域,虚线 MUST 只绘制融合后的外轮廓而不得保留内部交错边。Subtract MUST 使用红色填充,Set 与 Add MUST 使用灰色填充。 + +#### Scenario: Overlay persists until release +- **WHEN** 用户继续拖动 Brush 且尚未释放左键 +- **THEN** Viewport Overlay 以当前 Mode 的填充和虚线边界显示从起点到当前点的准确 footprint,Image 本身保持不变 + +#### Scenario: Crossing strokes merge visually +- **WHEN** Brush footprint 与自身交错或重叠 +- **THEN** 交错部分显示为连续的一整块填充,虚线只围绕融合区域的外边界 + +### Requirement: Polyline edits Alpha through the Mask pipeline +Polyline SHALL 以逐点单击构建闭合 Selection:鼠标移动 MUST 预览下一条边;至少三个点后点击起点 8 px 范围或按 Enter MUST 完成;Backspace MUST 删除最后一个已提交点;RMB 或 Esc MUST 取消。完成的 Polyline MUST 栅格化为一个 SelectionMask,并 MUST 以当前 Set、Add 或 Subtract Mode 修改当前 Alpha,保持 RGB、图片尺寸和 Image Empty 画框不变。 + +#### Scenario: Clicking the start closes Polyline +- **WHEN** 用户已提交至少三个点并在起点 8 px 范围内单击 +- **THEN** 系统闭合路径并应用当前 Mask Mode + +#### Scenario: Enter confirms Polyline +- **WHEN** 用户已提交至少三个点并按 Enter 或 Numpad Enter +- **THEN** 系统闭合路径并应用当前 Mask Mode + +#### Scenario: Backspace removes one point +- **WHEN** Polyline 已有两个以上已提交点且用户按 Backspace +- **THEN** 系统删除最后一点并继续预览未完成路径 + +### Requirement: Image selections carry only positive path geometry +`SelectionPath` SHALL 只保存并序列化 Points。Mask、Cutout、Scene Property、Operator 参数和 Server Job 请求 MUST NOT 声明或传递 Invert、Keep Original、Refine Selection 或 Refine Model 状态;Selection 栅格化 SHALL 只建立带抗锯齿 Padding 的局部 Path Bounds。 + +#### Scenario: Cutout serializes a path +- **WHEN** Cutout 把 Lasso SelectionPath 传给 Shape Operator +- **THEN** JSON 只包含 `points`,恢复后得到相同的正向路径 + +#### Scenario: Public state contains current concepts +- **WHEN** 系统枚举 Scene Property、Mask/Cutout Operator annotation 和 Cutout Job 请求 +- **THEN** 只出现当前 Gesture、Mode、Radius、Shape、Depth 与 Normal 业务字段 + diff --git a/openspec/specs/mask-mode-options/spec.md b/openspec/specs/mask-mode-options/spec.md new file mode 100644 index 0000000..e38a02d --- /dev/null +++ b/openspec/specs/mask-mode-options/spec.md @@ -0,0 +1,29 @@ +# mask-mode-options Specification + +## Purpose +TBD - created by archiving change add-cutout-polyline. Update Purpose after archive. +## Requirements +### Requirement: Mask exposes Set Extend and Subtract + +Mask MUST 按顺序提供 `SET` / Set、`EXTEND` / Extend、`SUBTRACT` / Subtract,枚举数值分别为 0、1、2;界面和状态文本 MUST 使用 Extend。 + +#### Scenario: User extends Alpha +- **WHEN** 用户以 Extend 提交值为 mask 的选区到现有 alpha +- **THEN** 结果 Alpha 为 `max(alpha, mask)`,RGB 保持不变 + +#### Scenario: User uses Set or Subtract +- **WHEN** 用户使用 Set 或 Subtract +- **THEN** 结果 Alpha 分别为 `alpha * mask` 或 `alpha * (1-mask)`,RGB 保持不变 + +### Requirement: Mask defaults to Set + +Scene 的 Mask Mode 和 Mask Operator 的模式属性 MUST 默认 Set,并通过同一属性定义保持一致。 + +#### Scenario: New scene and operator defaults +- **WHEN** 创建新 Scene 或未指定模式的 Mask Operator +- **THEN** 模式默认值为 `SET` + +#### Scenario: User chooses another mode +- **WHEN** 用户主动设置 Extend 或 Subtract 后启动 Mask +- **THEN** 本次操作使用用户保存的模式 + diff --git a/openspec/specs/material-color-images/spec.md b/openspec/specs/material-color-images/spec.md new file mode 100644 index 0000000..26bd101 --- /dev/null +++ b/openspec/specs/material-color-images/spec.md @@ -0,0 +1,82 @@ +# material-color-images Specification + +## Purpose +TBD - created by archiving change unify-material-color-images. Update Purpose after archive. +## Requirements +### Requirement: Material color uses the requested source bounds + +所有材质生成入口 SHALL 按本次准备时的源图像素和范围生成颜色图。Cutout SHALL 使用裁切范围,四种 Convert To 与剪贴板 Plane SHALL 使用整图范围。AI 的参与 SHALL 保持相同颜色来源及颜色分辨率。 + +#### Scenario: Cutout uses or omits AI + +- **WHEN** 同一源图和裁切范围分别执行普通 Cutout 和需要 AI 的 Cutout +- **THEN** 两者的颜色图 SHALL 对应同一裁切内容、尺寸和原有 Alpha,包含透明区域 RGB + +#### Scenario: A full image is converted + +- **WHEN** 执行 Plane、Depth Plane、Relief Plane、Panorama 或剪贴板 Plane +- **THEN** 颜色图 SHALL 对应完整源图片,保持原始尺寸和当前像素 + +### Requirement: Every material owns an isolated color image + +每次生成材质 SHALL 使用独立 Color Image 和独立临时颜色文件路径,以源图基名、`_color` 及实际文件后缀命名并打包。原 Image 的内容和解释设置 SHALL 保持不变,材质 Color SHALL 不携带剪贴板原图缓存身份。 + +#### Scenario: Source users vary + +- **WHEN** 原图没有其他用户,或被其他 Empty、材质或笔刷引用 +- **THEN** 新材质 SHALL 使用独立颜色图,原图的引用、像素、名称、路径、颜色空间及 Alpha 模式 SHALL 保持原值 + +#### Scenario: Original file is imported after material painting + +- **WHEN** 转换后修改材质颜色图,再次按原文件路径导入图片,包括原 Image 已移除的情况 +- **THEN** 导入 SHALL 不命中材质颜色图,不带入该颜色图的材质设置和绘制像素 + +### Requirement: Encoding preserves color precision and presentation + +颜色图 SHALL 保留对应范围的 RGB、Alpha、透明区域 RGB 及源编码精度,float / HDR SHALL 保留浮点范围。普通 byte 材质 SHALL 沿用颜色空间适配与 Alpha 配对规则,所有 float / HDR 材质 SHALL 保留源颜色空间与 Alpha 模式。 + +#### Scenario: Ordinary material is configured + +- **WHEN** 普通 byte 材质适配关闭或回退到 sRGB,或者启用可用的 inverse 空间 +- **THEN** 颜色图 SHALL 分别采用 sRGB + PREMUL 或对应 inverse 空间 + STRAIGHT,原图设置保持不变 + +#### Scenario: HDR and unsaved pixels are serialized + +- **WHEN** float / HDR、generated、dirty 或 packed 源图完成颜色图生成、打包和保存重载 +- **THEN** 结果 SHALL 在对应编码精度内保留业务像素、动态范围与选定的颜色解释 +- **AND** 所有 float / HDR 材质 SHALL 保留源图颜色空间与 Alpha 模式;Panorama 非 sRGB 分支沿用源解释 + +#### Scenario: Float source interpretation prevents lossless serialization + +- **WHEN** 按源颜色空间和 Alpha 模式解码 EXR 后无法保留源浮点像素 +- **THEN** 操作 SHALL 明确拒绝转换,清理临时资源并保持源图不变 +- **AND** float / HDR 材质 SHALL 不启用场景视图适配的 Alpha Fix + +### Requirement: Material operations release temporary resources safely + +临时颜色文件 SHALL 在打包和相关 Job 使用结束后释放。取消或失败 SHALL 清理本次临时文件与未交付数据,保留源对象及原图。成功转换 SHALL 支持 Undo / Redo;异步源对象失效或 Image 被替换时 SHALL 取消应用。 + +#### Scenario: Packed result survives file cleanup + +- **WHEN** 操作成功且临时目录已删除 +- **THEN** 材质颜色图 SHALL 仍能正确读取并保存重载 + +#### Scenario: An operation fails or is cancelled + +- **WHEN** 导出、模型处理、图像加载、材质或对象创建失败,或用户取消 Job +- **THEN** 本次临时资源 SHALL 释放,源对象、源像素和源图设置保持原值 + +#### Scenario: Conversion is undone and redone + +- **WHEN** 用户撤销并重做转换 +- **THEN** 撤销 SHALL 恢复源对象和源图,重做 SHALL 恢复带独立颜色图的转换结果 + +### Requirement: Plane accepts static images only + +Convert to Plane SHALL 仅接受静态图片;Movie、Sequence 和动画图像 SHALL 在创建颜色图或对象前被拒绝,并给出明确错误。 + +#### Scenario: Animated source is selected + +- **WHEN** 源 Image 为 Movie 或 Sequence,即使其声明时长为一帧,或源图片含多帧 +- **THEN** Plane SHALL 报告仅支持静态图片,并保持源对象及图像不变 + diff --git a/openspec/specs/material-view-adaptation/spec.md b/openspec/specs/material-view-adaptation/spec.md new file mode 100644 index 0000000..dbb35fc --- /dev/null +++ b/openspec/specs/material-view-adaptation/spec.md @@ -0,0 +1,112 @@ +# material-view-adaptation Specification + +## Purpose +TBD - created by archiving change configure-material-view-adaptation. Update Purpose after archive. +## Requirements +### Requirement: Preferences expose material view adaptation + +AnyImage SHALL 在 Preferences 的 Shading 分组提供 `Adapt to Scene View Transform` 布尔选项,默认关闭。扩展设置及读取入口 SHALL 位于 `preferences.py`,并通过 `addon_preferences()` 读取。 + +#### Scenario: Preferences use the default + +- **WHEN** 用户尚未设置该选项或读取不到扩展偏好 +- **THEN** 材质颜色贴图 SHALL 使用关闭适配的行为 + +### Requirement: Material color space determines alpha presentation + +开启适配时,AnyImage SHALL 按当前场景 View Transform 选择既有映射中的 inverse 颜色空间,并使用 STRAIGHT。关闭适配时 SHALL 使用 sRGB + PREMUL。实际回退为 sRGB 时 SHALL 使用 PREMUL,并保持 Preferences 中的适配开关原值。 + +#### Scenario: A supported view is adapted + +- **WHEN** 适配开启,当前场景为 AgX,且 `AgX Base sRGB` 可用 +- **THEN** 材质 Color Image SHALL 使用 `AgX Base sRGB` + STRAIGHT + +#### Scenario: Other mapped views are adapted + +- **WHEN** 适配开启,场景使用 ACES 1.3、ACES 2.0、Filmic 或 Khronos PBR Neutral,且对应空间可用 +- **THEN** Color Image SHALL 分别使用 `ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB` 或 `Khronos PBR Neutral sRGB` +- **AND** Alpha 模式 SHALL 为 STRAIGHT + +#### Scenario: Adaptation is disabled + +- **WHEN** 适配关闭,创建任意受支持入口的材质 +- **THEN** Color Image SHALL 使用 sRGB + PREMUL + +#### Scenario: The scene resolves to sRGB + +- **WHEN** 适配开启,但场景为 Standard、视图未匹配、场景不可用或 inverse 空间不被支持 +- **THEN** Color Image SHALL 使用 sRGB + PREMUL + +#### Scenario: The fallback color space is unavailable + +- **WHEN** 所需颜色空间及 sRGB 均无法应用 +- **THEN** 配置 SHALL 明确报错并保留贴图之前的有效配置 +- **AND** Preferences 中的适配开关 SHALL 保持原值 + +### Requirement: Material entry points apply the same policy + +AnyImage SHALL 在 Plane、Depth Plane、Cutout 和剪贴板 Mesh 材质创建时统一配置 Color Image。最终模式 SHALL 在后续绑定及交付步骤中保留。Depth 与 Normal SHALL 保留各自的数据解释约定。 + +#### Scenario: A material is created from an edited color result + +- **WHEN** 一个 PREMUL 编辑结果进入开启适配的 inverse 材质 +- **THEN** 最终材质 Color Image SHALL 为 STRAIGHT +- **AND** 材质交付后 SHALL 保留该模式 + +#### Scenario: Shadeless material is created + +- **WHEN** 用户通过支持 Shadeless 的入口创建材质 +- **THEN** Color Image SHALL 应用相同颜色空间与 Alpha 配对策略 + +### Requirement: Configuration preserves business pixels and source ownership + +材质 Color 配置 SHALL 保留原有业务 RGB、Alpha 和完全透明区域 RGB,并在 Pack、保存重载和再次编辑后保持可读取。配置 SHALL 作用于材质专用 Color Image 或独立 Color 副本。 + +#### Scenario: The source image has other users + +- **WHEN** 源 Image 同时被其他对象引用 +- **THEN** 配置 SHALL 作用于独立 Color 结果 +- **AND** 原 Image 的颜色空间、Alpha 模式和业务像素 SHALL 保持原值 + +#### Scenario: A color result is packed and reopened + +- **WHEN** byte、float、generated、dirty 或 packed 来源的 Color 完成配置并保存重载 +- **THEN** 业务 RGB/Alpha SHALL 在既有编码精度内保留 +- **AND** 结果 SHALL 保持所选颜色空间和 Alpha 模式 + +### Requirement: Preferences exclusively control material adaptation + +Preferences SHALL 是适配开关的唯一控制来源。新建材质 SHALL 读取当前偏好;已创建的材质颜色贴图 SHALL 保留原配置。创建操作和颜色空间解析 SHALL 保持偏好值。 + +#### Scenario: The preference changes + +- **WHEN** 用户在 Preferences 开启或关闭适配 +- **THEN** AnyImage 已创建的材质颜色贴图 SHALL 保持原颜色空间、Alpha 模式和像素 +- **AND** 后续新建材质 SHALL 使用新的偏好设置 + +#### Scenario: A new material shares its source with an existing material + +- **WHEN** 切换偏好后,使用已被现有材质引用的源 Image 创建新材质 +- **THEN** 新材质 SHALL 使用独立 Color Image 并应用当前偏好 +- **AND** 已有材质的贴图及其配置 SHALL 保持原值 + +#### Scenario: A material is created or an inverse space is unavailable + +- **WHEN** 创建材质或颜色空间解析发生回退 +- **THEN** Preferences 中的适配开关 SHALL 保持操作前的值 + +### Requirement: Adaptation affects only material color textures + +本选项 SHALL 仅配置 AnyImage 材质的 Color 贴图。Empty、普通图片编辑结果、Depth、Normal 和非目标材质 SHALL 保持原有行为与数据。 + +#### Scenario: A material color image is shared with an Empty + +- **WHEN** 新建材质的源 Color Image 同时被 Empty 引用 +- **THEN** 系统 SHALL 为材质 Color 创建独立副本并重新绑定目标节点 +- **AND** Empty 引用的原 Image 的颜色空间、Alpha 模式与业务像素 SHALL 保持原值 + +#### Scenario: Ordinary image editing runs with adaptation enabled + +- **WHEN** 开启适配后执行 Mask、Frame、Rectify 或图片编辑 Job +- **THEN** 编辑结果 SHALL 沿用各自已有颜色空间与 Alpha 规则 + diff --git a/openspec/specs/model-asset-retirement/spec.md b/openspec/specs/model-asset-retirement/spec.md new file mode 100644 index 0000000..088194a --- /dev/null +++ b/openspec/specs/model-asset-retirement/spec.md @@ -0,0 +1,38 @@ +# model-asset-retirement Specification + +## Purpose +TBD - created by archiving change simplify-ai-model-tiers. Update Purpose after archive. +## Requirements +### Requirement: 资产准备与发布跟随正式清单 +`model prepare` 和 `model sync` SHALL 覆盖九款正式模型的完整文件集合,对每个文件验证大小和 SHA-256;缺失、损坏或无法验证的资产 SHALL 明确失败。 + +#### Scenario: 新模型资产损坏 +- **WHEN** HAT 或 BiRefNet 的本地文件与登记大小或哈希不一致 +- **THEN** 准备或发布失败,该模型不被标记为完整就绪 + +#### Scenario: 新镜像验收 +- **WHEN** 新模型上传到 R2 +- **THEN** 使用实际下载来源回读并校验完整文件,记录每款模型的来源、大小、哈希和就绪结果 + +### Requirement: 退役资源在各发布入口一致移除 +DA3 全部现存变体、MoGe-2 ViT-L Normal、SAFMN 和 PiSA-SR SHALL 从正式模型集合、自动准备、下载和生产推理入口退役;对应专用源码、导出登记与专用构建依赖 SHALL 清理。 + +#### Scenario: 执行正式准备命令 +- **WHEN** 完成本次模型调整后运行模型准备或同步 +- **THEN** 只准备和发布九款正式模型,不下载、导出或上传退役模型 + +### Requirement: 远端和本地退役清理采用明确对象清单 +本次退役清理 SHALL 在新模型通过发布验收后执行。清理前 SHALL 列出并保存归属于退役模型的本地目录、开发缓存文件与 R2 对象键,仅操作核对过的目标,并在结束后复查对象存在性。 + +#### Scenario: 清理 DA3 远端残留 +- **WHEN** R2 中仍存在未列入正式模型清单的 DA3 变体 +- **THEN** 将归属已确认的 DA3 对象纳入本次退役清单、删除并复查,不能将停止上传视为删除完成 + +#### Scenario: 相邻目录包含其他资源 +- **WHEN** 模型目录或桶内同时存在保留模型、其他对象或用户产物 +- **THEN** 清理范围严格限定于已列出的退役目标,其他资源保持原样 + +#### Scenario: 部分对象清理失败 +- **WHEN** 本地或 R2 删除未能完整完成 +- **THEN** 记录成功与失败目标,报告未完成项,并在复查全部通过前保持退役任务未完成 + diff --git a/openspec/specs/model-inference-memory/spec.md b/openspec/specs/model-inference-memory/spec.md new file mode 100644 index 0000000..fc62b35 --- /dev/null +++ b/openspec/specs/model-inference-memory/spec.md @@ -0,0 +1,61 @@ +# model-inference-memory Specification + +## Purpose +TBD - created by archiving change release-model-inference-memory. Update Purpose after archive. +## Requirements +### Requirement: Cached model sessions survive inference cleanup + +系统 SHALL 在清理一次推理使用的临时内存后保留对应模型的已加载 Session,使相同模型、Device 与 Runtime 的下一次请求复用该 Session。 + +#### Scenario: Matching request reuses the loaded model + +- **WHEN** 一个模型操作成功完成临时推理内存清理,随后以相同模型、Device 与 Runtime 再次请求该模型 +- **THEN** 系统复用同一个已加载 Session,且不重新从模型文件创建 Session + +#### Scenario: All model families share the lifecycle + +- **WHEN** BEN2、DA3、MoGe-2 或任一 Upscale 模型在支持清理的 Provider 上完成推理 +- **THEN** 每个模型均遵循相同的 Session 保留与临时内存释放语义 + +### Requirement: Temporary GPU inference memory is released at operation completion + +系统 SHALL 在 Execution Provider 支持 memory arena shrinkage 时,于一次成功业务推理的最后一次模型运行请求释放不再使用的临时 GPU arena,同时保留 Session 持有的模型资源。 + +#### Scenario: Single-run inference releases temporary memory + +- **WHEN** 一次业务推理只执行一次模型运行并成功返回 +- **THEN** 该次模型运行请求在结束时收缩支持回收的 GPU memory arena + +#### Scenario: Multi-frame inference releases after the final frame + +- **WHEN** 一次业务推理依次处理多个输入帧 +- **THEN** 中间帧不触发 arena shrinkage,最后一帧的模型运行请求触发 arena shrinkage + +#### Scenario: Tiled inference releases after the final tile + +- **WHEN** Upscale 为一个或多个输入帧执行多次分块模型运行 +- **THEN** 中间分块与中间帧不触发 arena shrinkage,整个业务推理的最后一个分块触发 arena shrinkage + +### Requirement: Unsupported providers retain existing inference behavior + +系统 MUST 只向明确支持所选 memory arena shrinkage 配置的 Execution Provider 提交该配置;其他 Provider SHALL 按原有运行方式执行。 + +#### Scenario: Provider does not support GPU arena shrinkage + +- **WHEN** 模型 Session 使用 CPU、DirectML、CoreML 或其他未声明支持该 GPU arena shrinkage 配置的 Provider +- **THEN** 系统不提交对应的 Provider 或 Run 清理配置,且推理继续正常执行 + +### Requirement: Inference results and failure contracts remain stable + +临时内存释放 SHALL 不改变模型输入、模型输出、Job 产物、取消检查或既有资源错误处理。 + +#### Scenario: Cleanup-enabled inference returns the same model result + +- **WHEN** 对相同输入与模型分别执行启用和未启用临时内存释放的推理 +- **THEN** 两次推理返回相同结构、尺寸、dtype 与数值语义的模型结果 + +#### Scenario: Upscale provider resource failure + +- **WHEN** Upscale 的 Execution Provider 因 GPU 或系统内存不足而失败 +- **THEN** 系统继续释放失效的 Upscale Session,并返回既有的可读资源错误 + diff --git a/openspec/specs/moge-depth-artifacts/spec.md b/openspec/specs/moge-depth-artifacts/spec.md new file mode 100644 index 0000000..9e5054b --- /dev/null +++ b/openspec/specs/moge-depth-artifacts/spec.md @@ -0,0 +1,113 @@ +# moge-depth-artifacts Specification + +## Purpose +TBD - created by archiving change normalize-moge-alpha-depth. Update Purpose after archive. +## Requirements +### Requirement: GeometryFrame 是统一几何预测边界 + +系统 SHALL 以单帧 `GeometryFrame` 表示供产物生成使用的几何预测。Frame SHALL 包含二维 float32 Depth、同尺寸 float32 Validity、3×3 float32 像素空间相机内参,以及可选的同尺寸三通道 float32 Normal 与 Points。Validity SHALL 统一保存模型提供的逐像素有效性信号。 + +#### Scenario: MoGe-2 预测适配 +- **WHEN** MoGe-2 完成一帧预测 +- **THEN** 系统生成包含 Depth、Normal、由原始 Mask 转换而来的 `0/1` Validity、像素空间相机内参与按需 Points 的 `GeometryFrame` + +#### Scenario: DA3 预测适配 +- **WHEN** DA3 预测被转换为统一几何结果 +- **THEN** 系统生成包含 Depth、派生 Points、由原始 Confidence 提供的连续 Validity 与相机内参的 `GeometryFrame`,且 Normal 为空 + +#### Scenario: Frame 字段维度不一致 +- **WHEN** 任一像素 Field 的高宽与 Depth 不一致,或相机内参不是 3×3 +- **THEN** `GeometryFrame` 构建失败并报告字段维度错误 + +### Requirement: Geometry Artifact 只消费 GeometryFrame + +系统 SHALL 从 `GeometryFrame` 生成 Vector Depth、Z Depth、Normal Map 与 Depth Metadata,Artifact Writer SHALL NOT 直接依赖 MoGe-2 或 DA3 专用 Prediction 类型。 + +#### Scenario: 生成 Vector Depth +- **WHEN** 一个 `GeometryFrame` 包含 Points +- **THEN** Vector Depth Writer 从 Frame 的 Points 与 Validity 生成 `vector-depth.exr` + +#### Scenario: 生成 Z Depth 与 Metadata +- **WHEN** 一个 `GeometryFrame` 包含 Depth、Validity 与 Intrinsics +- **THEN** Z Depth 与 Metadata Writer 从该 Frame 生成 `z-depth.exr` 与 `depth.json` + +#### Scenario: 生成 Normal Map +- **WHEN** 一个 `GeometryFrame` 包含 Normal +- **THEN** Normal Writer 从 Frame 的 Normal 及调用方提供的 Region 可见 Mask 生成 Normal Map + +#### Scenario: 缺少所需 Field +- **WHEN** Writer 请求的 Points 或 Normal 在 `GeometryFrame` 中为空 +- **THEN** 产物生成失败并报告缺少对应 Geometry Field + +### Requirement: MoGe-2 使用统一黑底输入 + +系统 SHALL 在 MoGe-2 推理前将最终 Region RGBA 合成到黑底,模型 RGB SHALL 等于源 RGB 乘以归一化 Alpha。 + +#### Scenario: 完全透明像素 +- **WHEN** 最终 Region 像素的 Alpha 为 `0` +- **THEN** MoGe-2 接收到的对应 RGB 为 `(0, 0, 0)` + +#### Scenario: 半透明像素 +- **WHEN** 最终 Region 像素的 Alpha 为 `0.5` +- **THEN** MoGe-2 接收到的对应 RGB 为源 RGB 的一半 + +#### Scenario: 完全不透明像素 +- **WHEN** 最终 Region 像素的 Alpha 为 `1` +- **THEN** MoGe-2 接收到的对应 RGB 与源 RGB 相同 + +#### Scenario: 前景工作流输入一致 +- **WHEN** Remove Background 与 Fit to Foreground 产生相同的可见 RGB 和 Alpha,仅完全透明像素下的隐藏 RGB 不同 +- **THEN** 两条工作流提交给 MoGe-2 的模型 RGB 完全相同 + +### Requirement: Vector Depth 独立保存 Points 与 Validity + +系统 SHALL 在 `vector-depth.exr` 的 RGB 中保存 `GeometryFrame.points` 的每个原始有限相机空间 XYZ,且 SHALL 不因 `GeometryFrame.validity` 为零而修改 XYZ;Alpha SHALL 只保存 `GeometryFrame.validity`。 + +#### Scenario: Validity 为零的有限 Point +- **WHEN** MoGe-2 为一个像素返回有限 XYZ 且原始 Mask 转换得到的 Validity 为 `0` +- **THEN** EXR RGB 等于该 XYZ,Alpha 等于 `0` + +#### Scenario: Validity 为一的有限 Point +- **WHEN** MoGe-2 为一个像素返回有限 XYZ 且原始 Mask 转换得到的 Validity 为 `1` +- **THEN** EXR RGB 等于该 XYZ,Alpha 等于 `1` + +#### Scenario: 输入 Alpha 不改变 Vector Depth Alpha +- **WHEN** Region 输入 Alpha 与 MoGe-2 原始 Mask 不同 +- **THEN** `vector-depth.exr` Alpha 等于由 MoGe-2 原始 Mask 转换得到的 Validity + +### Requirement: Z Depth 独立保存 Depth 与 Validity + +系统 SHALL 在 `z-depth.exr` 的 RGB 三个通道中保存 `GeometryFrame.depth` 的每个原始有限 Depth,且 SHALL 不因 `GeometryFrame.validity` 为零而修改 Depth;Alpha SHALL 只保存 `GeometryFrame.validity`。 + +#### Scenario: Validity 为零的有限 Depth +- **WHEN** MoGe-2 为一个像素返回有限 Depth 且原始 Mask 转换得到的 Validity 为 `0` +- **THEN** EXR RGB 三个通道均等于该 Depth,Alpha 等于 `0` + +#### Scenario: Validity 为一的有限 Depth +- **WHEN** MoGe-2 为一个像素返回有限 Depth 且原始 Mask 转换得到的 Validity 为 `1` +- **THEN** EXR RGB 三个通道均等于该 Depth,Alpha 等于 `1` + +#### Scenario: 输入 Alpha 不改变 Z Depth Alpha +- **WHEN** Region 输入 Alpha 与 MoGe-2 原始 Mask 不同 +- **THEN** `z-depth.exr` Alpha 等于由 MoGe-2 原始 Mask 转换得到的 Validity + +### Requirement: Depth Artifact 拒绝非有限结果 + +系统 MUST 在写入 Depth EXR 前校验所有待写入 Depth 或 XYZ;任何通道包含 NaN 或 Inf 时 MUST 终止 Artifact 生成并报告错误,且 MUST NOT 以零值替换。 + +#### Scenario: Vector Depth 包含非有限值 +- **WHEN** 任意原始 XYZ 分量为 NaN 或 Inf +- **THEN** `vector-depth.exr` 生成失败并报告无效 Point Field + +#### Scenario: Z Depth 包含非有限值 +- **WHEN** 任意原始 Depth 为 NaN 或 Inf +- **THEN** `z-depth.exr` 生成失败并报告无效 Depth Field + +### Requirement: 校准筛选不改写 Depth Artifact + +系统 SHALL 按需使用输入 Alpha、正的 `GeometryFrame.validity`、正 Depth 与有限性共同筛选 Depth Metadata 的参考样本,但 SHALL NOT 把该派生筛选结果存为 Frame 的第二套有效性字段,且 SHALL NOT 改写 Depth EXR 的 RGB 或 Alpha。 + +#### Scenario: 零 Validity 区域存在连续 Depth +- **WHEN** Validity 为零的区域存在有限 Depth 或 XYZ +- **THEN** 这些值保留在 EXR RGB 中,同时不进入参考深度计算 + diff --git a/openspec/specs/moge3-model/spec.md b/openspec/specs/moge3-model/spec.md new file mode 100644 index 0000000..496fd71 --- /dev/null +++ b/openspec/specs/moge3-model/spec.md @@ -0,0 +1,73 @@ +# moge3-model Specification + +## Purpose +TBD - created by archiving change add-moge3-model. Update Purpose after archive. +## Requirements +### Requirement: Execute complete MoGe 3 with ONNX + +正式推理 SHALL 通过 ONNX Runtime 执行 MoGe 3 的学习网络计算,并完成三次细化;运行环境 SHALL 无需 PyTorch、FlexGEMM 或 Triton。 + +#### Scenario: Production inference +- **WHEN** 在仅安装声明的 ONNX 运行依赖的 Server 中执行 MoGe 3 +- **THEN** 完成包含三次细化的预测,不依赖外部 Torch 环境或服务 + +### Requirement: Validate conversion before availability + +系统 SHALL 在完整流程的动态尺寸、分辨率等级、精度、性能和执行提供程序验证通过后才将新模型登记为正式可用模型。 + +#### Scenario: Incomplete conversion +- **WHEN** 只有关闭细化的模型可以运行,或完整模型未通过预定数值验收 +- **THEN** 保留验证结果且不将其作为完整 MoGe 3 发布 + +### Requirement: Select and prepare model + +系统 SHALL 提供 MOGE3_VITL 选项,保留已有模型键和默认值,下载并校验推理所需的全部 ONNX 资产。 + +#### Scenario: Download success +- **WHEN** 所需图文件及外部权重全部下载并通过完整性校验 +- **THEN** 标记模型就绪,深度平面、裁切与全景使用用户所选模型 + +#### Scenario: Interrupted download +- **WHEN** 下载失败、取消或文件不完整 +- **THEN** 不标记就绪,并允许重新准备 + +### Requirement: Preserve geometry contract + +MoGe 3 SHALL 按所选分辨率等级提供与分析图尺寸对应的深度、法线、有效性、点图和相机内参,并遵循既有源 Alpha 处理契约。 + +#### Scenario: Image inference +- **WHEN** 有效图片完成推理 +- **THEN** 有效区域深度为有限正值,产物可供既有几何工作流应用 + +#### Scenario: Panorama inference +- **WHEN** 全景任务选择 MoGe 3 +- **THEN** 透视视图使用已知 90 度水平 FOV,模型 Mask 与源有效性共同约束融合及 Alpha 输出 + +### Requirement: Reuse existing input and workflow controls + +MoGe 3 SHALL 沿用 MoGe 2 的输入准备、最大分析尺寸、分辨率等级及设备选择流程。同一组已下载模型资产 SHALL 接受现有流程产生的不同图片尺寸、宽高比和分辨率等级,输出尺寸对应分析图片。 + +#### Scenario: Vary image dimensions +- **WHEN** 用户连续提交现有输入流程支持的不同尺寸和宽高比图片 +- **THEN** 使用同一组模型资产完成预测,遵循现有分析尺寸限制及输出尺寸契约 + +#### Scenario: Change resolution level +- **WHEN** 用户修改现有 MoGe Resolution Level 后再次执行任务 +- **THEN** 新任务采用对应 token 数并执行三次细化,复用已下载模型资产 + +#### Scenario: Switch to MoGe 3 in an existing workflow +- **WHEN** 用户在深度平面、裁切或全景工作流中将模型改为 MoGe 3 +- **THEN** 沿用原有任务参数、操作步骤与几何产物,模型内部完成多图及细化调度 + +### Requirement: Manage supported sessions + +系统 SHALL 按已验证的 ONNX 设备能力执行,复用连续帧或视图的会话,在模型切换及关闭时释放资源,对不支持的执行方式和推理失败给出明确反馈。 + +#### Scenario: Model switching +- **WHEN** 用户在 MoGe 2 与 MoGe 3 之间切换 +- **THEN** 执行新选择并释放旧模型资源 + +#### Scenario: Unsupported execution or cancellation +- **WHEN** 执行设备不支持、资源不足或任务取消 +- **THEN** 给出明确状态,不应用部分结果,并允许后续重新运行 + diff --git a/openspec/specs/object-space-normal-alignment/spec.md b/openspec/specs/object-space-normal-alignment/spec.md new file mode 100644 index 0000000..016e1bc --- /dev/null +++ b/openspec/specs/object-space-normal-alignment/spec.md @@ -0,0 +1,39 @@ +# object-space-normal-alignment Specification + +## Purpose +TBD - created by archiving change standardize-blender-object-coordinates. Update Purpose after archive. +## Requirements +### Requirement: Object Space Normal follows the canonical object frame + +系统 SHALL 让 Plane、Depth Plane、Relief Plane、Cutout 与 Depth Cutout 的 Object Space Normal 贴图按新的 canonical Blender 对象坐标解释:X 为图片水平、Z 为 up、Y 为深度方向。Object Space Normal 渲染结果 MUST 与最终 evaluated geometry 的几何法线方向一致。 + +#### Scenario: Depth object uses Object Space Normal + +- **WHEN** Depth Plane 或 Depth Solid 使用 Object Space Normal 贴图渲染 +- **THEN** 平面法线朝向 -Y +- **AND** 深度表面产生的 normal 变化围绕 -Y 基准 +- **AND** 渲染 normal 不再按旧 `-Z` 基准解释 + +### Requirement: Depth Symmetry normal maps follow mirror and final orientation + +`O Image Cutout Symmetry` SHALL 为 Object Space Normal 提供最终朝向所需的面属性。材质 SHALL 对 canonical front 保持 normal,对 canonical back 反射 Y 分量,并应用最终 yaw 旋转;侧壁 SHALL 关闭 normal map 影响。 + +#### Scenario: Front, back, and wall normals + +- **WHEN** Depth Symmetry 对象启用 Object Space Normal 并分别渲染 front、back 和 side wall +- **THEN** front normal 按 canonical 方向解释 +- **AND** back normal 的 Y 分量相对 front 发生反射 +- **AND** 最终 X 朝向旋转被应用 +- **AND** side wall 不显示 Object Space Normal 细节 + +### Requirement: Old Depth Axis normal compensation is removed + +材质和 normal-map 辅助代码 MUST 不再为 `O Image Cutout` 或 `O Image Depth Cutout` 的旧 `Depth Axis=+X` 提供 `CUTOUT_Z_TO_X` 补偿。Object Normal 的最终轴向转换 SHALL 只来自 Depth Symmetry 的新朝向属性。 + +#### Scenario: Inspect normal transform attributes + +- **WHEN** 检查 `O Image Layer` 使用的 Object Normal 转换逻辑 +- **THEN** 不存在以旧 `Depth Axis` 为输入的轴向补偿 +- **AND** Depth Symmetry 的最终朝向属性由 `O Image Cutout Symmetry` 写入 +- **AND** 材质按该属性执行最终 normal 旋转 + diff --git a/openspec/specs/panorama-conversion/spec.md b/openspec/specs/panorama-conversion/spec.md new file mode 100644 index 0000000..9e17e69 --- /dev/null +++ b/openspec/specs/panorama-conversion/spec.md @@ -0,0 +1,37 @@ +# panorama-conversion Specification + +## Purpose +TBD - created by archiving change add-panorama-conversion. Update Purpose after archive. +## Requirements +### Requirement: Image Empty panorama conversion entry + +系统 SHALL 在 Image Empty 的 AnyImage 右键菜单展示 `Convert to Panorama`,调用 `anyimage.convert_to_panorama`。 + +#### Scenario: Select panorama conversion +- **WHEN** 用户选中静态 2:1 Image Empty 并选择 Convert to Panorama +- **THEN** 提交当前图片及 Alpha 的全景生成任务,使用 Preferences 中的模型、推理级别和分析尺寸 + +#### Scenario: Unsupported image +- **WHEN** 输入是动画、视频或非 2:1 图片 +- **THEN** 提示输入限制并保留源对象 + +### Requirement: Consume current image alpha + +转换 SHALL 直接使用当前源图及 Alpha。背景处理由用户通过现有 Remove Background 完成。 + +#### Scenario: Already removed background +- **WHEN** 源 Empty 的天空已透明 +- **THEN** 当前 Alpha 进入全景任务,转换过程中只运行几何模型 + +### Requirement: Complete conversion transaction + +系统 SHALL 在主线程加载结果、打包贴图并创建节点对象,成功后替换源 Empty,并提供完整撤销。 + +#### Scenario: Successful conversion +- **WHEN** Job 返回有效产物 +- **THEN** 创建使用 O Image Panorama 的对象,继承源 Empty 世界变换并以其原点为拍摄中心,激活结果并支持撤销恢复源对象 + +#### Scenario: Cancelled or failed conversion +- **WHEN** 任务失败、取消或响应时源对象已失效 +- **THEN** 清理本任务暂存及未使用数据块,不应用部分转换结果 + diff --git a/openspec/specs/panorama-geometry-generation/spec.md b/openspec/specs/panorama-geometry-generation/spec.md new file mode 100644 index 0000000..1d8f229 --- /dev/null +++ b/openspec/specs/panorama-geometry-generation/spec.md @@ -0,0 +1,37 @@ +# panorama-geometry-generation Specification + +## Purpose +TBD - created by archiving change add-panorama-conversion. Update Purpose after archive. +## Requirements +### Requirement: Generate panoramic radial distance + +Server SHALL 从当前 RGBA 全景生成 12 个重叠的 90 度透视视图,使用已知 FOV 恢复 MoGe2 几何并融合为横向周期的径向距离场。 + +#### Scenario: Continuous panorama +- **WHEN** 输入包含跨越 U=0/1 的连续表面 +- **THEN** 两侧使用同一周期求解,产出有限正距离且无额外拼接断层 + +#### Scenario: Invalid views and cancellation +- **WHEN** 部分视图全透明或模型无有效预测 +- **THEN** 利用其余有效观测完成融合,全局无有效几何时返回明确错误,并在视图与融合阶段响应取消 + +### Requirement: Export combined depth alpha + +系统 SHALL 返回颜色文件、`depth.exr` 与 `depth.json`。深度 RGB 保存径向距离,Alpha 在导出时一次合成融合 Mask × 源 Alpha;元数据 SHALL 标明等距柱状投影、深度图尺寸和参考距离。 + +#### Scenario: Transparent background +- **WHEN** 源图某区域 Alpha 为零 +- **THEN** 该区域深度 EXR Alpha 为零,颜色文件保留源 Alpha + +#### Scenario: Fractional alpha +- **WHEN** 有效模型 Mask 为 1 且源 Alpha 为 0.5 +- **THEN** 深度导出 Alpha 为 0.5,几何端直接消费该值 + +### Requirement: Use managed production runtime + +全景生成 SHALL 使用现有模型会话、Preferences 配置和 Job 生命周期;Job 参数可序列化,Server 与 Blender 数据保持进程边界。 + +#### Scenario: Configured inference +- **WHEN** 用户修改模型或推理分辨率级别后发起转换 +- **THEN** 新任务采用配置值,最终网格细分仍由独立 Subdivide 参数控制 + diff --git a/openspec/specs/panorama-input-cropping/spec.md b/openspec/specs/panorama-input-cropping/spec.md new file mode 100644 index 0000000..6e36a3b --- /dev/null +++ b/openspec/specs/panorama-input-cropping/spec.md @@ -0,0 +1,46 @@ +# panorama-input-cropping Specification + +## Purpose +TBD - created by archiving change auto-crop-panorama-input. Update Purpose after archive. +## Requirements +### Requirement: Panorama input uses an exact centered 2:1 crop +The system SHALL prepare panorama conversion from the largest centered integer-pixel rectangle whose width is exactly twice its height. The prepared color material and AI analysis input MUST use the same cropped pixels without stretching, padding, or aspect-ratio resampling. + +#### Scenario: Source is wider than 2:1 +- **WHEN** a user converts a usable static image whose width is greater than twice its height +- **THEN** the system crops columns equally where possible from the left and right and prepares an exact 2:1 input at the source height + +#### Scenario: Source is narrower than 2:1 +- **WHEN** a user converts a usable static image whose width is less than twice its height +- **THEN** the system crops rows equally where possible from the top and bottom and prepares the largest exact 2:1 input at or below the source width + +#### Scenario: Source width is odd +- **WHEN** a usable static source has an odd width that cannot be part of an exact integer-pixel 2:1 rectangle +- **THEN** the system omits the unmatched column as part of its deterministic centered crop + +#### Scenario: Source is already 2:1 +- **WHEN** a user converts a usable static image whose width is exactly twice its height +- **THEN** the system prepares the full source image without removing pixels + +### Requirement: Panorama crop is disclosed to the user +The system SHALL issue one warning whenever panorama input preparation removes source pixels, and the warning MUST identify both the original and cropped pixel dimensions. + +#### Scenario: Conversion requires cropping +- **WHEN** the computed 2:1 bounds differ from the source bounds +- **THEN** the system reports a warning containing the source width and height and the cropped width and height + +#### Scenario: Conversion does not require cropping +- **WHEN** the source bounds already form an exact 2:1 image +- **THEN** the system does not report a crop warning + +### Requirement: Unsupported panorama sources remain rejected +The system MUST reject animated images and images too small to produce a positive integer-pixel 2:1 crop before submitting panorama generation. + +#### Scenario: Source is animated +- **WHEN** a user attempts to convert an animated image +- **THEN** the system reports an error and does not submit panorama generation + +#### Scenario: Source cannot produce a positive crop +- **WHEN** a source image cannot provide at least a 2-pixel-wide by 1-pixel-high crop +- **THEN** the system reports an error and does not submit panorama generation + diff --git a/openspec/specs/panorama-mask-regions/spec.md b/openspec/specs/panorama-mask-regions/spec.md new file mode 100644 index 0000000..1262e8d --- /dev/null +++ b/openspec/specs/panorama-mask-regions/spec.md @@ -0,0 +1,95 @@ +# panorama-mask-regions Specification + +## Purpose +TBD - created by archiving change separate-panorama-mask-regions. Update Purpose after archive. +## Requirements +### Requirement: Mask Threshold 按面分出两个区域 + +`O Image Depth Panorama` MUST 用 Separate Geometry(Face 域)把球面网格按 Mask Threshold 分成两个区域:面中心采样低于 Mask Threshold 的面由 `Selection` 输出,其余面由 `Inverted` 输出。判定 MUST 复用同一次 Mask Threshold 比较,MUST NOT 另行采样或按深度通道判断;分面后 MUST NOT 再单独清理残留边。 + +#### Scenario: 切口落在面中心轮廓 + +- **WHEN** 无效区与有效区之间存在只跨过部分顶点的面 +- **THEN** 该面在面中心仍有效时留在高 mask 区域,轮廓落在面中心的连线上,而不是所有相邻顶点都有效的内圈 + +#### Scenario: 两个区域互补 + +- **WHEN** 任意 mask 图下求值 +- **THEN** 两个区域的面数之和等于输入球面的面数,各区域输出的边都至少属于一个面 + +### Requirement: Depth Mask 打开时只输出高 mask 区域 + +Depth Mask 打开时,`O Image Depth Panorama` MUST 只输出高 mask 区域经过完整深度处理的几何,低 mask 区域 MUST NOT 出现在输出中;输出 MUST 与按面删除低 mask 面后的处理结果等价。 + +#### Scenario: 打开时与按面删除等价 + +- **WHEN** Depth Mask 打开 +- **THEN** 输出的面、顶点坐标与 UV 同按面删除低 mask 面后处理的结果一致 + +#### Scenario: 全部低于阈值 + +- **WHEN** Depth Mask 打开且所有面中心的采样都低于 Mask Threshold +- **THEN** 输出为空几何 + +### Requirement: Depth Mask 关闭时两区域分别处理再合并 + +Depth Mask 关闭时,低 mask 区域 MUST 保持自身方向、整片位移到 `Dome Radius` 球面,高 mask 区域 MUST 照常做 Depth Split、径向深度重建与切口带平滑,两者 MUST 在各自处理完成后 Join 成同一输出。低 mask 区域 MUST NOT 参与 Depth Split 与深度边界模糊,但 MUST 与高 mask 区域一样在自身自由边界上按 Boundary Smooth 松弛;输出 MUST NOT 跨区域焊接或平均顶点。 + +#### Scenario: 两个区域各自落位 + +- **WHEN** Depth Mask 关闭,且 mask 图同时含高、低两个区域 +- **THEN** 高 mask 区域各点半径等于 `Dome Radius + Depth Scale × (d − Dome Radius)`,低 mask 区域各点半径等于 `Dome Radius` + +#### Scenario: 低 mask 区域的采样深度不影响输出 + +- **WHEN** Depth Mask 关闭,且低 mask 区域写入与有效侧相差一个数量级的深度 +- **THEN** 该区域仍整片落在 `Dome Radius` 球面上 + +#### Scenario: Depth Scale 为 0 + +- **WHEN** Depth Scale 为 0 且 Depth Mask 关闭 +- **THEN** 两个区域都落在半径为 `Dome Radius` 的球面上,输出仍是完整球面 + +#### Scenario: 接缝两侧不共享顶点 + +- **WHEN** Depth Mask 关闭,且 mask 图同时含高、低两个区域 +- **THEN** 接缝两侧各自保留自己的顶点,接缝在两侧都是自由边,输出顶点数多于同一网格不分面时的顶点数 + +#### Scenario: 两侧边界都随 Boundary Smooth 松弛 + +- **WHEN** Depth Mask 关闭且 Boundary Smooth 大于 0 +- **THEN** 低 mask 区域接缝带内的点与高 mask 区域接缝带内的点都移动,两个区域的内部点保持不变,低 mask 区域的内部点仍落在 `Dome Radius` 球面上 + +### Requirement: Depth Scale 为 0 且 Depth Mask 关闭时输出整球 + +Depth Scale 为 0 且 Depth Mask 关闭时,`O Image Depth Panorama` MUST 直接输出半径为 `Dome Radius` 的完整球面,MUST NOT 做分面、Depth Split、切口带平滑,也 MUST NOT 留下未共享的接缝顶点;输出 MUST 是封闭网格。Depth Mask 打开时 MUST NOT 走这条旁路。 + +#### Scenario: 关闭开关的平坦投影没有切线 + +- **WHEN** Depth Scale 为 0 且 Depth Mask 关闭 +- **THEN** 输出的面数与顶点数与同一球面的完整网格一致,没有自由边,各点半径等于 `Dome Radius` + +#### Scenario: 打开开关的平坦投影仍按 mask 排除 + +- **WHEN** Depth Scale 为 0 且 Depth Mask 打开 +- **THEN** 输出只含高 mask 区域,低 mask 区域不出现 + +### Requirement: 高 mask 区域沿用既有切口与深度语义 + +Separate Geometry 形成的自由边界 MUST 继续走既有切口规则:深度取相邻面采样,点保持自身射线方向。Depth Split、Depth Scale、UV 与材质槽继承 MUST 保持既有职责,`O Image Depth Plane` 的 Depth Mask 删除行为 MUST 保持不变。 + +#### Scenario: 打开开关时切口不出现尖刺 + +- **WHEN** Depth Mask 打开,且低 mask 侧采样与有效侧相差一个数量级 +- **THEN** 切口圈各点的径向距离等于相邻有效面中心的采样距离 + +#### Scenario: 关闭开关时高 mask 区域仍按切口规则处理 + +- **WHEN** Depth Mask 关闭且 Boundary Smooth 为 0 +- **THEN** 高 mask 区域的接缝点深度取相邻面采样,不跟随低 mask 侧的采样 + +#### Scenario: Plane 不受影响 + +- **WHEN** `O Image Depth Plane` 用同一深度图求值,Depth Mask 打开或关闭 +- **THEN** 输出与本次改动前逐位一致 + diff --git a/openspec/specs/panorama-surface-prototype/spec.md b/openspec/specs/panorama-surface-prototype/spec.md new file mode 100644 index 0000000..4bb23eb --- /dev/null +++ b/openspec/specs/panorama-surface-prototype/spec.md @@ -0,0 +1,37 @@ +# panorama-surface-prototype Specification + +## Purpose +TBD - created by archiving change prototype-panorama-surface. Update Purpose after archive. +## Requirements +### Requirement: Isolated panorama experiment + +试验 SHALL 将代码和输出保存在独立试验目录,使用已有模型及运行环境。 + +#### Scenario: Run prototype +- **WHEN** 执行全景原型 +- **THEN** 生成距离数据、独立 Blender 文件与验证结果,原有源码及节点资产保持原状 + +### Requirement: Periodic distance reconstruction + +试验 SHALL 对完整全景使用周期经度映射、多视图距离融合及已知 90 度相机视角。 + +#### Scenario: Synthetic reference +- **WHEN** 使用解析球面或房间距离验证 +- **THEN** 投影与融合产生有限正距离,并记录与真值的误差 + +### Requirement: Geometry node surface + +试验 SHALL 使用可调 Subdivide 的 Quad Sphere,按全景距离沿球面射线定位顶点。Depth Scale 为 0 时保持单位球,1 时恢复预测距离。深度导出 SHALL 将 Mask × 源图 Alpha 写入 depth.exr 的 Alpha;几何节点 SHALL 只接收一张深度图片,并按该 Alpha 与 Validity Threshold 的比较结果剔除无效面。 + +#### Scenario: Reload Blender asset +- **WHEN** 在新的 Blender 进程加载试验文件 +- **THEN** 全有效输入下网格闭合且仅有四边面,面数为 6 × 4^Subdivide,Depth Scale 0 与 1 分别得到单位球和预测表面 + +#### Scenario: Invalid image samples +- **WHEN** 输入包含无效 Mask 或透明图片 Alpha +- **THEN** 按 O Image Depth Plane 的点域布尔选择与面域删除方式剔除不合格区域 + +#### Scenario: Sky removal before geometry prediction +- **WHEN** 生成真实全景照片示例 +- **THEN** 先去除天空背景,使用处理后的透明全景进行几何预测与深度导出 + diff --git a/openspec/specs/panorama-surface/spec.md b/openspec/specs/panorama-surface/spec.md new file mode 100644 index 0000000..03dc0de --- /dev/null +++ b/openspec/specs/panorama-surface/spec.md @@ -0,0 +1,45 @@ +# panorama-surface Specification + +## Purpose +TBD - created by archiving change add-panorama-conversion. Update Purpose after archive. +## Requirements +### Requirement: Editable Quad Sphere surface + +系统 SHALL 提供资产 `O Image Panorama`,使用全四边面 Quad Sphere,并提供 Subdivide、Depth Scale、Validity Threshold 和 Split Threshold。几何节点 SHALL 只有一个深度图片输入。 + +#### Scenario: Sphere and depth endpoints +- **WHEN** 所有样本有效且 Split Threshold 为 0 +- **THEN** Subdivide=n 产生 6×4^n 个四边面,Depth Scale=0 为单位球,Depth Scale=1 为预测径向距离表面 + +#### Scenario: Validity culling +- **WHEN** 深度 EXR Alpha 低于 Validity Threshold +- **THEN** 按 Depth Plane 的点域布尔字段与面域删除方式剔除对应区域 + +### Requirement: Depth Plane split semantics + +全景 SHALL 复用 Depth Plane 的相邻面深度差判断、内边分裂、面角深度重定位和条带面清理机制,并使用参考距离归一化的径向距离适配球面。 + +#### Scenario: Disabled splitting +- **WHEN** Split Threshold 为 0 +- **THEN** 分裂与条带清理完全旁路,有效性剔除保持生效 + +#### Scenario: Foreground depth discontinuity +- **WHEN** 相邻有效面的归一化距离跳变达到分裂条件 +- **THEN** 分离选中的内边,断层附近面角沿原球面射线使用所属面的距离,未选中顶点保持原始深度采样 + +#### Scenario: Threshold and scale consistency +- **WHEN** 增大 Split Threshold 或对所有距离与参考距离同时乘以相同正比例 +- **THEN** 增大阈值不会减少被选中的断层边,等比例距离变化不会改变断层边选择 + +#### Scenario: Seam and strip cleanup +- **WHEN** 深度断层跨越全景经度接缝或分裂产生孤立条带 +- **THEN** 经度接缝采用周期采样,面角 UV 保持正确,条带面按 Depth Plane 的同一拓扑快照规则清理 + +### Requirement: Packaged node assets + +生产节点组及 Quad Sphere 子组 SHALL 包含在 `O_AnyImage.blend` 中,通过现有加载入口使用。 + +#### Scenario: Reopen converted object +- **WHEN** 在新 Blender 进程重新打开转换后的文件 +- **THEN** 无需访问原型目录或外部参考库即可求值,贴图、细分、Depth Scale 和 Split Threshold 均正常工作 + diff --git a/openspec/specs/pasted-brush-preview/spec.md b/openspec/specs/pasted-brush-preview/spec.md new file mode 100644 index 0000000..45dcecd --- /dev/null +++ b/openspec/specs/pasted-brush-preview/spec.md @@ -0,0 +1,45 @@ +# pasted-brush-preview Specification + +## Purpose +TBD - created by archiving change update-pasted-brush-preview. Update Purpose after archive. +## Requirements +### Requirement: Texture Paint 粘贴同步笔刷预览 + +系统 SHALL 在 Texture Paint 模式成功绑定粘贴图片后,将当前可编辑笔刷的自定义预览更新为该图片,并刷新相关界面。 + +#### Scenario: 外部库笔刷首次粘贴 +- **WHEN** 当前笔刷来自外部库,用户在 Texture Paint 模式粘贴图片 +- **THEN** 创建并激活的本地笔刷副本使用该图片作为纹理和自定义预览 + +#### Scenario: 已有笔刷重复粘贴 +- **WHEN** 用户向当前可编辑笔刷粘贴另一张图片 +- **THEN** 同一笔刷的纹理和预览同步更新为新图片 + +#### Scenario: 其他绘制模式粘贴 +- **WHEN** 用户在 Sculpt 或 Vertex Paint 模式粘贴图片 +- **THEN** 系统沿用该模式现有纹理绑定行为并保留笔刷原预览 + +### Requirement: 预览使用已打包图片并保留显示效果 + +系统 SHALL 支持从已打包的剪贴板图片生成预览,保持图片宽高比和透明区域;源图片像素、尺寸及打包状态 MUST 保持。 + +#### Scenario: 非正方形透明图片 +- **WHEN** 用户粘贴已打包且原临时文件已删除的非正方形透明图片 +- **THEN** 笔刷预览展示完整图片,保持比例和透明区域,源图片数据保持 + +### Requirement: 预览随文件保存 + +笔刷自定义预览 SHALL 随当前 `.blend` 文件保存,在预览临时文件清理后仍可恢复。 + +#### Scenario: 保存后重新打开 +- **WHEN** 用户成功更新笔刷预览,保存并重新打开 `.blend` 文件 +- **THEN** 对应本地笔刷仍显示该图片预览 + +### Requirement: 预览失败保留可用纹理 + +预览更新失败时系统 SHALL 保留已绑定纹理并提示预览更新失败;预览临时文件 MUST 在成功或失败后清理。 + +#### Scenario: Blender 拒绝加载预览 +- **WHEN** 纹理已绑定但自定义预览加载失败 +- **THEN** 笔刷仍可使用粘贴纹理绘制,用户收到预览失败提示,临时文件被清理 + diff --git a/openspec/specs/pinned-geometry-smoothing/spec.md b/openspec/specs/pinned-geometry-smoothing/spec.md new file mode 100644 index 0000000..ca32c2d --- /dev/null +++ b/openspec/specs/pinned-geometry-smoothing/spec.md @@ -0,0 +1,109 @@ +# pinned-geometry-smoothing Specification + +## Purpose +TBD - created by archiving change pin-geometry-node-smoothing. Update Purpose after archive. +## Requirements +### Requirement: 边界平滑固定开启 Pin Sharp 与 Pin Boundary + +`O Image Cutout Symmetry` 的 `Seam Smooth`,以及 `O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama` 的 `Boundary Smooth` SHALL 固定使用 `Pin Sharp = 1` 与 `Pin Boundary = 1`,且 MUST NOT 为这两个 pin 新增或修改 modifier 输入。 + +#### Scenario: 边界点按边界条带松弛 + +- **WHEN** 平滑命中边界点,包括 Split 形成的切边与对称平面的切缝 +- **THEN** 该点的目标位置取自边界条带自身权重 0.5 的一步 Blur +- **AND** 该点不被内部 Blur 结果拖拽,轮廓不因平滑收缩 + +#### Scenario: 边界顶点仍沿边界移动 + +- **WHEN** 边界上存在阶梯状顶点 +- **THEN** 顶点沿边界移动以消除阶梯 +- **AND** 顶点不会被固定在原位 + +#### Scenario: 尖锐处少动 + +- **WHEN** 平滑范围内存在法线不连续的尖锐处 +- **THEN** 该点的平滑权重乘以 `|Blur(Normal, 10)|` +- **AND** 尖锐处的位移小于同等权重下平坦区域的位移 + +### Requirement: Fill Smooth 平滑衔接带 + +`O Image Cutout Symmetry` 的 `Fill Smooth` SHALL 平滑 front 表面与它挤出的侧壁之间的衔接带。锚点 SHALL 是 front 的完整边界带,即外侧轮廓与对称面上的切缝,SHALL 按四圈渐变衰减并覆盖对称轴上的点。该衔接处不是网格边界,因此 MUST NOT 套用 `Pin Boundary`,但 SHALL 保留 `Pin Sharp`。 + +#### Scenario: 衔接带使用邻域平均 + +- **WHEN** `Fill Smooth` 大于 0 +- **THEN** 受力点按邻域平均松弛,不切换到边界条带目标 + +#### Scenario: 衔接带保留锐度权重 + +- **WHEN** 衔接带范围内存在法线不连续的尖锐处 +- **THEN** 该点的平滑权重乘以 `|Blur(Normal, 10)|` + +#### Scenario: 衔接带覆盖对称轴上的点 + +- **WHEN** `Fill Smooth` 大于 0 +- **THEN** 对称轴上的点与外侧轮廓同样产生面内位移 + +#### Scenario: 衔接带比两圈更宽 + +- **WHEN** `Fill Smooth` 大于 0 +- **THEN** 从轮廓向内第三圈的点仍然受力 +- **AND** 超出配置圈数的点保持原位 + +### Requirement: 作用范围保持渐变语义 + +`Boundary Smooth`、`Fill Smooth`、`Seam Smooth` SHALL 继续表示平滑生效的渐变区域与迭代次数,落在范围外的顶点 MUST 保持原位。 + +#### Scenario: 范围外不动 + +- **WHEN** 任一平滑的迭代次数大于 0 +- **THEN** 落在渐变范围外的顶点位置与输入完全一致 +- **AND** 拓扑与 UV 保持不变 + +#### Scenario: Cutout 轮廓与切边权重差异 + +- **WHEN** Depth Cutout 同时存在原始轮廓与 Split 切边 +- **THEN** Split 切边使用完整权重 +- **AND** 原始轮廓使用 0.1 倍权重 +- **AND** Depth Plane 与 Panorama 的轮廓权重保持 1.0 + +#### Scenario: 关闭平滑 + +- **WHEN** 迭代次数为 0 +- **THEN** 输出几何与未平滑的基线完全一致 + +### Requirement: 共享平滑实现 + +`common/smoothing.py` SHALL 提供单一的平滑构建函数:pin 形态使用边界条带目标与锐度系数,普通形态只使用邻域平均目标。各节点组只提供几何、迭代次数、作用范围、权重、可移动分量与是否 pin;系统 MUST NOT 为单个节点组保留独立的平滑实现或旧偏移路径。 + +#### Scenario: 站点调用一致 + +- **WHEN** 任一节点组构建顶点位置平滑 +- **THEN** 该组只传入几何、迭代次数、作用范围、权重、允许移动的分量与是否 pin +- **AND** 边界条带分支、锐度权重与普通邻域平均目标由共享实现生成 + +#### Scenario: 对称平面约束 + +- **WHEN** `O Image Cutout Symmetry` 应用 Fill 或 Seam 平滑 +- **THEN** 位移限制在对称平面内的分量 +- **AND** 镜像焊接与闭合性保持不变 + +### Requirement: 属性平滑保持现状 + +法线平滑、深度模糊、掩码与统计模糊 MUST NOT 纳入 pin,保持既有节点与权重。 + +#### Scenario: 属性平滑不受影响 + +- **WHEN** 构建 Depth Cutout、Depth Plane 或 Panorama +- **THEN** 法线平滑与深度模糊的节点结构、迭代次数与权重与变更前一致 + +### Requirement: 资产与测试同步 + +源码、保存资产与测试 SHALL 保持一致;几何节点及嵌套组 MUST 保持 Capture Attribute 为零。 + +#### Scenario: 重建资产 + +- **WHEN** 执行节点资产构建并在独立 Blender 进程重新加载 +- **THEN** 资产与源码构建结果通过相同的行为验证 +- **AND** Capture Attribute 检查通过 + diff --git a/openspec/specs/plane-conversion/spec.md b/openspec/specs/plane-conversion/spec.md new file mode 100644 index 0000000..718009d --- /dev/null +++ b/openspec/specs/plane-conversion/spec.md @@ -0,0 +1,113 @@ +# plane-conversion Specification + +## Purpose +TBD - created by archiving change rework-plane-conversion. Update Purpose after archive. +## Requirements +### Requirement: Converted planes use a centered origin +系统 SHALL 让 Convert to Plane 和 Convert to Depth Plane 的结果对象以生成矩形面的中心为对象原点,同时保持源 Image Empty 所表示矩形的世界空间位置不变。 + +#### Scenario: Convert an offset Image Empty +- **WHEN** 用户转换一个 `empty_image_offset` 不以原点为中心的 Image Empty +- **THEN** 结果对象原点位于矩形面的中心 +- **AND** 结果面的四个世界空间角点与转换前 Image Empty 的显示范围一致 + +#### Scenario: Preserve transformed placement +- **WHEN** 源 Image Empty 带有平移、旋转或缩放 +- **THEN** 普通 Plane 和 Depth Plane 均通过对象矩阵保留这些世界空间变换 +- **AND** 结果 Mesh 顶点以对象原点为中心分布 + +### Requirement: Plane node groups retain the Mesh material set +`O Mesh Plane` 和 `O Image Depth Plane` SHALL 不公开 `Material` socket。创建对象的代码 SHALL 把图像材质放入 Mesh 的第 `0` 个材质槽;`O Mesh Plane` SHALL 在删除输入几何前把其材质集合传递给纯生成几何,并让生成面使用材质索引 `0`。 + +#### Scenario: Evaluate a generated Plane +- **WHEN** 带有第 0 个材质槽的对象通过 `O Mesh Plane` 生成平面或体块 +- **THEN** 求值后的 Mesh 保留该材质槽 +- **AND** 所有面使用材质索引 `0` +- **AND** Modifier 不包含 `Material` 输入 + +#### Scenario: Evaluate a generated Depth Plane +- **WHEN** 带有第 0 个材质槽的对象通过 `O Image Depth Plane` 生成地形体块 +- **THEN** 求值后的 Mesh 保留该材质槽 +- **AND** 所有面使用材质索引 `0` +- **AND** `O Image Depth Plane` 不向 `O Mesh Plane` 传递材质 + +### Requirement: Depth Plane exposes unified depth controls +`O Image Depth Plane` SHALL 使用 `Thickness`、`Depth Scale`、`Base Plane Depth` 和 `Subdivide` 作为用户控制项。`Depth Image` 与 `Uniform Scale` SHALL 继续作为隐藏数据输入。旧的 `Depth Amount`、`Reference Depth` 和 `Material` 输入 SHALL 不再存在。 + +#### Scenario: Create a Depth Plane modifier +- **WHEN** Convert to Depth Plane 创建 Modifier +- **THEN** `Depth Scale` 默认值为 `1.0` +- **AND** `Base Plane Depth` 取当前 Depth Metadata 的参考深度乘以 `Uniform Scale` +- **AND** `Subdivide` 默认值为 `6` + +#### Scenario: Inspect the node group interface +- **WHEN** 加载发布资产中的 `O Image Depth Plane` +- **THEN** 输入接口包含 `Geometry`、`Subdivide`、`Thickness`、`Depth Scale`、`Base Plane Depth`、`Uniform Scale` 和 `Depth Image` +- **AND** 输入接口不包含 `Depth Amount`、`Reference Depth` 或 `Material` + +### Requirement: Depth Plane shares one prediction for depth and material normal +系统 SHALL 让 Depth Plane 通过通用 MoGe-2 Artifact 生成层执行一次模型预测,并从同一个 `GeometryFrame` 生成 `z-depth.exr`、`depth.json` 与 `tangent-normal.png`。Blender SHALL 将 Tangent Normal 作为 Non-Color 图片加载、Pack 并接入结果材质;Normal Map SHALL 只影响材质着色,不参与 Geometry Nodes 置换。 + +#### Scenario: Generate Depth Plane artifacts +- **WHEN** Depth Plane Job 处理一张完整图片 +- **THEN** MoGe-2 inference 只执行一次 +- **AND** Job 返回 `z_depth`、`depth_metadata` 和 `tangent_normal` + +#### Scenario: Create the Depth Plane material +- **WHEN** Blender 接收成功的 Depth Plane Job 结果 +- **THEN** `tangent-normal.png` 以 Non-Color Tangent Space Normal 加载并 Pack +- **AND** 结果材质的 Normal 输入使用该图片 +- **AND** 结果材质的 Principled IOR 为 `1.2` +- **AND** `z-depth.exr` 继续单独驱动 Depth Plane 几何置换 + +#### Scenario: Keep workflow-specific job orchestration +- **WHEN** Depth Plane 与 Cutout 请求 MoGe-2 产物 +- **THEN** 两者复用同一个通用 Artifact 生成层 +- **AND** Depth Plane Job 不承担 Cutout 的 Selection、Refine Selection 或裁切结果协议 + +### Requirement: Depth Plane is a single-sided terrain block +系统 SHALL 从 `O Mesh Plane` 的有厚度几何生成 Depth Plane。固定底面 SHALL 位于原图片平面,未置换的可变面 SHALL 位于其正法线方向的 `Thickness` 距离处;只有可变面完整应用深度置换,固定底面 SHALL 完全不动,侧壁中间环 SHALL 按其厚度位置线性渐变置换。 + +#### Scenario: Disable depth displacement +- **WHEN** `Depth Scale` 为 `0` +- **THEN** 固定底面保持在原图片平面 +- **AND** 可变面位于正法线方向的 `Thickness` 距离处 + +#### Scenario: Change depth displacement +- **WHEN** 用户改变 `Depth Scale` 或 `Base Plane Depth` +- **THEN** 可变面按完整位移变化 +- **AND** 固定底面的所有顶点位置保持不变 + +#### Scenario: Interpolate side-wall rings +- **WHEN** `Thickness` 大于 `0` 且 Depth Plane 发生置换 +- **THEN** 侧壁包含位于正面与背面之间的细分环 +- **AND** 每个环的位移按其从固定底面到可变面的归一化位置在零位移与完整位移之间线性插值 + +### Requirement: Depth displacement uses the calibrated zero plane +系统 SHALL 以采样 Z Depth 乘以隐藏的 `Uniform Scale` 得到局部深度,并以 `Base Plane Depth` 为零位移位置。比零平面更近的采样 SHALL 向正法线方向置换,比零平面更远的采样 SHALL 向固定底面置换。 + +#### Scenario: Sample the base depth +- **WHEN** 某个正面顶点采样的缩放深度等于 `Base Plane Depth` +- **THEN** 该顶点的深度位移为零 + +#### Scenario: Sample a nearer depth +- **WHEN** 某个正面顶点采样的缩放深度小于 `Base Plane Depth` +- **THEN** 该顶点沿正法线方向产生与 `Depth Scale` 成比例的位移 + +#### Scenario: Sample a farther depth +- **WHEN** 某个正面顶点采样的缩放深度大于 `Base Plane Depth` +- **THEN** 该顶点向固定底面产生与 `Depth Scale` 成比例的位移 + +### Requirement: Inward displacement does not cross the fixed base +系统 SHALL 把负向可变面位移限制在固定底面之前,并保留非零间隙以避免可变面、侧壁环和底面折叠或相交。正向位移 SHALL 不受 `Thickness` 限制,因此最终包围盒深度可以大于基础厚度。 + +#### Scenario: Excessive inward displacement +- **WHEN** 深度公式产生绝对值大于或等于 `Thickness` 的负向位移 +- **THEN** 可变面停留在固定底面之前 +- **AND** 所有侧壁环仍按顺序位于固定底面与可变面之间 + +#### Scenario: Outward displacement exceeds the base thickness +- **WHEN** 正向深度位移大于 `Thickness` +- **THEN** 系统保留完整正向位移 +- **AND** 固定底面仍位于原图片平面 + diff --git a/openspec/specs/polyline-gesture-interaction/spec.md b/openspec/specs/polyline-gesture-interaction/spec.md new file mode 100644 index 0000000..c99a81a --- /dev/null +++ b/openspec/specs/polyline-gesture-interaction/spec.md @@ -0,0 +1,65 @@ +# polyline-gesture-interaction Specification + +## Purpose +TBD - created by archiving change match-polyline-gesture-interaction. Update Purpose after archive. +## Requirements +### Requirement: Polyline exposes a progressive close target +Polyline SHALL 在至少三个已提交点后,根据鼠标到起点的屏幕距离显示渐进闭合提示。实际闭合半径 MUST 为 `15 px × UI Scale`;提示距离 MUST 为实际闭合半径的平方。鼠标从提示范围边缘接近起点时,提示圈半径 MUST 通过平滑插值从 `1 px × UI Scale` 增长到实际闭合半径,并 MUST 使用明暗对比轮廓保持可见。 + +#### Scenario: Cursor approaches the start point +- **WHEN** Polyline 已提交至少三个点,且鼠标从提示范围边缘向起点移动 +- **THEN** 起点提示圈连续放大,并在鼠标到达起点时显示为实际闭合半径 + +#### Scenario: Cursor remains outside the hint distance +- **WHEN** Polyline 已提交至少三个点,但鼠标位于提示距离之外 +- **THEN** 系统不绘制起点闭合提示圈 + +#### Scenario: Polyline has fewer than three points +- **WHEN** Polyline 尚未提交三个点 +- **THEN** 系统不绘制起点闭合提示圈且不允许点击起点完成 + +#### Scenario: UI Scale changes +- **WHEN** Blender UI Scale 不是 1.0 +- **THEN** 闭合命中半径、提示距离与提示圈尺寸使用同一 UI Scale 计算 + +### Requirement: Polyline supports deliberate mouse completion +Polyline SHALL 在至少三个已提交点后允许用户点击起点实际闭合半径内或双击左键完成。起点点击 MUST 不添加新顶点;双击的第二击 MUST 不添加重复顶点。点数不足时,点击或双击 MUST NOT 提交 Selection。 + +#### Scenario: User clicks inside the close target +- **WHEN** Polyline 已提交至少三个点,且用户在起点实际闭合半径内按下左键 +- **THEN** 系统使用现有已提交顶点完成 Polyline,不添加点击位置为新顶点 + +#### Scenario: User double-clicks after three points +- **WHEN** Polyline 已提交至少三个点,且用户双击左键 +- **THEN** 系统完成 Polyline,并且双击的第二击不产生重复顶点 + +#### Scenario: User double-clicks before three points +- **WHEN** Polyline 的已提交点少于三个,且用户双击左键 +- **THEN** 系统保持 Polyline Modal 运行且不提交 Selection + +#### Scenario: User clicks outside the close target +- **WHEN** Polyline 已提交至少三个点,且用户在起点实际闭合半径外单击有效的新位置 +- **THEN** 系统提交该位置为下一个顶点并继续 Polyline + +### Requirement: Polyline retains keyboard and cancellation controls +Polyline MUST 保留 Enter 与 Numpad Enter 完成、Backspace 删除最后一点、RMB 与 Esc 取消以及工具切换取消的现有行为。状态文本 SHALL 同时说明点击起点、双击或 Enter 可以完成,并说明 Backspace 删除一点。 + +#### Scenario: User confirms with Enter +- **WHEN** Polyline 已提交至少三个点,且用户按 Enter 或 Numpad Enter +- **THEN** 系统完成 Polyline + +#### Scenario: User removes the last point +- **WHEN** Polyline 已提交两个或更多点,且用户按 Backspace +- **THEN** 系统删除最后一个已提交点并继续预览 + +#### Scenario: User cancels the gesture +- **WHEN** 用户按 RMB、Esc 或切换到其他工具 +- **THEN** 系统取消未完成的 Polyline 并移除 Overlay + +### Requirement: Mask uses a cross painting cursor +Mask Tool SHALL 为 Lasso、Brush 与 Polyline Gesture 统一使用 Blender 的 `PAINT_CROSS` cursor。 + +#### Scenario: User activates Mask +- **WHEN** 用户在 3D Viewport 激活 Mask Tool +- **THEN** 工具光标显示为 `PAINT_CROSS`,并在切换 Lasso、Brush 与 Polyline 时保持一致 + diff --git a/openspec/specs/post-cutout-symmetry/spec.md b/openspec/specs/post-cutout-symmetry/spec.md new file mode 100644 index 0000000..52539b1 --- /dev/null +++ b/openspec/specs/post-cutout-symmetry/spec.md @@ -0,0 +1,78 @@ +# post-cutout-symmetry Specification + +## Purpose +TBD - created by archiving change compose-depth-cutout-symmetry. Update Purpose after archive. +## Requirements +### Requirement: 原入口串联 Cutout 与对称节点 + +系统 SHALL 保留 Depth Solid 与 Depth Symmetry 入口。Depth Symmetry SHALL 按顺序创建 `O Image Depth Cutout` 和 `O Image Cutout Symmetry` 修改器,将 Cutout 两种 Thickness 输入均初始化为 0,并分别将深度标定参数与对称方向交给对应节点。 + +#### Scenario: 创建默认对称对象 +- **WHEN** 用户通过原 Depth Symmetry 入口完成轮廓创建 +- **THEN** 对象先执行零厚度 Cutout,再执行对称;Cutout 保留节点默认参数,包括 Depth Split、Boundary Smooth、Smooth 与 Depth Axis,仅沿用通用的手势模式选择、清理阈值和图像标定输入 +- **AND** 切换 Balloon / Shell 后厚度仍为 0 + +### Requirement: 节点厚度默认值与入口初始化 + +`O Image Depth Cutout` 节点资产的 Balloon / Shell 两种 Thickness 默认值 SHALL 均为 0。Depth Solid 创建时 SHALL 按输入 subtype 分别设置 Balloon Thickness 为 1、Shell Thickness 为 0.2,其他创建默认设置保持不变;Depth Symmetry SHALL 使用两种厚度的零默认值。 + +#### Scenario: 手动添加 Cutout 节点 +- **WHEN** 用户从资产添加未经入口覆写的 Cutout 节点 +- **THEN** Balloon / Shell 两种 Thickness 均为 0 + +#### Scenario: 创建 Depth Solid +- **WHEN** 用户通过 Depth Solid 入口创建对象 +- **THEN** Balloon Thickness 为 1,Shell Thickness 为 0.2,两个输入均完成初始化 +- **AND** 切换模式时使用对应厚度值,边界平滑等其他默认设置保持不变 + +### Requirement: 成形后镜像与补面 + +对称节点 SHALL 接收上游完整几何,沿用原 Projected 的方向对齐、偏移、负侧面移除、镜像翻面与对应边界直壁连接语义。Fill Sides SHALL 控制是否添加连接侧壁。 + +#### Scenario: 非零厚度的两种成形结果 +- **WHEN** Balloon 或 Shell 的非零厚度输出连接至对称节点 +- **THEN** 节点对已成形结果执行对称;Shell 的壳体内外表面一同参与 +- **AND** Fill Sides 关闭时不添加连接壁,开启时连接对应边界且保持面方向一致 + +### Requirement: 精简对称控制 + +对称节点 SHALL 仅公开 Geometry、Symmetry Direction、Depth Offset、Fill Sides、Fill Smooth 输入及 Geometry 输出,始终生成镜像两侧。默认方向 SHALL 为 (0, 0, 1),偏移 SHALL 为 0,Fill Smooth SHALL 为 2,Fill Sides SHALL 开启。 + +#### Scenario: 默认补面平滑 +- **WHEN** 从资产加载新的对称节点 +- **THEN** Fill Smooth 默认为 2 + +#### Scenario: 调整对称方向 +- **WHEN** 用户调整 Symmetry Direction 和 Depth Offset +- **THEN** 几何使用原方向对齐及轴向偏移语义更新,对称平面位于调整后的局部 XY 平面 +- **AND** 界面不提供 Double Sided、Depth Axis、全局 Smooth 或 Boundary Smooth + +### Requirement: 深度 Cutout 使用 Object Space 法线 + +Depth Solid 与 Depth Symmetry SHALL 使用 Object Space 法线贴图,并在创建材质时开启 `O Image Layer` 的 Object Space。 + +#### Scenario: 创建深度 Cutout 材质 +- **WHEN** 用户通过任一深度 Cutout 入口生成对象 +- **THEN** 生成流程请求并读取 object_normal,材质的 Object Space 为开启状态 + +### Requirement: 仅平滑新增侧壁 + +Fill Smooth SHALL 控制新增侧壁的几何平滑,固定与保留输入几何相接的端点并保持镜像对称;数值为 0 SHALL 使用原直壁构成。 + +#### Scenario: 平滑连接壁 +- **WHEN** Fill Sides 开启且 Fill Smooth 大于 0 +- **THEN** 有曲率的轮廓所生成侧壁内部顶点可被平滑移动,原保留表面及接缝端点不受该平滑移动 +- **AND** 对应连接保持封闭,镜像对称关系保持成立 + +#### Scenario: 禁用补面 +- **WHEN** Fill Sides 关闭而 Fill Smooth 改变 +- **THEN** 输出几何保持一致 + +### Requirement: 零厚度匹配原 Projected + +在相同输入、标定与方向偏移、相同预处理条件下,Cutout 零厚度接对称节点 SHALL 与原 Depth Symmetry 的 Projected 在顶点位置、面连接及朝向上等价。 + +#### Scenario: 两种模式与补面开关的一致性 +- **WHEN** 分别选择 Balloon / Shell 且两个厚度均为 0,Cutout 分割与平滑关闭、Depth Axis 为 -Z,旧 Projected 的 Smooth 与 Boundary Smooth 为 0,新 Fill Smooth 为 0 +- **THEN** 对 Fill Sides 开关分别比较,新旧结果的顶点位置在浮点容差内一致,面数、连接及朝向一致 + diff --git a/openspec/specs/production-only-ai-workflows/spec.md b/openspec/specs/production-only-ai-workflows/spec.md new file mode 100644 index 0000000..c11ab02 --- /dev/null +++ b/openspec/specs/production-only-ai-workflows/spec.md @@ -0,0 +1,49 @@ +# production-only-ai-workflows Specification + +## Purpose +TBD - created by archiving change remove-debug-workflows. Update Purpose after archive. +## Requirements +### Requirement: Blender exposes production AI workflows + +扩展 MUST 移除 Debug 主面板、DA3 / MoGe-2 / BEN2 / Upscale 子面板及四个 `anyimage.debug_*` Operator,并保持正式类型注册和逆序注销完整。 + +#### Scenario: Extension registration lifecycle +- **WHEN** 扩展注册、注销并重新注册 +- **THEN** Debug 面板和 Operator 均不在注册清单中,正式面板与 Operator 正常可用 + +### Requirement: Server registers production jobs only + +Server MUST 移除 `debug-da3`、`debug-moge2`、`debug-ben2` 和 `debug-upscale` Job 及其处理实现,不提供兼容入口。 + +#### Scenario: Registered jobs +- **WHEN** Server 加载 Job 注册清单 +- **THEN** 四个 Debug Job 均缺席,正式 Job 保持已注册且可执行 + +### Requirement: Debug implementation is removed completely + +源码和扩展文件选择 MUST 移除 Debug 专用模块、Scene 参数、UI 辅助函数、参数组装、原始预测序列化、帧复制及相应导入和导出。 + +#### Scenario: Source and packaging boundaries +- **WHEN** 检查模块引用与扩展打包文件清单 +- **THEN** `operators/debug.py`、`server/jobs/debug_predictions.py`、`server/outputs/` 和 Debug 专用辅助实现均缺席,保留模块可正常导入 + +#### Scenario: Scene settings +- **WHEN** 读取 `AnyImageSettings` 的属性声明 +- **THEN** Debug 专用属性已删除,正式使用的 `video_frames`、Cutout 和 Mask 属性保持可用 + +#### Scenario: Debug-only model dependencies +- **WHEN** 加载模型目录与缓存 Resource +- **THEN** DA3 模型声明、推理模块与缓存分支均已移除,正式 MoGe-2、BEN2 和 Upscale 模型保持可用 + +### Requirement: Shared production inference remains available + +系统 SHALL 保持正式深度处理、Cutout、Remove Background 和 Upscale 所需的模型、输入校验、设备选择、多帧处理及内存释放行为。 + +#### Scenario: Production model requests +- **WHEN** 正式 Operator 组装并提交 AI 请求 +- **THEN** 请求使用正式模型参数及 Add-on Preferences 设备设置,输入校验和视频帧数传递保持有效 + +#### Scenario: Production output and memory lifecycle +- **WHEN** 正式 Job 完成单帧或多帧模型推理 +- **THEN** 生成对应正式产物,并按既有最终推理边界请求内存释放 + diff --git a/openspec/specs/projected-depth-surface/spec.md b/openspec/specs/projected-depth-surface/spec.md new file mode 100644 index 0000000..9882046 --- /dev/null +++ b/openspec/specs/projected-depth-surface/spec.md @@ -0,0 +1,89 @@ +# projected-depth-surface Specification + +## Purpose +TBD - created by archiving change add-projected-depth-plane. Update Purpose after archive. +## Requirements +### Requirement: Full-image depth surface has a conversion entry +系统 SHALL 提供 `anyimage.convert_to_depth_surface`,界面显示 Convert to Depth Surface,与 Plane、Depth Plane 并列;该入口 SHALL 从当前静态 Image Empty 创建完整图片的相机投影曲面。 + +#### Scenario: Convert a still image +- **WHEN** 当前 Image Empty 是静态图片且 AI 环境可用 +- **THEN** 执行一次完整图片转换,成功后结果沿用源对象名称并替换源 Empty +- **AND** 一次 Undo 恢复转换前状态 + +#### Scenario: Reject unsupported media +- **WHEN** 输入为多帧 Movie 或 Sequence +- **THEN** 提示仅支持静态图片,不提交推理任务 + +#### Scenario: AI setup is required +- **WHEN** AI 环境未就绪 +- **THEN** 沿用现有 AI 设置流程,源 Empty 保持可用 + +### Requirement: Base geometry matches Plane conversion +Depth Surface SHALL 使用普通 Convert to Plane 的基础 Mesh 创建、中心矩阵、UV 和 O Mesh Plane 细分。相同 Subdivide 下投影前零厚度几何 MUST 与普通 Plane 的零厚度几何一致。整图矩形拓扑 SHALL 覆盖透明区域及深度跳变区域。 + +#### Scenario: Compare planar meshes +- **WHEN** 对同一图片以相同 Subdivide 创建普通 Plane 和 Thickness 0、Depth Scale 0 的 Depth Surface +- **THEN** 两者顶点位置、面连接、UV 和材质索引一致 + +#### Scenario: Preserve placement +- **WHEN** 源 Image Empty 具有非中心显示偏移以及平移、旋转、缩放 +- **THEN** Depth Scale 0、Thickness 0 的结果四角与源显示矩形的世界空间四角一致 +- **AND** 对象原点位于显示矩形中心 + +#### Scenario: Preserve rectangular coverage +- **WHEN** 图片包含透明孔洞或纹理深度跳变 +- **THEN** 基础网格仍为完整矩形细分,Alpha 由材质表达 + +### Requirement: Surface controls include normal smoothing +`O Image Depth Surface` SHALL 只公开 Subdivide、Thickness、Depth Scale、Reference Depth 以及 Normal Smooth 五项用户控制。默认值 SHALL 分别为 6、0、1 和 Metadata reference_depth 乘以 Uniform Scale。Geometry SHALL 为结构输入,Depth Image 与 Uniform Scale SHALL 为隐藏数据输入。输入 MUST 使用 SINGLE;Thickness、Reference Depth MUST 使用 DISTANCE。 + +#### Scenario: Inspect a new modifier +- **WHEN** 创建 Depth Surface 修改器 +- **THEN** 用户看到规定的五项控制,Reference Depth 与 Normal Smooth 位于 Options;Normal Smooth 默认 50,范围 0–50 +- **AND** 不出现 Cutout 的 Mode、Split、Balloon、Inflation、轮廓或轴向控制 + +### Requirement: Geometry follows camera projection +Depth Surface SHALL 按现有 Depth Cutout 的相机 XYZ 坐标约定投影。采样 XYZ 乘以 Uniform Scale 后,XY SHALL 以 Depth Scale 从基础平面插值至相机 X、负相机 Y;Z SHALL 为 `(Reference Depth − scaled_camera_z) × Depth Scale`。 + +#### Scenario: Full projection +- **WHEN** Thickness 为 0、Depth Scale 为 1,输入合成相机 XYZ 纹理 +- **THEN** 每个顶点的位置符合相机投影公式,包括 Y 方向与深度基准 + +#### Scenario: Adjust depth strength +- **WHEN** Depth Scale 分别为 0、0.5、1 和大于 1 的值 +- **THEN** XY 与 Z 按同一缩放语义连续变化,0 恢复基础平面,大于 1 正常外推 + +#### Scenario: Adjust reference depth +- **WHEN** Reference Depth 增加局部距离 d,Depth Scale 为 s +- **THEN** 曲面 Z 增加 d × s,投影 XY 保持不变 + +### Requirement: Thickness forms a uniform surface shell +Thickness 为 0 时系统 SHALL 输出单层曲面;Thickness 大于 0 时 SHALL 保留投影正面,按正面 POINT 法线按 Normal Smooth 进行邻域平滑并归一化后的方向向内生成背面并连接矩形边界。 + +#### Scenario: Zero thickness +- **WHEN** Thickness 为 0 +- **THEN** 仅存在一层曲面,无重复背面或退化侧壁 + +#### Scenario: Positive thickness +- **WHEN** 对平坦深度输入设置正 Thickness +- **THEN** 对应正背面顶点之间的距离等于 Thickness,边界闭合且法线朝向一致 + +### Requirement: One prediction produces complete image artifacts +整图 Depth Surface SHALL 通过共用 Artifact 层执行一次 MoGe-2 推理,生成 VECTOR Depth、Metadata 与 Tangent Normal,并保留完整 Color。Blender SHALL 在主线程加载、Pack 并设置图片色彩空间,将 Normal 用于材质,将 VECTOR Depth 用于几何。 + +#### Scenario: Complete a generation job +- **WHEN** 整图 Depth Surface Job 成功 +- **THEN** 只执行一次推理,并返回 `vector_depth`、`depth_metadata`、`tangent_normal` 及完整颜色产物 + +#### Scenario: Preserve existing depth plane generation +- **WHEN** 用户执行现有 Convert to Depth Plane +- **THEN** 继续生成 Z Depth 与 Tangent Normal,并创建固定底面浮雕 + +### Requirement: Node assets preserve geometry data +新节点组 SHALL 从 `assets/O_AnyImage.blend` 按名称加载。节点求值 SHALL 保留 UV 与第 0 个材质槽,并清理自身临时属性;构建验证 SHALL 检查接口、实际几何与 Blender 中的节点布局。 + +#### Scenario: Evaluate the asset +- **WHEN** 加载并求值 `O Image Depth Surface` +- **THEN** 结果保留 UV、用户属性和材质,不残留自身 `o_depth_*` 临时属性 + diff --git a/openspec/specs/reference-depth-calibration/spec.md b/openspec/specs/reference-depth-calibration/spec.md new file mode 100644 index 0000000..439a656 --- /dev/null +++ b/openspec/specs/reference-depth-calibration/spec.md @@ -0,0 +1,108 @@ +# reference-depth-calibration Specification + +## Purpose +TBD - created by archiving change unify-reference-depth. Update Purpose after archive. +## Requirements +### Requirement: Reference depth has one implementation +参考深度 SHALL 只由 `common/depth.py` 从 Depth 纹理计算。Server MUST NOT 计算或写出参考深度,`depth.json` SHALL 只包含 `image_size` 与 `intrinsics`。 + +#### Scenario: Write depth metadata +- **WHEN** Server 写出 `depth.json` +- **THEN** 文件包含 `image_size` 与 `intrinsics` +- **AND** 文件不包含 `reference_depth` + +#### Scenario: Load depth artifacts +- **WHEN** Blender 加载 Depth 纹理与元数据 +- **THEN** 参考深度由 Depth 纹理计算 +- **AND** 元数据只提供相机内参与图像尺寸 + +### Requirement: Depth texture alpha and depth values define the reference domain +参考深度 SHALL 只统计同时满足 `Alpha > 0.95`、深度有限且深度大于零的像素。系统 MUST NOT 使用 Cutout 的 Alpha Threshold 或选区透明度比例参与该统计。 + +#### Scenario: Exclude transparent and invalid pixels +- **WHEN** 某像素的 Alpha 不大于 `0.95`,或相机 Z 非有限、非正 +- **THEN** 该像素不参与参考深度统计 + +#### Scenario: Exclude semi-transparent source edges +- **WHEN** 源图 Alpha 与模型有效性的乘积落在 `0.5` 与 `0.95` 之间 +- **THEN** 该像素不参与参考深度统计 + +#### Scenario: Exclude everything +- **WHEN** 没有任何像素满足有效条件 +- **THEN** 参考深度取 `REFERENCE_DEPTH_BASELINE` + +### Requirement: Reference depth uses the upper depth percentile +参考深度 SHALL 取有效像素相机 Z 的 95 百分位,并以模型单位返回。所有深度几何 SHALL 使用同一判据,MUST NOT 按目标区分统计量。 + +#### Scenario: Distant background is dropped before the percentile +- **WHEN** 部分有效像素的距离超过有效像素中位数的 `REFERENCE_DEPTH_RANGE_FACTOR` 倍 +- **THEN** 这些像素不参与百分位统计 +- **AND** 参考深度只由该范围内的像素决定 + +#### Scenario: Keep a distant subject +- **WHEN** 全部有效像素都远于 `REFERENCE_DEPTH_BASELINE`,但仍在自身中位数的 `REFERENCE_DEPTH_RANGE_FACTOR` 倍以内 +- **THEN** 参考深度按这些像素的 95 百分位计算 +- **AND** 主体不会因为基准落在自身之前而被钳制 + +#### Scenario: Depth distribution is skewed +- **WHEN** 有效深度在前后景之间分布不均 +- **THEN** 参考深度取有效深度的 95 百分位 +- **AND** 结果不小于同一深度场的中位数 + +#### Scenario: Share the reference across depth shapes +- **WHEN** 同一 Depth 纹理分别用于 Depth Plane、Relief Plane、Depth Solid 与 Depth Symmetry +- **THEN** 四者使用相同的有效域规则与相同的百分位 + +#### Scenario: Selection has little transparency +- **WHEN** 参考域内几乎没有透明像素 +- **THEN** 系统仍按该参考域的有效像素计算 95 百分位 +- **AND** 不使用固定基准值 + +### Requirement: Depth direction fits fall back to a flat plane +深度方向拟合 SHALL 在没有可用样本时返回无倾斜的规范方向,MUST NOT 报错。Relief Plane 与 Depth Symmetry 的 `Direction` SHALL 因此回退为 `(0, 0, 1)`,调用方 MUST NOT 各自实现兜底。 + +#### Scenario: Fit a relief without usable depth samples +- **WHEN** 深度图没有可参与拟合的有效像素 +- **THEN** Relief Plane 仍成功创建 +- **AND** `Depth Direction` 为 `(0, 0, 1)` + +#### Scenario: Fit depth symmetry without usable samples +- **WHEN** 基础形状的采样点全部落在没有有效像素的区域 +- **THEN** Depth Symmetry 仍成功创建 +- **AND** `Direction` 为 `(0, 0, 1)` + +### Requirement: Cutout scopes the reference depth to its selection +Cutout SHALL 把当前选区作为参考域传入参考深度计算,并额外要求像素位于该选区内。Plane 转换 SHALL 使用整幅 Depth 纹理作为参考域。 + +#### Scenario: Selection covers part of an opaque image +- **WHEN** 源图没有透明区域且用户只选中局部主体 +- **THEN** 参考深度只统计选区内的有效像素 + +#### Scenario: Convert a whole image to a depth plane +- **WHEN** 用户把整幅图像转换为 Depth 或 Relief Plane +- **THEN** 参考深度统计整幅 Depth 纹理的有效像素 + +### Requirement: Reference depth anchors the uniform scale +参考深度 SHALL 作为 `Uniform Scale` 的归一化基准参与深度几何换算,MUST NOT 被预先乘以尺度。系统 MUST NOT 为参考深度新增用户参数。 + +#### Scenario: Convert model units to image plane units +- **WHEN** 系统创建深度几何 +- **THEN** 参考深度以模型单位参与 `Uniform Scale` 计算 +- **AND** 已缩放的参考深度写入 `Reference Depth` Modifier 输入 + +### Requirement: Alpha Threshold only controls the outline +Server MUST NOT 接收 Alpha Threshold 参数。Cutout 的 Alpha Threshold SHALL 只用于轮廓提取与内容有效性判定,MUST NOT 改变参考深度。 + +#### Scenario: Change alpha threshold +- **WHEN** 用户改变 Alpha Threshold 并重新生成深度几何 +- **THEN** 轮廓按新阈值变化 +- **AND** 参考深度保持不变 + +### Requirement: Relief Plane places its base plane by reference depth +`O Image Relief Plane` SHALL 使用默认值为 `0` 的 `Depth Offset`,基准平面由 95 百分位参考深度决定,`Depth Offset` 只保留微调职责。 + +#### Scenario: Create a relief plane +- **WHEN** 用户新建 Relief Plane +- **THEN** `Depth Offset` 为 `0` +- **AND** 基准平面位于参考深度 + diff --git a/openspec/specs/relief-plane-depth-controls/spec.md b/openspec/specs/relief-plane-depth-controls/spec.md new file mode 100644 index 0000000..5658ae0 --- /dev/null +++ b/openspec/specs/relief-plane-depth-controls/spec.md @@ -0,0 +1,77 @@ +# relief-plane-depth-controls Specification + +## Purpose +TBD - created by archiving change improve-relief-depth-controls. Update Purpose after archive. +## Requirements +### Requirement: Relief depth is clamped at the thickness height +`O Image Relief Plane` SHALL 将未置换可变面所在的 `Thickness` 高度定义为 depth 0。经过参考深度矫正、方向换算和 `Depth Scale` 得到的最终浮雕位移 MUST 不小于零,使可变面不进入固定底面与 `Thickness` 高度之间的基础厚度区域。 + +#### Scenario: Far depth would move below depth zero +- **WHEN** 矫正参考深度与采样深度之差经过方向换算和缩放后小于零 +- **THEN** 可变面的最终浮雕位移钳制为零 +- **AND** 可变面位于 `Thickness` 高度 + +#### Scenario: Positive relief depth +- **WHEN** 矫正参考深度与采样深度之差经过方向换算和缩放后大于零 +- **THEN** 可变面保留完整的正向浮雕位移 +- **AND** 正向位移不受 `Thickness` 大小限制 + +#### Scenario: Keep the fixed base +- **WHEN** `Thickness` 大于零且浮雕深度发生变化 +- **THEN** 固定底面的所有顶点保持在对象空间 `Z=0` +- **AND** 侧壁各层按其从固定底面到可变面的归一化位置线性应用最终浮雕位移 + +### Requirement: Relief Plane exposes a reference depth correction +`O Image Relief Plane` SHALL 在主界面公开 `Depth Offset` 距离输入,默认值为 `1` 并允许正负值。`Depth Direction` 与 `Depth Offset` SHALL 移出 Options,并在 `Thickness` 后按 `Depth Direction`、`Depth Offset`、`Depth Scale` 排列。系统 SHALL 先按 `Reference Depth + Depth Offset` 得到矫正参考深度,再进行 `Depth Direction` 参考平面换算、与缩放采样深度求差及 `Depth Scale` 缩放,最后把负位移钳制为零。 + +#### Scenario: Use the default offset +- **WHEN** `Depth Offset` 为默认值 `1` +- **THEN** 矫正参考深度等于 `Reference Depth + 1` +- **AND** 系统按现有方向换算和 `Depth Scale` 计算浮雕位移 +- **AND** 最终结果仍钳制在 depth 0 + +#### Scenario: Increase the reference depth correction +- **WHEN** 用户增加 `Depth Offset` +- **THEN** 系统在方向换算和 `Depth Scale` 之前增加矫正参考深度 +- **AND** Depth Offset 对最终位移的影响随 `Depth Scale` 一同缩放 + +#### Scenario: Decrease the reference depth correction +- **WHEN** 用户减小 `Depth Offset` +- **THEN** 系统在方向换算和 `Depth Scale` 之前减小矫正参考深度 +- **AND** 任何结果均不低于 depth 0 + +#### Scenario: Apply depth direction after correction +- **WHEN** `Depth Offset` 非零且用户改变 `Depth Direction` +- **THEN** 系统使用包含 Depth Offset 的矫正参考深度构建方向调整后的参考平面 +- **AND** 随后才计算相对深度和应用 `Depth Scale` + +### Requirement: Depth Scale uses depth zero as its invariant origin +`Depth Scale` SHALL 以 depth 0 为几何位移基准,缩放包含 `Depth Offset` 矫正的相对深度差,MUST 不缩放 `Thickness` 高度本身。 + +#### Scenario: Set depth scale to zero +- **WHEN** `Depth Scale` 为 `0` +- **THEN** 采样深度差、`Reference Depth` 和 `Depth Offset` 均不产生几何位移 +- **AND** 可变面位于 `Thickness` 高度 + +#### Scenario: Change depth scale +- **WHEN** 用户改变 `Depth Scale` 且保持 `Thickness`、`Reference Depth` 和 `Depth Offset` 不变 +- **THEN** 系统以 depth 0 为基准缩放包含 Depth Offset 矫正的采样深度差 +- **AND** `Thickness` 所在高度保持不变 + +### Requirement: Relief Plane initializes an approximate depth direction +系统 SHALL 在创建 Relief Plane 时,将完整深度图的有效像素映射到图片平面局部坐标,以 `Uniform Scale` 换算深度并进行稳健平面拟合,再用拟合平面的归一化方向初始化 `Depth Direction`。Relief Plane 与 Cutout Symmetry MUST 共用同一平面拟合实现。 + +#### Scenario: Create a relief from a sloped depth field +- **WHEN** 有效深度场包含可拟合的整体斜率 +- **THEN** 新建 Relief Plane 的 `Depth Direction` 使用拟合斜率对应的归一化方向 +- **AND** `Depth Offset` 保持默认值 `1` + +#### Scenario: Create a relief from a flat depth field +- **WHEN** 有效深度场整体为平面且没有斜率 +- **THEN** 新建 Relief Plane 的 `Depth Direction` 近似为 `(0, 0, 1)` + +#### Scenario: Create a relief without usable depth samples +- **WHEN** 深度图没有可用于拟合的正有限深度 +- **THEN** Relief Plane 仍然成功创建 +- **AND** `Depth Direction` 回退为 `(0, 0, 1)` + diff --git a/openspec/specs/selection-content-processing/spec.md b/openspec/specs/selection-content-processing/spec.md new file mode 100644 index 0000000..e036b48 --- /dev/null +++ b/openspec/specs/selection-content-processing/spec.md @@ -0,0 +1,143 @@ +# selection-content-processing Specification + +## Purpose +TBD - created by archiving change refine-image-output-semantics. Update Purpose after archive. +## Requirements +### Requirement: Refine preserves the submitted bounds canvas +系统 SHALL 让 BEN2 Refine 输出保持输入 RGBA 的完整矩形尺寸,并沿用提交前的 Selection bounds;Server MUST 不按生成 Alpha 紧裁切输出,也 MUST 不接收 Selection Mask。 + +#### Scenario: BEN2 output occupies only part of the bounds +- **WHEN** BEN2 只在输入矩形的一部分生成可见 Alpha +- **THEN** Refine 输出 SHALL 保留完整输入尺寸和透明区域,Blender SHALL 沿用提交前保存的 bounds + +#### Scenario: Refine Job reports its files +- **WHEN** `refine-image-selection` 或 Cutout artifact Job 完成 Refine +- **THEN** Job SHALL 返回完整尺寸的 Refine 图片,并且 MUST 不生成或返回 Refine 后的局部 bounds 文件 + +### Requirement: Refined content Alpha combines source and BEN2 +BEN2 Refine 内容 Alpha SHALL 等于 `Source Alpha × BEN2 Alpha`,并且 Selection Mask MUST 不参与 BEN2 推理或 Server 端 Refine 输出。 + +#### Scenario: Source contains transparent pixels +- **WHEN** BEN2 在源 Alpha 为零或半透明的像素生成前景 Alpha +- **THEN** Refine 图片 SHALL 通过 `preserve_input_alpha` 保持源 Alpha 与 BEN2 Alpha 的乘积 + +#### Scenario: Selection excludes visible content inside bounds +- **WHEN** bounds 内存在位于 Selection Mask 外的可见内容 +- **THEN** BEN2 与 Server Refine 输出 SHALL 仍能使用并保留该内容,不得预先乘入 Selection Mask + +### Requirement: Cutout textures share refined content scope +启用 Refine Selection 的 Cutout SHALL 使用同一份 `Source Alpha × BEN2 Alpha` 内容生成 Color、Normal 与 Depth 贴图,Selection Mask MUST 不限制这些贴图的生成。 + +#### Scenario: Cutout generates color and normal +- **WHEN** Cutout 同时启用 Refine Selection 与 Normal Map +- **THEN** Color Alpha 与 Normal 可见区域 SHALL 来自完整 Refine 图片 Alpha,不得乘入 Selection Mask + +#### Scenario: Cutout generates depth +- **WHEN** Refined Cutout 生成 Vector 或 Z Depth +- **THEN** MoGe-2 黑底输入和 Depth Metadata 参考内容范围 SHALL 来自完整 Refine 图片 Alpha,不得乘入 Selection Mask + +#### Scenario: Depth texture preserves model validity +- **WHEN** 系统写出 Vector 或 Z Depth EXR +- **THEN** RGB SHALL 保留完整连续 Field,Alpha SHALL 保存 MoGe-2 Validity,不得用 Refine Alpha 或 Selection Mask 覆盖 + +### Requirement: Cutout mesh combines refined content with Selection +Cutout SHALL 只在 Blender 网格构建阶段计算 `Source Alpha × BEN2 Alpha × Selection Mask`,并使用该结果约束 BaseShape、内容空值检查和网格标定。 + +#### Scenario: BEN2 keeps content outside the user Selection +- **WHEN** BEN2 在 Selection bounds 内保留了 Selection Mask 外的前景 +- **THEN** Color、Normal 与 Depth SHALL 保留该内容,但最终 Cutout 网格 MUST 不包含 Selection Mask 外的区域 + +#### Scenario: Combined Cutout content is empty +- **WHEN** Refine Alpha 与 Selection Mask 相乘后没有超过 Cutout Alpha 阈值的像素 +- **THEN** Cutout SHALL 报告空内容且不创建网格 + +#### Scenario: Cutout generates only Normal or Depth +- **WHEN** Cutout 未启用 Refine、但因 Normal 或 Depth 进入 Job +- **THEN** 贴图 SHALL 使用完整 bounds 源 Alpha,网格 SHALL 使用 `Source Alpha × Selection Mask` + +### Requirement: Crop Refine applies Selection in Blender +Crop Refine SHALL 在 Blender 中计算 `Source Alpha × BEN2 Alpha × Selection Mask` 作为最终图片 Alpha,同时保持提交前的矩形尺寸和 placement bounds。 + +#### Scenario: BEN2 keeps pixels outside a Crop Selection +- **WHEN** BEN2 在 Crop Selection Mask 外生成可见 Alpha +- **THEN** 最终 Crop 图片 SHALL 将这些像素 Alpha 置为零,但 MUST 不改变提交前 bounds + +#### Scenario: Crop uses one rasterized Selection +- **WHEN** Crop 提交 Refine Job 并异步接收结果 +- **THEN** 系统 SHALL 复用提交阶段生成的 Selection Mask,不得在响应阶段重新栅格化 SelectionPath,也不得把 Mask 发送给 Server + +#### Scenario: Combined Crop content is empty +- **WHEN** Refine Alpha 与 Selection Mask 相乘后没有可见像素 +- **THEN** Crop SHALL 报告空内容且不创建结果 Image Empty + +### Requirement: Selection Mask is independent of image content +系统 SHALL 仅根据 Selection Path、Invert、图片尺寸与抗锯齿设置栅格化 Selection Mask,并且 Selection 栅格化不得读取、接收或合并源图片 Alpha、颜色或 Alpha threshold。 + +#### Scenario: Images have different Alpha +- **WHEN** 相同 Selection Path、图片尺寸与抗锯齿设置用于透明度内容不同的图片 +- **THEN** 系统产生逐值相同且 bounds 相同的 Selection Mask + +#### Scenario: Selection is inverted +- **WHEN** Selection Path 启用 Invert +- **THEN** 系统在完整图片画布上产生路径外的纯几何 Selection Mask,而不检查源图片可见内容 + +### Requirement: Image businesses combine source Alpha explicitly +系统 SHALL 由 Image Tool 与本地 Cutout 在实际业务阶段显式组合 Selection Mask 值和对应 Bounds 内的源 Alpha;Selection Mask MUST 在组合前后保持纯几何值不变。 + +#### Scenario: Image Tool edits a Selection +- **WHEN** 用户提交未启用 Refine 的 Image Tool Selection +- **THEN** Image Tool 在几何 bounds 内把 Selection 值乘入源 Alpha,并由编辑业务计算内容 bounds、空结果和最终图片 + +#### Scenario: Local Cutout builds a Shape +- **WHEN** 用户选择不需要 AI 的 Cutout Shape +- **THEN** Cutout 在构建 Shape 时组合局部源 Alpha 与 Selection 值,并使用业务结果约束几何和判断空内容 + +#### Scenario: Source content is transparent +- **WHEN** 几何 Selection 有效但对应源 Alpha 没有可见内容 +- **THEN** 消费该内容的 Image 或 Cutout 业务报告空内容,Selection 栅格化本身仍成功 + +### Requirement: AI receives one rectangular Bounds image +系统 SHALL 为需要 AI 的 Image Tool 或 Cutout 按 Selection 几何 bounds 写出一份矩形局部 RGBA 文件,并且该文件 MUST 保留 bounds 内的源 Alpha,不得预先乘入 Lasso Selection 值。 + +#### Scenario: Refine receives Lasso context +- **WHEN** 用户对 Lasso Selection 启用 Refine +- **THEN** BEN2 接收 Lasso 几何 bounds 对应的完整矩形 RGBA,矩形内、Lasso 外的源像素保持原样 + +#### Scenario: Normal or Depth requires AI +- **WHEN** Cutout 仅因 Normal 或 Depth 进入 AI 流程 +- **THEN** Job 使用与 Refine 相同的 Bounds 矩形输入协议,不建立另一套 Masked Selection 输入 + +#### Scenario: Blender Image source varies +- **WHEN** 源 Image 来自文件、Packed 数据或 Blender 内存 +- **THEN** Operator 通过同一个 Bounds 图片序列化入口产生 AI 输入,不按来源建立不同业务主链 + +### Requirement: Refine may expand within Selection Bounds +系统 SHALL 将 Refine 的 Lasso 解释为矩形上下文范围,而不是 BEN2 输出的硬 Mask;最终 Refine Alpha SHALL 由 BEN2 在该矩形内的结果决定。 + +#### Scenario: BEN2 preserves content outside Lasso +- **WHEN** BEN2 在 Selection bounds 内保留了 Lasso 路径外的像素 +- **THEN** 系统保留这些像素,不再与原始 Lasso Selection Mask 相交 + +#### Scenario: BEN2 returns no visible content +- **WHEN** Refine 输出在矩形 bounds 内没有可见 Alpha +- **THEN** Refine 业务报告空内容且不创建 Image 或 Cutout 结果 + +### Requirement: AI operations reuse encoded local images +系统 SHALL 复用 AI 流程中已经编码的局部 RGBA 文件作为最终颜色 Image,不得重新读取完整源图并创建包含相同颜色内容的新内存 Image 后再次编码。 + +#### Scenario: Image Tool Refine completes +- **WHEN** BEN2 返回已收紧 bounds 的 Refine RGBA 文件 +- **THEN** Image Tool 直接加载、配置并 Pack 该文件,然后按响应 bounds 替换源 Image Empty + +#### Scenario: Cutout Refine completes +- **WHEN** Cutout 的 BEN2 Refine 返回局部 RGBA 文件 +- **THEN** Cutout 使用该文件的 Alpha 构建结果几何,并使用同一个已加载 Image 作为颜色纹理 + +#### Scenario: Cutout generates only Normal or Depth +- **WHEN** Cutout AI 流程没有 Refine 输出 +- **THEN** Cutout 直接加载并 Pack 提交前的 Bounds 输入作为颜色纹理 + +#### Scenario: AI operation cleans up +- **WHEN** 已有输入或 Refine 输出成功加载并 Pack,或者操作取消 +- **THEN** 系统按 Job 生命周期清理临时文件,且 Blender 结果不再依赖临时路径 + diff --git a/openspec/specs/selection-rasterization/spec.md b/openspec/specs/selection-rasterization/spec.md new file mode 100644 index 0000000..67c0b4f --- /dev/null +++ b/openspec/specs/selection-rasterization/spec.md @@ -0,0 +1,68 @@ +# selection-rasterization Specification + +## Purpose +TBD - created by archiving change simplify-selection-rasterization. Update Purpose after archive. +## Requirements +### Requirement: Selection uses one overlap-preserving fill +系统 SHALL 使用 non-zero winding 栅格化所有 Selection Path,使同方向自交或重复绕行的重叠区域保持选中,并且不得暴露可切换的填充规则。 + +#### Scenario: Lasso overlaps itself +- **WHEN** Image Tool 或 Cutout 的 Selection 路径以同一方向重复包围一个区域 +- **THEN** 该重叠区域在 Viewport 预览和最终 Selection Mask 中均保持选中 + +#### Scenario: Simple path is selected +- **WHEN** Selection 路径是普通矩形、半平面、凹多边形或不自交 Lasso +- **THEN** 系统产生与既有 non-zero 填充相同的选中区域 + +### Requirement: Selection Path contains its inversion state +系统 SHALL 使用只包含普通二维点数组和 Invert 的不可变 Selection Path 值在用户手势与实际 Image 或 Cutout 操作之间传递 Selection;该类型 SHALL 保留完整值的 JSON 编解码,但不得包含填充规则。 + +#### Scenario: Image Tool submits a Selection +- **WHEN** 用户完成 Image Tool 手势 +- **THEN** 同一 Operator 把包含 Points 与 Invert 的 Selection Path 直接交给实际图片编辑 + +#### Scenario: Cutout menu creates Shape operations +- **WHEN** Cutout Pie Menu 为当前点数组创建 Shape 按钮 +- **THEN** 菜单将包含 Points 与 Invert 的完整 Path 编码一次,并为每个 Shape Operator 复用同一字符串 + +#### Scenario: Cutout Shape operation restores the Selection +- **WHEN** 用户点击任一 Cutout Shape 按钮 +- **THEN** Shape Operator 从单一 Path JSON 恢复 Points 与 Invert 后再进行栅格化 + +### Requirement: Selection rasterization uses scanline events +系统 SHALL 通过路径边与扫描行的交点事件构建 non-zero winding Mask,每条路径边不得重复创建扫描行块乘以 Selection 宽度的二维像素比较数组。 + +#### Scenario: High-resolution Lasso is rasterized +- **WHEN** 系统处理高分辨率图片上的多点 Lasso +- **THEN** 每条边只为实际跨越的扫描行产生端点事件,所有端点完成后每行执行一次水平累计 + +#### Scenario: Path point count increases +- **WHEN** Selection 画布不变而路径点数增加 +- **THEN** 栅格器增加交点事件处理,不按每个新增点重新扫描完整二维 Selection 像素区域 + +### Requirement: Non-inverted Selection processing stays local +系统 SHALL 对非 Invert Selection 只在裁切后的路径范围内执行填充、抗锯齿、源 Alpha 合并和可见 bounds 计算;抗锯齿范围 MUST 包含固定卷积核所需的边缘 padding。 + +#### Scenario: Local Selection is rasterized +- **WHEN** 非 Invert Selection 只覆盖源图的一部分 +- **THEN** 中间 Mask 和 Alpha 运算范围限制在路径 bounds 及必要的抗锯齿 padding + +#### Scenario: Inverted Selection is rasterized +- **WHEN** Selection 启用 Invert +- **THEN** 系统使用完整图片范围表达路径外区域,但仍只累计一次 scanline 事件 + +### Requirement: Optimized rasterization preserves Mask semantics +系统 MUST 保持现有像素中心判定、Invert、抗锯齿核、Alpha 阈值、空 Selection 警告和最终局部 Selection Mask bounds 语义。 + +#### Scenario: Selection has source Alpha +- **WHEN** Selection Path 与源图 Alpha 一起栅格化 +- **THEN** 系统只保留 Alpha 高于阈值且 non-zero winding Mask 有效的像素,并返回其紧致 bounds + +#### Scenario: Selection has no visible pixels +- **WHEN** Selection 与图片范围或可见源 Alpha 没有交集 +- **THEN** 系统报告空 Selection,且不向后续 Image、Cutout 或 AI 操作提交结果 + +#### Scenario: Equivalent Selection is processed +- **WHEN** 相同 Selection Path、源 Alpha、阈值和抗锯齿设置由参考实现与优化实现处理 +- **THEN** 两者产生逐值一致的 Mask values 和相同 bounds + diff --git a/openspec/specs/separate-depth-relief-planes/spec.md b/openspec/specs/separate-depth-relief-planes/spec.md new file mode 100644 index 0000000..4de69a7 --- /dev/null +++ b/openspec/specs/separate-depth-relief-planes/spec.md @@ -0,0 +1,53 @@ +# separate-depth-relief-planes Specification + +## Purpose +TBD - created by archiving change split-depth-and-relief-planes. Update Purpose after archive. +## Requirements +### Requirement: Independent conversion entries and assets +系统 SHALL 提供 Convert to Depth Plane 和 Convert to Relief Plane 两个入口,分别加载 `O Image Depth Plane` 与 `O Image Relief Plane`。Depth Plane MUST 使用相机投影,Relief Plane MUST 使用固定底面浮雕。两组 SHALL 提供各自参数并移除 Mode 选择。 + +#### Scenario: Convert an image to a depth plane +- **WHEN** 用户对静态 Image Empty 执行 Convert to Depth Plane +- **THEN** 系统创建相机投影曲面,绑定独立 Depth Plane 节点组,并保留原图构图、UV 与世界位置 + +#### Scenario: Convert an image to a relief plane +- **WHEN** 用户对静态 Image Empty 执行 Convert to Relief Plane +- **THEN** 系统创建固定底面浮雕,绑定独立 Relief Plane 节点组,Thickness 与 Depth Scale 延续原 Relief 语义 + +#### Scenario: Inspect modifier controls +- **WHEN** 用户查看两种结果的修改器 +- **THEN** 两者各自提供 Subdivide、Thickness、Depth Scale、Reference Depth,Depth Plane 提供 Normal Smooth,两者均无 Mode 选择 + +### Requirement: Normal space follows conversion type +系统 SHALL 为 Depth Plane 生成并消费 `object_normal`,材质使用 OBJECT 空间;为 Relief Plane 生成并消费 `tangent_normal`,材质使用 TANGENT 空间。法线编码 MUST 与对应对象局部坐标一致。 + +#### Scenario: Depth material uses object normals +- **WHEN** Depth Plane Job 完成并创建材质 +- **THEN** 材质加载 Object Normal 产物并启用 Object Space,法线轴向与相机投影后的局部几何一致 + +#### Scenario: Relief material uses tangent normals +- **WHEN** Relief Plane Job 完成并创建材质 +- **THEN** 材质加载 Tangent Normal 产物并使用 Tangent Space + +### Requirement: Shared conversion lifecycle +两个转换 SHALL 使用静态图与 AI 就绪检查、统一深度产物协议、对象替换及失败清理流程。新增 Operator MUST 纳入注册与逆序注销。 + +#### Scenario: Failed result creation +- **WHEN** 任一转换在加载产物或创建对象时失败 +- **THEN** 系统保留可用源对象,并清理本次创建且未被使用的数据块 + +#### Scenario: Extension lifecycle +- **WHEN** 扩展注册后逆序注销 +- **THEN** 两个转换入口均正确注册与移除 + +### Requirement: Depth plane shares Cutout smoothing +Depth Plane SHALL 在投影与厚度之后复用 Depth Cutout 的 Smooth 模块。Smooth MUST 默认 0、范围 0–20;Options 中的 Smooth Weight MUST 默认 1、范围 0–1,使用 FACTOR 子类型。 + +#### Scenario: Smooth final geometry +- **WHEN** 用户增加 Smooth 并使用正 Smooth Weight +- **THEN** 最终顶点位置按 Cutout 的共用规则平滑,面连接与 UV 保持一致 + +#### Scenario: Disable smoothing +- **WHEN** Smooth 为 0 或 Smooth Weight 为 0 +- **THEN** 几何位置与未平滑结果相同 + diff --git a/openspec/specs/server-cache-display/spec.md b/openspec/specs/server-cache-display/spec.md new file mode 100644 index 0000000..f2d092e --- /dev/null +++ b/openspec/specs/server-cache-display/spec.md @@ -0,0 +1,20 @@ +# server-cache-display Specification + +## Purpose +TBD - created by archiving change show-server-controls-while-running. Update Purpose after archive. +## Requirements +### Requirement: Cached model list layout +Server 面板 SHALL 显示所有 Cached 模型名称,并 SHALL 在存在多个模型时让每个模型名称占据独立的视觉行。 + +#### Scenario: Multiple models are cached +- **WHEN** Server 为 Running 且 Cached 列表包含两个或更多模型 +- **THEN** 面板逐行显示每个模型名称,并保留缓存清理按钮 + +#### Scenario: One model is cached +- **WHEN** Server 为 Running 且 Cached 列表只包含一个模型 +- **THEN** 面板在一行内显示 `Cached` 与该模型名称,并保留缓存清理按钮 + +#### Scenario: No model is cached +- **WHEN** Server 为 Running 且 Cached 列表为空 +- **THEN** 面板显示 `Cached: None`,并保留缓存清理按钮 + diff --git a/openspec/specs/server-lifecycle-controls/spec.md b/openspec/specs/server-lifecycle-controls/spec.md new file mode 100644 index 0000000..26dfada --- /dev/null +++ b/openspec/specs/server-lifecycle-controls/spec.md @@ -0,0 +1,20 @@ +# server-lifecycle-controls Specification + +## Purpose +TBD - created by archiving change show-server-controls-while-running. Update Purpose after archive. +## Requirements +### Requirement: Running Server lifecycle controls +Server 面板 SHALL 在 Server 的每个 Running 子状态下显示重启和停止控制,并 SHALL 保留该子状态对应的状态文本与图标。 + +#### Scenario: Server is running and waiting +- **WHEN** Server 状态为 `READY` +- **THEN** 面板显示 Running 状态、重启按钮和停止按钮 + +#### Scenario: Server is running and busy +- **WHEN** Server 状态为 `BUSY` +- **THEN** 面板显示 Running (Busy) 状态、重启按钮和停止按钮 + +#### Scenario: Server is not running +- **WHEN** Server 状态不是 Running 子状态 +- **THEN** 面板不显示 Running 状态的重启和停止按钮,并沿用该状态对应的现有控制或提示 + diff --git a/openspec/specs/shell-normal-smoothing/spec.md b/openspec/specs/shell-normal-smoothing/spec.md new file mode 100644 index 0000000..f60b57b --- /dev/null +++ b/openspec/specs/shell-normal-smoothing/spec.md @@ -0,0 +1,23 @@ +# shell-normal-smoothing Specification + +## Purpose +TBD - created by archiving change compose-depth-cutout-symmetry. Update Purpose after archive. +## Requirements +### Requirement: 薄壳法线平滑不受相对厚度衰减 + +Shell 的 Normal Smooth SHALL 控制原正面点域法线的模糊次数,归一化后的结果用于壳体厚度偏移方向。系统 SHALL 不使用 Shell Thickness / Reference Depth 衰减该平滑;厚度控制偏移距离,Balloon 保留现有权重行为。 + +#### Scenario: 小厚度下调节平滑 +- **WHEN** 非平面且法线有起伏的正面以小厚度生成 Shell,用户增大 Normal Smooth +- **THEN** 壳体的归一化偏移方向发生相应平滑变化,且不会因厚度与参考深度的比值接近零而退回原始法线 +- **AND** 保持前表面相同而改变厚度时,平滑方向保持一致、偏移距离随厚度变化 + +### Requirement: 法线平滑保留正面与零厚度行为 + +Shell 的 Normal Smooth SHALL 仅通过厚度偏移作用于壳体,保留原深度正面;零厚度 SHALL 不因该参数改变几何。 + +#### Scenario: 零厚度与平面 +- **WHEN** Shell 厚度为 0 时改变 Normal Smooth +- **THEN** 顶点位置与面连接保持一致 +- **AND** 对常法线平面使用非零厚度时,改变 Normal Smooth 也不改变壳体形状 + diff --git a/openspec/specs/texture-node-image-actions/spec.md b/openspec/specs/texture-node-image-actions/spec.md new file mode 100644 index 0000000..cf7a4e3 --- /dev/null +++ b/openspec/specs/texture-node-image-actions/spec.md @@ -0,0 +1,85 @@ +# texture-node-image-actions Specification + +## Purpose +TBD - created by archiving change add-texture-node-image-actions. Update Purpose after archive. +## Requirements +### Requirement: Material image texture context actions + +系统 SHALL 在材质 Shader Editor 中为具有有效图片的活动 Image Texture 节点提供 AnyImage 右键菜单,包含 Remove Background 和 Upscale;一次操作 SHALL 处理当前节点。 + +#### Scenario: An image texture is active +- **WHEN** 用户在材质编辑上下文中对有图片的活动 Image Texture 节点打开右键菜单 +- **THEN** 菜单提供两项操作,Upscale 显示预期 2× 输出尺寸 + +#### Scenario: The target is unavailable +- **WHEN** 当前上下文为 World、Compositor、Geometry Nodes,或活动节点不是 Image Texture、没有图片、不可编辑 +- **THEN** 系统不提供可执行的节点图片操作,且不使用活动 Empty 作为替代目标 + +#### Scenario: A material or node group is being edited +- **WHEN** 用户固定材质或进入其节点组并对有效 Image Texture 发起操作 +- **THEN** 系统处理该编辑树内的目标节点图片 + +### Requirement: Existing AI processing settings apply + +系统 SHALL 沿用 Empty Image 操作的 AI 环境准备、模型选择、任务状态和输入限制;Upscale SHALL 使用当前模型输出 2× 结果。 + +#### Scenario: Environment preparation is needed +- **WHEN** 用户点击操作且 AI 环境或模型尚未准备好 +- **THEN** 系统进入现有准备流程 + +#### Scenario: Upscale is unavailable +- **WHEN** 服务忙碌或图片长边达到当前最大 AI 输入尺寸 +- **THEN** Upscale 不可执行;服务忙碌时 Remove Background 也不可执行 + +#### Scenario: An animated texture is processed +- **WHEN** 目标图片为现有流程支持的视频或序列 +- **THEN** 系统依据节点播放设置和现有处理帧数约定取得输入,结果序列保留对应播放时间位置 + +### Requirement: Results replace the image at the target node + +系统 SHALL 在成功后保留目标节点、位置、连线和纹理设置,并在该节点使用结果图片。源图独占时 SHALL 保留 Image 身份、名称与 Fake User;源图有其他实际使用者时 SHALL 仅替换当前节点图片绑定并保持其他使用者和原图不变。结果 SHALL 继承源色彩空间、正确保存透明信息并支持保存重开。 + +#### Scenario: A sole image is processed +- **WHEN** 目标节点独占源图并完成去背景或放大 +- **THEN** 原 Image 内容及尺寸更新为结果,身份和名称保留,临时结果被释放 + +#### Scenario: Other users share the image +- **WHEN** 另一纹理节点或 Empty 等数据使用者仍引用源图 +- **THEN** 当前节点使用独立处理结果,其他使用者继续使用未改变的原图 + +#### Scenario: Only a fake user is present +- **WHEN** 源图除当前节点外只有 Fake User 或编辑器显示引用 +- **THEN** 系统保留源 Image 身份并更新内容 + +#### Scenario: Background removal succeeds +- **WHEN** 用户对节点执行 Remove Background 成功 +- **THEN** 图片带有处理后的透明信息,材质原有连线和渲染设置保留 + +### Requirement: Asynchronous completion keeps the original target + +系统 SHALL 将结果提交到发起任务时的节点,并在提交前验证原节点和图片绑定仍有效。 + +#### Scenario: Active selection changes +- **WHEN** 处理期间用户切换编辑器、材质或活动节点 +- **THEN** 结果仍提交到原节点 + +#### Scenario: The target is renamed +- **WHEN** 原节点或所属材质在任务期间改名且绑定仍有效 +- **THEN** 结果提交到同一原节点 + +#### Scenario: The original target is invalid +- **WHEN** 原节点被删除、同名重建、变为只读或改绑图片 +- **THEN** 系统报告目标失效,保留现有节点和图片状态,并清理本次结果 + +### Requirement: Edits support failure recovery and undo + +系统 SHALL 在失败或取消时保持或恢复原图片内容、节点绑定和播放设置,并清理临时资源。成功操作 SHALL 支持一次撤销还原及重做恢复。 + +#### Scenario: Commit fails +- **WHEN** 写入图片内容或绑定、配置播放设置期间发生错误 +- **THEN** 节点绑定与原图完整恢复,其他使用者保持不变 + +#### Scenario: The user undoes and redoes an edit +- **WHEN** 用户撤销一次成功操作,然后重做 +- **THEN** 撤销恢复原图、尺寸、绑定和播放设置,重做恢复处理结果且无需再次推理 + diff --git a/openspec/specs/two-times-upscale/spec.md b/openspec/specs/two-times-upscale/spec.md new file mode 100644 index 0000000..76fb4b8 --- /dev/null +++ b/openspec/specs/two-times-upscale/spec.md @@ -0,0 +1,78 @@ +# two-times-upscale Specification + +## Purpose +TBD - created by archiving change add-pisa-sr-two-times-upscale. Update Purpose after archive. +## Requirements +### Requirement: Unified two-times output + +系统 SHALL 对全部 Upscale 模型返回处理输入宽高各 2 倍的结果,并将该倍率用于界面说明和尺寸计算;处理输入为应用已有最大输入尺寸限制后的图像。 + +#### Scenario: Existing models produce two-times images +- **WHEN** 任一现有模型处理 W×H 输入 +- **THEN** 系统先得到原生 4W×4H 结果,再使用 Lanczos 缩小到 2W×2H +- **AND** 使用已有 4× 权重 + +#### Scenario: Repeated upscale +- **WHEN** 用户对一次 2× 结果再次执行 Upscale 且输入尺寸仍满足限制 +- **THEN** 新结果宽高各为本次输入的 2 倍,即最初输入的 4 倍 + +### Requirement: Fixed PiSA-SR preset + +系统 SHALL 提供 `PISA_SR` 模型选项,使用固定像素强度 1.0、语义强度 0.7、空提示词和 seed 42,通过 ONNX Runtime 生成 2× 结果。现有默认模型 SHALL 保持有效。 + +#### Scenario: Selected preset is executed +- **WHEN** 用户选择 PiSA-SR 并执行 Upscale +- **THEN** 模型对同一 latent 的预测按 `pixel + 0.7 * (full - pixel)` 组合 +- **AND** 输入插值、采样、解码与颜色校正遵循已验证的实验基线 + +#### Scenario: Repeatable inference +- **WHEN** 相同图像在相同运行环境重复执行 PiSA-SR +- **THEN** 系统使用相同噪声和处理规则,结果在该设备的数值容差内一致 + +### Requirement: Arbitrary dimensions and alpha preservation + +系统 SHALL 支持合法输入范围内的小图、非方形图、奇数尺寸和跨 tile 图像,输出精确 2× 尺寸;有 alpha 的生产输入 SHALL 保留由源 alpha 插值而来的透明度。 + +#### Scenario: Tiled PiSA-SR image +- **WHEN** 输入需要多个 PiSA-SR tile +- **THEN** 系统融合重叠区域并裁去填充,输出无多余边缘的 2W×2H 图像 +- **AND** 连续纹理与渐变测试图的 tile 边界不出现可辨识拼接线 + +#### Scenario: Transparent input +- **WHEN** 输入包含全透明和半透明像素 +- **THEN** 输出 alpha 等于源 alpha 直接 Lanczos 放大到最终尺寸的结果 +- **AND** RGB 生成过程不生成或覆盖 alpha + +### Requirement: Model readiness and resource lifecycle + +系统 SHALL 对 PiSA-SR 全部必需资产完成下载及校验后才判定就绪,按所选设备创建管线;其模型切换、取消、资源错误和显式清理 SHALL 覆盖全部组件。 + +#### Scenario: Incomplete model package +- **WHEN** PiSA-SR 任一必需文件缺失或校验失败 +- **THEN** 系统提示准备模型并阻止使用不完整管线进行推理 + +#### Scenario: Bounded UNet residency +- **WHEN** PiSA-SR 执行像素与完整预测阶段 +- **THEN** GPU 同时常驻的 UNet 至多一套,阶段间中间数据被保留以完成 0.7 组合 + +#### Scenario: Cancel or resource failure +- **WHEN** 推理被取消或发生资源错误 +- **THEN** 系统停止后续阶段并清理任务中间文件 +- **AND** 资源错误释放整个 PiSA-SR 管线并报告可理解的失败原因 + +#### Scenario: Unsupported device +- **WHEN** 所选设备无法执行已分发的 PiSA-SR 资产 +- **THEN** 系统报告明确的设备支持原因或执行已验证并明确呈现的回退路径 + +### Requirement: Consistent job behavior + +系统 SHALL 在生产与调试任务采用同一模型预设和最终倍率,并对多帧输入逐帧应用该约定。 + +#### Scenario: Multiple frames +- **WHEN** Upscale 处理多帧输入 +- **THEN** 每帧输出均为其处理输入的 2× 尺寸,帧顺序正确且阶段之间可以取消 + +#### Scenario: Debug matches production +- **WHEN** 相同 RGB 输入、模型和设备分别通过生产与 Debug 入口处理 +- **THEN** 两者的 RGB 处理规则和最终尺寸一致 + diff --git a/openspec/specs/unified-depth-plane/spec.md b/openspec/specs/unified-depth-plane/spec.md new file mode 100644 index 0000000..db824bf --- /dev/null +++ b/openspec/specs/unified-depth-plane/spec.md @@ -0,0 +1,62 @@ +# unified-depth-plane Specification + +## Purpose +TBD - created by archiving change unify-depth-plane-and-artifact. Update Purpose after archive. +## Requirements +### Requirement: One conversion exposes two geometry modes + +系统 SHALL 仅提供 Convert to Depth Plane 整图深度入口,使用 `anyimage.convert_to_depth_plane` 与资产组 `O Image Depth Plane`。Mode SHALL 提供 Relief 与 Camera,默认 Relief。独立 Depth Surface 入口、注册类和公开资产组 SHALL 删除。 + +#### Scenario: Convert and switch modes +- **WHEN** 静态 Image Empty 完成转换后,用户在修改器切换 Relief / Camera +- **THEN** 同一对象和深度图片产生对应几何,不重新推理、不创建新深度图片 +- **AND** 两种模式共享并保留已有控制值 + +#### Scenario: Preserve conversion lifecycle +- **WHEN** 转换成功或失败 +- **THEN** 成功时沿用源名称、替换源对象且一次 Undo 可恢复;失败时清理部分结果并保留源对象 +- **AND** AI 环境检查和静态媒体限制继续生效 + +### Requirement: Unified modifier has a stable interface + +接口 SHALL 依次为 Geometry、Mode、Subdivide、两个独立的 Thickness、Depth Scale、Options 内 Reference Depth 与 Normal Smooth、Data 内 Uniform Scale 与 Depth Image。两个 Thickness SHALL 同名显示,依次控制 Relief 与 Camera,默认均为 0 m、最小 0,使用不同 identifier 独立保存。Subdivide SHALL 默认为 6、范围 0–10;Depth Scale 默认为 1、最小 0;Normal Smooth 默认为 50、范围 0–50。Reference Depth SHALL 由元数据基准深度乘以 Uniform Scale 初始化。Data 输入 SHALL 隐藏;所有输入 SHALL 使用 SINGLE,长度使用 DISTANCE。 + +#### Scenario: Inspect mode controls +- **WHEN** 创建或切换修改器模式 +- **THEN** 接口顺序和默认值符合契约,Normal Smooth 只影响 Camera 的厚度法线 +- **AND** 整图模式不提供 Split 或 Cutout 轮廓控制 + +#### Scenario: Keep mode thickness independent +- **WHEN** 在一种模式中修改另一模式对应的 Thickness,并切换模式后返回 +- **THEN** 当前模式几何保持不变,两个厚度值分别保留 + +### Requirement: Relief preserves the fixed base + +Relief SHALL 读取 B/Z 深度,保持基础 XY、固定底面、现有侧壁插值和负位移限制。Thickness SHALL 表示浮雕的基础厚度。 + +#### Scenario: Evaluate relief +- **WHEN** 用与旧标量输入相同的 Z、不同的 X/Y 求值 Relief +- **THEN** 顶点位置、面连接、UV 与材质和原 Depth Plane 结果一致 + +### Requirement: Camera preserves the projected shell + +Camera SHALL 使用完整矩形细分;相机 XYZ 乘以 Uniform Scale 后得到 C,XY SHALL 按 Depth Scale 从基础 XY 插值到 `(C.x, -C.y)`,Z SHALL 为 `(Reference Depth - C.z) × Depth Scale`。零 Thickness SHALL 输出单层;正 Thickness SHALL 沿平滑归一化后的正面 POINT 法线向内生成背面并闭合边界。 + +#### Scenario: Match the ordinary plane +- **WHEN** Camera 的 Thickness 和 Depth Scale 均为 0 +- **THEN** 相同 Subdivide 下几何与普通 Plane 一致,包括非中心显示偏移和对象变换后的世界位置 + +#### Scenario: Project and thicken +- **WHEN** 使用非中心主点合成数据,Depth Scale 为 0、0.5、1 或 2,Normal Smooth 为 0、2 或 50 +- **THEN** 投影符合公式;正 Thickness 的对应正背面距离等于 Thickness,0 次平滑使用原始法线 +- **AND** 透明区域与深度跳变区域保持完整矩形连接 + +### Requirement: Asset evaluation preserves geometry data + +统一组 SHALL 从资产按名称加载,保留 UV、材质和用户属性,并清理自身临时属性。定义和保存资产 SHALL 满足相同接口与几何契约,布局 SHALL 在 Blender 中按实际绘制尺寸检查。 + +#### Scenario: Validate the delivered asset +- **WHEN** 加载更新后的节点资产 +- **THEN** 公开组清单包含统一 Depth Plane 且没有独立 Depth Surface,两种模式通过几何求值 +- **AND** 总览和局部步骤没有节点遮挡,未用输出隐藏,主轴与支线清楚 + diff --git a/pyproject.toml b/pyproject.toml index 5e21ebc..d2e53a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "anyimage" -version = "0.13.3" +version = "1.0.0" description = "Fast image-guided structure building tools for Blender" readme = "README.md" requires-python = ">=3.11,<3.12" diff --git a/src/anyimage/blender_manifest.toml b/src/anyimage/blender_manifest.toml index 3906755..008ec33 100644 --- a/src/anyimage/blender_manifest.toml +++ b/src/anyimage/blender_manifest.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "anyimage" -version = "0.13.3" +version = "1.0.0" name = "AnyImage" tagline = "Build spatial structures from images, fast" maintainer = "OmooLab" diff --git a/uv.lock b/uv.lock index c172d20..30c77e4 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "anyimage" -version = "0.13.3" +version = "1.0.0" source = { editable = "." } dependencies = [ { name = "blendjob" },