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:
2026-09-22 22:23:31 -04:00
parent 97f3730413
commit 0a7f97cec7
18 changed files with 1782 additions and 78 deletions
+109 -26
View File
@@ -19,8 +19,8 @@
},
"material_preset": "rubber",
"metadata": {
"created_at": "2026-09-21T16:00:00",
"modified_at": "2026-09-21T16:00:00"
"created_at": "2026-09-22T00:26:21",
"modified_at": "2026-09-22T00:26:21"
},
"prop_name": "Baseball",
"shapes": [
@@ -30,23 +30,72 @@
"outline_color": "#A0A0A0FF",
"outline_width": 2.0,
"points": [
{ "x": 35.0, "y": 0.0 },
{ "x": 30.31, "y": 17.5 },
{ "x": 17.5, "y": 30.31 },
{ "x": 0.0, "y": 35.0 },
{ "x": -17.5, "y": 30.31 },
{ "x": -30.31, "y": 17.5 },
{ "x": -35.0, "y": 0.0 },
{ "x": -30.31, "y": -17.5 },
{ "x": -17.5, "y": -30.31 },
{ "x": 0.0, "y": -35.0 },
{ "x": 17.5, "y": -30.31 },
{ "x": 30.31, "y": -17.5 }
{
"x": 25.1999988555908,
"y": 0.0
},
{
"x": 21.8231983184814,
"y": 12.5999994277954
},
{
"x": 12.5999994277954,
"y": 21.8231983184814
},
{
"x": 0.0,
"y": 25.1999988555908
},
{
"x": -12.5999994277954,
"y": 21.8231983184814
},
{
"x": -21.8231983184814,
"y": 12.5999994277954
},
{
"x": -25.1999988555908,
"y": 0.0
},
{
"x": -21.8231983184814,
"y": -12.5999994277954
},
{
"x": -12.5999994277954,
"y": -21.8231983184814
},
{
"x": 0.0,
"y": -25.1999988555908
},
{
"x": 12.5999994277954,
"y": -21.8231983184814
},
{
"x": 21.8231983184814,
"y": -12.5999994277954
}
],
"radius": 35.0,
"radius": 25.1996288299561,
"reshaped": false,
"shape_type": "circle",
"vertex_flags": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
},
{
"closed": false,
@@ -54,14 +103,31 @@
"outline_color": "#D62828FF",
"outline_width": 2.0,
"points": [
{ "x": -12.0, "y": -28.0 },
{ "x": -22.0, "y": -14.0 },
{ "x": -22.0, "y": 14.0 },
{ "x": -12.0, "y": 28.0 }
{
"x": -8.64000034332275,
"y": -20.1599998474121
},
{
"x": -15.8400001525879,
"y": -10.0799999237061
},
{
"x": -15.8400001525879,
"y": 10.0799999237061
},
{
"x": -8.64000034332275,
"y": 20.1599998474121
}
],
"reshaped": true,
"shape_type": "line",
"vertex_flags": [0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0
]
},
{
"closed": false,
@@ -69,14 +135,31 @@
"outline_color": "#D62828FF",
"outline_width": 2.0,
"points": [
{ "x": 12.0, "y": -28.0 },
{ "x": 22.0, "y": -14.0 },
{ "x": 22.0, "y": 14.0 },
{ "x": 12.0, "y": 28.0 }
{
"x": 8.64000034332275,
"y": -20.1599998474121
},
{
"x": 15.8400001525879,
"y": -10.0799999237061
},
{
"x": 15.8400001525879,
"y": 10.0799999237061
},
{
"x": 8.64000034332275,
"y": 20.1599998474121
}
],
"reshaped": true,
"shape_type": "line",
"vertex_flags": [0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0
]
}
],
"version": "1.0"
+111 -27
View File
@@ -13,14 +13,14 @@
"role": "grip",
"rotation_deg": 0.0,
"x": 0.0,
"y": -42.5,
"y": -82.066291809082,
"z_index_offset": 1
}
},
"material_preset": "wood",
"metadata": {
"created_at": "2026-09-21T16:00:00",
"modified_at": "2026-09-21T16:00:00"
"created_at": "2026-09-22T00:22:32",
"modified_at": "2026-09-22T00:22:32"
},
"prop_name": "Brown Briefcase",
"shapes": [
@@ -30,18 +30,51 @@
"outline_color": "#26150CFF",
"outline_width": 2.0,
"points": [
{ "x": -25.0, "y": -30.0 },
{ "x": -25.0, "y": -55.0 },
{ "x": 25.0, "y": -55.0 },
{ "x": 25.0, "y": -30.0 },
{ "x": 18.0, "y": -30.0 },
{ "x": 18.0, "y": -47.0 },
{ "x": -18.0, "y": -47.0 },
{ "x": -18.0, "y": -30.0 }
{
"x": -48.2742919921875,
"y": -57.9291458129883
},
{
"x": -48.2742919921875,
"y": -106.20344543457
},
{
"x": 48.2742919921875,
"y": -106.20344543457
},
{
"x": 48.2742919921875,
"y": -57.9291458129883
},
{
"x": 34.7574882507324,
"y": -57.9291458129883
},
{
"x": 34.7574882507324,
"y": -90.7556686401367
},
{
"x": -34.7574882507324,
"y": -90.7556686401367
},
{
"x": -34.7574882507324,
"y": -57.9291458129883
}
],
"reshaped": true,
"shape_type": "polygon",
"vertex_flags": [0, 0, 0, 0, 0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0,
0,
0,
0,
0
]
},
{
"closed": true,
@@ -49,14 +82,31 @@
"outline_color": "#3B200AFF",
"outline_width": 3.0,
"points": [
{ "x": -70.0, "y": -30.0 },
{ "x": 70.0, "y": -30.0 },
{ "x": 70.0, "y": 40.0 },
{ "x": -70.0, "y": 40.0 }
{
"x": -135.168014526367,
"y": -57.9291458129883
},
{
"x": 135.168014526367,
"y": -57.9291458129883
},
{
"x": 135.168014526367,
"y": 77.2388610839844
},
{
"x": -135.168014526367,
"y": 77.2388610839844
}
],
"reshaped": false,
"shape_type": "polygon",
"vertex_flags": [0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0
]
},
{
"closed": true,
@@ -64,14 +114,31 @@
"outline_color": "#8B7500FF",
"outline_width": 1.5,
"points": [
{ "x": -45.0, "y": -32.0 },
{ "x": -30.0, "y": -32.0 },
{ "x": -30.0, "y": -22.0 },
{ "x": -45.0, "y": -22.0 }
{
"x": -86.8937225341797,
"y": -61.791088104248
},
{
"x": -57.9291458129883,
"y": -61.791088104248
},
{
"x": -57.9291458129883,
"y": -42.481372833252
},
{
"x": -86.8937225341797,
"y": -42.481372833252
}
],
"reshaped": false,
"shape_type": "polygon",
"vertex_flags": [0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0
]
},
{
"closed": true,
@@ -79,14 +146,31 @@
"outline_color": "#8B7500FF",
"outline_width": 1.5,
"points": [
{ "x": 30.0, "y": -32.0 },
{ "x": 45.0, "y": -32.0 },
{ "x": 45.0, "y": -22.0 },
{ "x": 30.0, "y": -22.0 }
{
"x": 57.9291458129883,
"y": -61.791088104248
},
{
"x": 86.8937225341797,
"y": -61.791088104248
},
{
"x": 86.8937225341797,
"y": -42.481372833252
},
{
"x": 57.9291458129883,
"y": -42.481372833252
}
],
"reshaped": false,
"shape_type": "polygon",
"vertex_flags": [0, 0, 0, 0]
"vertex_flags": [
0,
0,
0,
0
]
}
],
"version": "1.0"