Implement Phase 10a - Prop Scaling
- Added specifications for Phase 10a, introducing permanent size changes to `.prop` documents and reversible uniform instance scaling in Sandbox EDIT mode. - Updated `PropCanvas`, `PropAnchorData`, `PropEditor`, `StageGizmos`, `SandboxStage`, and `StageSelection` classes to support new scaling features. - Implemented document transactions for scaling, including normalization to a 96 px maximum dimension. - Introduced a new **Transform Whole Prop** tool in the Prop Editor for moving, rotating, and resizing props. - Created unit tests for the new scaling functionality, validating shape scaling, anchor transformations, and whole-prop interactions. - Ensured compatibility with existing prop formats and maintained the integrity of the document structure during scaling operations.
This commit is contained in:
@@ -242,6 +242,13 @@ preview_area.size * 0.5`, so it also re-centers on window resize; it remains a
|
||||
`prop_block.gd`, `prop_canvas.gd`, `thumbnails/prop_thumbnail.gd`, and
|
||||
`thumbnails/thumbnail_cache.gd` via `preload` consts. `_apply_json_data(json, is_recovery =
|
||||
false) -> String` returns `""` on success or an error string (dialog on failure).
|
||||
- **Phase 10a final controls:** the Arrange top bar orders **Transform Whole Prop**,
|
||||
**Normalize Size**, **Center Prop**, and **Show Stickman Guide**. The numeric scale textbox
|
||||
and Apply Scale button are not exposed. Transform Whole Prop is Arrange-only and previews
|
||||
whole-document move, origin-based rotation, and uniform resize; Snap rounds move deltas,
|
||||
Ctrl snaps rotation to 15°, editable anchors follow, and Esc cancels the preview. Reshape
|
||||
and Attach hide Transform Whole Prop and Normalize Size; Center Prop and Show Stickman Guide
|
||||
remain available.
|
||||
- `scripts/prop_canvas.gd` — `class_name PropCanvas`, `extends Control`; the Prop Editor's
|
||||
**multi-shape vector canvas** (Phase 9, **not used by the editor**), mirroring
|
||||
`BodyPartPanel`'s proven input model (cursor-centered zoom, pinch/pan gestures, middle-mouse
|
||||
@@ -317,6 +324,11 @@ preview_area.size * 0.5`, so it also re-centers on window resize; it remains a
|
||||
~2 px wider) + a **bright core pass** (`SELECTION_CORE_COLOR`, white, original width). The
|
||||
pair is visible on **any** background (the Round 6 fill-adaptive `contrast_color_for` was
|
||||
removed because it could not know what sits *behind* a selected shape).
|
||||
- **Phase 10a whole-prop transform:** the canvas exposes typed move/rotate/resize request
|
||||
signals and renders a union gizmo while Transform Whole Prop is armed. Body, rotation-dot,
|
||||
and corner gestures use snapshot-only previews and commit one atomic document edit on
|
||||
release; `set_whole_prop_transform_enabled()` and related transform names are the preferred
|
||||
API. Resize-named compatibility aliases remain internally for existing controller paths.
|
||||
- `scripts/prop_file.gd` — `class_name PropFile`, `extends RefCounted`; the static `.prop`
|
||||
schema + converters (Phase 9, **not used by the editor**). Consts `FILE_VERSION := "1.0"`,
|
||||
`FILE_EXT := ".prop"`, `DEFAULT_DIR := "res://props"`, `SUPPORTED_VERSIONS: ["1.0"]`,
|
||||
@@ -897,6 +909,11 @@ PackedVector2Array`, `radius: float`, `fill_color`, `outline_color`, `outline_wi
|
||||
matching direct-state contact row as world-space normal/position, with a center-direction
|
||||
fallback when timing leaves no row. The physics signal covers prop-vs-prop and Phase 4.5
|
||||
stickman-prop contacts; the old geometric feet-point stickman test is removed.
|
||||
- **Phase 10a instance scaling:** Sandbox EDIT exposes four uniform corner crosses only for
|
||||
a single `PropBlock`. During PLAY, the authored root scale is moved into child visuals and
|
||||
collision geometry, including the custom one-hull path and nonuniform circle fallback;
|
||||
returning to EDIT restores the authored root scale. Preset mass and material physics stay
|
||||
unchanged.
|
||||
- `scripts/trigger_area.gd` — `class_name TriggerArea`, `extends Node2D`; a **placeable sensor**
|
||||
(Phase 4, **not used by the editor**). `@export size: Vector2` (default 96×96), `get_area_rect()
|
||||
-> Rect2` (centered local-space rectangle), and `_draw()` (translucent green fill +
|
||||
@@ -973,7 +990,10 @@ KNOCK_UP_VELOCITY = (0, -450))` (mass-scaled `apply_central_impulse` on every ra
|
||||
`global_position`/`global_rotation` + zeroed velocity. Because the freeze+teleport can take a
|
||||
physics frame or two to settle, `_restore_authored_state()` is also re-asserted over the next
|
||||
few `_physics_process` frames (`_restore_frames_left`) — so every Play session starts from and
|
||||
returns to the same authored state. **Placement ghost:** while a palette
|
||||
returns to the same authored state. **Phase 10a scale lifecycle:** prop corner-scale gestures
|
||||
use the existing transform signal path, synchronize the frozen body during EDIT, save the
|
||||
authored scale on commit, and retain that scale across PLAY → EDIT → PLAY. **Placement ghost:**
|
||||
while a palette
|
||||
item is active, `set_placement_mode(id)` spawns a translucent (`modulate.a = 0.5`),
|
||||
non-colliding (`collision_layer/mask = 0`, frozen) copy of the object reparented out of
|
||||
`World` into `_ghost_holder`; `_process()` tracks it to the cursor (+ snap) via
|
||||
@@ -1245,7 +1265,10 @@ page_count}` helper. `_build_cell(entry)` returns a `Button` with a `TextureRect
|
||||
`_rotate_start_rotation + (current − start angle)`, with holding **Ctrl** snapping to
|
||||
15° increments via `snappedf(new_rotation, deg_to_rad(15.0))`.
|
||||
`_draw()` draws a selection outline for **every** selected node. Pure `_draw()` +
|
||||
distance-based hit-testing (no `Area2D`); `enum Handle { NONE, TRANSLATE, ROTATE }`.
|
||||
distance-based hit-testing (no `Area2D`); `enum Handle { NONE, TRANSLATE, ROTATE, SCALE }`.
|
||||
**Phase 10a** adds four constant-screen-size corner crosses for a single `PropBlock`; the
|
||||
`Handle.SCALE` drag applies one positive uniform factor around the root origin through the
|
||||
existing transform signals. Other selections keep the existing gizmos.
|
||||
`transform_committed(nodes: Array[Node2D])` emitted on drag end. `set_enabled(false)` hides +
|
||||
disables in PLAY. Also draws the box-select marquee via `set_box_rect(rect)`.
|
||||
- `scripts/stage_grid.gd` — `class_name StageGrid`, `extends Node2D`; the optional world-space
|
||||
|
||||
Reference in New Issue
Block a user