blockymodel-web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/dist/blockymodel-web.js +2717 -0
  3. package/dist/blockymodel-web.js.map +1 -0
  4. package/dist/blockymodel-web.umd.cjs +122 -0
  5. package/dist/blockymodel-web.umd.cjs.map +1 -0
  6. package/dist/editor/Editor.d.ts +94 -0
  7. package/dist/editor/Editor.d.ts.map +1 -0
  8. package/dist/editor/History.d.ts +52 -0
  9. package/dist/editor/History.d.ts.map +1 -0
  10. package/dist/editor/SelectionManager.d.ts +57 -0
  11. package/dist/editor/SelectionManager.d.ts.map +1 -0
  12. package/dist/editor/Serializer.d.ts +44 -0
  13. package/dist/editor/Serializer.d.ts.map +1 -0
  14. package/dist/editor/TransformManager.d.ts +73 -0
  15. package/dist/editor/TransformManager.d.ts.map +1 -0
  16. package/dist/editor/commands/AddNodeCommand.d.ts +24 -0
  17. package/dist/editor/commands/AddNodeCommand.d.ts.map +1 -0
  18. package/dist/editor/commands/Command.d.ts +50 -0
  19. package/dist/editor/commands/Command.d.ts.map +1 -0
  20. package/dist/editor/commands/RemoveNodeCommand.d.ts +28 -0
  21. package/dist/editor/commands/RemoveNodeCommand.d.ts.map +1 -0
  22. package/dist/editor/commands/SetPositionCommand.d.ts +24 -0
  23. package/dist/editor/commands/SetPositionCommand.d.ts.map +1 -0
  24. package/dist/editor/commands/SetPropertyCommand.d.ts +41 -0
  25. package/dist/editor/commands/SetPropertyCommand.d.ts.map +1 -0
  26. package/dist/editor/commands/SetRotationCommand.d.ts +24 -0
  27. package/dist/editor/commands/SetRotationCommand.d.ts.map +1 -0
  28. package/dist/editor/commands/SetScaleCommand.d.ts +24 -0
  29. package/dist/editor/commands/SetScaleCommand.d.ts.map +1 -0
  30. package/dist/editor/index.d.ts +15 -0
  31. package/dist/editor/index.d.ts.map +1 -0
  32. package/dist/index.d.ts +21 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/loaders/BlockyModelLoader.d.ts +48 -0
  35. package/dist/loaders/BlockyModelLoader.d.ts.map +1 -0
  36. package/dist/types/blockymodel.d.ts +72 -0
  37. package/dist/types/blockymodel.d.ts.map +1 -0
  38. package/dist/ui/HierarchyPanel.d.ts +73 -0
  39. package/dist/ui/HierarchyPanel.d.ts.map +1 -0
  40. package/dist/ui/PropertyPanel.d.ts +59 -0
  41. package/dist/ui/PropertyPanel.d.ts.map +1 -0
  42. package/dist/ui/UVEditor.d.ts +56 -0
  43. package/dist/ui/UVEditor.d.ts.map +1 -0
  44. package/dist/ui/index.d.ts +4 -0
  45. package/dist/ui/index.d.ts.map +1 -0
  46. package/dist/viewer/ViewerController.d.ts +71 -0
  47. package/dist/viewer/ViewerController.d.ts.map +1 -0
  48. package/package.json +63 -0
  49. package/src/editor/Editor.ts +196 -0
  50. package/src/editor/History.ts +123 -0
  51. package/src/editor/SelectionManager.ts +183 -0
  52. package/src/editor/Serializer.ts +212 -0
  53. package/src/editor/TransformManager.ts +270 -0
  54. package/src/editor/commands/AddNodeCommand.ts +53 -0
  55. package/src/editor/commands/Command.ts +63 -0
  56. package/src/editor/commands/RemoveNodeCommand.ts +59 -0
  57. package/src/editor/commands/SetPositionCommand.ts +51 -0
  58. package/src/editor/commands/SetPropertyCommand.ts +100 -0
  59. package/src/editor/commands/SetRotationCommand.ts +51 -0
  60. package/src/editor/commands/SetScaleCommand.ts +51 -0
  61. package/src/editor/index.ts +19 -0
  62. package/src/index.ts +49 -0
  63. package/src/loaders/BlockyModelLoader.ts +281 -0
  64. package/src/main.ts +290 -0
  65. package/src/styles.css +597 -0
  66. package/src/types/blockymodel.ts +82 -0
  67. package/src/ui/HierarchyPanel.ts +343 -0
  68. package/src/ui/PropertyPanel.ts +434 -0
  69. package/src/ui/UVEditor.ts +336 -0
  70. package/src/ui/index.ts +4 -0
  71. package/src/viewer/ViewerController.ts +295 -0
@@ -0,0 +1,270 @@
1
+ import * as THREE from "three";
2
+ import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
3
+ import type { Editor } from "./Editor";
4
+ import { SetPositionCommand } from "./commands/SetPositionCommand";
5
+ import { SetRotationCommand } from "./commands/SetRotationCommand";
6
+ import { SetScaleCommand } from "./commands/SetScaleCommand";
7
+ import type { Command } from "./commands/Command";
8
+
9
+ type TransformMode = "translate" | "rotate" | "scale";
10
+ type TransformCallback = (...args: unknown[]) => void;
11
+
12
+ /**
13
+ * Manages transform controls for selected objects
14
+ */
15
+ export class TransformManager {
16
+ private editor: Editor;
17
+ private controls: TransformControls;
18
+ private mode: TransformMode = "translate";
19
+ private eventListeners: Map<string, Set<TransformCallback>> = new Map();
20
+
21
+ // Track transform start state for undo
22
+ private transformStartPosition: THREE.Vector3 | null = null;
23
+ private transformStartRotation: THREE.Euler | null = null;
24
+ private transformStartScale: THREE.Vector3 | null = null;
25
+ private isTransforming: boolean = false;
26
+
27
+ constructor(editor: Editor) {
28
+ this.editor = editor;
29
+
30
+ // Create TransformControls
31
+ this.controls = new TransformControls(
32
+ this.editor.camera,
33
+ this.editor.renderer.domElement
34
+ );
35
+ this.controls.setSize(0.75);
36
+ this.editor.scene.add(this.controls.getHelper());
37
+
38
+ // Bind event handlers
39
+ this.handleDraggingChanged = this.handleDraggingChanged.bind(this);
40
+ this.handleObjectChange = this.handleObjectChange.bind(this);
41
+
42
+ this.controls.addEventListener("dragging-changed", this.handleDraggingChanged);
43
+ this.controls.addEventListener("objectChange", this.handleObjectChange);
44
+
45
+ // Setup keyboard shortcuts
46
+ this.handleKeyDown = this.handleKeyDown.bind(this);
47
+ document.addEventListener("keydown", this.handleKeyDown);
48
+ }
49
+
50
+ /**
51
+ * Handle dragging state changes
52
+ */
53
+ private handleDraggingChanged(event: { value: unknown }): void {
54
+ const isDragging = event.value as boolean;
55
+ // Disable orbit controls while transforming
56
+ this.editor.controls.enabled = !isDragging;
57
+
58
+ if (isDragging) {
59
+ // Starting transform - capture initial state
60
+ this.captureStartState();
61
+ this.isTransforming = true;
62
+ } else if (this.isTransforming) {
63
+ // Ending transform - create command
64
+ this.createCommand();
65
+ this.isTransforming = false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Capture the start state before transform
71
+ */
72
+ private captureStartState(): void {
73
+ const object = this.controls.object;
74
+ if (!object) return;
75
+
76
+ this.transformStartPosition = object.position.clone();
77
+ this.transformStartRotation = object.rotation.clone();
78
+ this.transformStartScale = object.scale.clone();
79
+ }
80
+
81
+ /**
82
+ * Create a command after transform completes
83
+ */
84
+ private createCommand(): void {
85
+ const object = this.controls.object;
86
+ if (!object) return;
87
+
88
+ let command: Command | null = null;
89
+
90
+ switch (this.mode) {
91
+ case "translate":
92
+ if (this.transformStartPosition && !object.position.equals(this.transformStartPosition)) {
93
+ command = new SetPositionCommand(
94
+ object,
95
+ object.position.clone(),
96
+ this.transformStartPosition.clone()
97
+ );
98
+ }
99
+ break;
100
+
101
+ case "rotate":
102
+ if (this.transformStartRotation) {
103
+ const changed =
104
+ object.rotation.x !== this.transformStartRotation.x ||
105
+ object.rotation.y !== this.transformStartRotation.y ||
106
+ object.rotation.z !== this.transformStartRotation.z;
107
+ if (changed) {
108
+ command = new SetRotationCommand(
109
+ object,
110
+ object.rotation.clone(),
111
+ this.transformStartRotation.clone()
112
+ );
113
+ }
114
+ }
115
+ break;
116
+
117
+ case "scale":
118
+ if (this.transformStartScale && !object.scale.equals(this.transformStartScale)) {
119
+ command = new SetScaleCommand(
120
+ object,
121
+ object.scale.clone(),
122
+ this.transformStartScale.clone()
123
+ );
124
+ }
125
+ break;
126
+ }
127
+
128
+ if (command) {
129
+ this.emit("objectChanged", object, command);
130
+ }
131
+
132
+ // Clear start state
133
+ this.transformStartPosition = null;
134
+ this.transformStartRotation = null;
135
+ this.transformStartScale = null;
136
+ }
137
+
138
+ /**
139
+ * Handle object change during transform (live updates)
140
+ */
141
+ private handleObjectChange(): void {
142
+ // This fires continuously during drag - can be used for live UI updates
143
+ const object = this.controls.object;
144
+ if (object) {
145
+ // Emit a lightweight change event for UI updates without creating commands
146
+ this.emit("objectChanging", object);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Handle keyboard shortcuts
152
+ */
153
+ private handleKeyDown(event: KeyboardEvent): void {
154
+ // Ignore if in input field
155
+ if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {
156
+ return;
157
+ }
158
+
159
+ switch (event.key.toLowerCase()) {
160
+ case "g":
161
+ this.setMode("translate");
162
+ break;
163
+ case "r":
164
+ if (!event.ctrlKey && !event.metaKey) {
165
+ this.setMode("rotate");
166
+ }
167
+ break;
168
+ case "s":
169
+ if (!event.ctrlKey && !event.metaKey) {
170
+ this.setMode("scale");
171
+ }
172
+ break;
173
+ case "escape":
174
+ if (this.isTransforming) {
175
+ // Cancel transform - restore start state
176
+ this.cancelTransform();
177
+ }
178
+ break;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Cancel current transform and restore original state
184
+ */
185
+ private cancelTransform(): void {
186
+ const object = this.controls.object;
187
+ if (!object) return;
188
+
189
+ if (this.transformStartPosition) {
190
+ object.position.copy(this.transformStartPosition);
191
+ }
192
+ if (this.transformStartRotation) {
193
+ object.rotation.copy(this.transformStartRotation);
194
+ }
195
+ if (this.transformStartScale) {
196
+ object.scale.copy(this.transformStartScale);
197
+ }
198
+
199
+ this.isTransforming = false;
200
+ this.transformStartPosition = null;
201
+ this.transformStartRotation = null;
202
+ this.transformStartScale = null;
203
+ }
204
+
205
+ /**
206
+ * Attach controls to an object
207
+ */
208
+ attach(object: THREE.Object3D | null): void {
209
+ const helper = this.controls.getHelper();
210
+ if (object) {
211
+ this.controls.attach(object);
212
+ helper.visible = true;
213
+ } else {
214
+ this.controls.detach();
215
+ helper.visible = false;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Set transform mode
221
+ */
222
+ setMode(mode: TransformMode): void {
223
+ this.mode = mode;
224
+ this.controls.setMode(mode);
225
+ this.emit("modeChanged", mode);
226
+ }
227
+
228
+ /**
229
+ * Get current transform mode
230
+ */
231
+ getMode(): TransformMode {
232
+ return this.mode;
233
+ }
234
+
235
+ /**
236
+ * Get TransformControls instance
237
+ */
238
+ getControls(): TransformControls {
239
+ return this.controls;
240
+ }
241
+
242
+ /**
243
+ * Add event listener
244
+ */
245
+ on(event: string, callback: TransformCallback): void {
246
+ if (!this.eventListeners.has(event)) {
247
+ this.eventListeners.set(event, new Set());
248
+ }
249
+ this.eventListeners.get(event)!.add(callback);
250
+ }
251
+
252
+ /**
253
+ * Emit event
254
+ */
255
+ private emit(event: string, ...args: unknown[]): void {
256
+ this.eventListeners.get(event)?.forEach((callback) => callback(...args));
257
+ }
258
+
259
+ /**
260
+ * Clean up
261
+ */
262
+ dispose(): void {
263
+ document.removeEventListener("keydown", this.handleKeyDown);
264
+ this.controls.removeEventListener("dragging-changed", this.handleDraggingChanged);
265
+ this.controls.removeEventListener("objectChange", this.handleObjectChange);
266
+ this.controls.dispose();
267
+ this.editor.scene.remove(this.controls.getHelper());
268
+ this.eventListeners.clear();
269
+ }
270
+ }
@@ -0,0 +1,53 @@
1
+ import * as THREE from "three";
2
+ import { BaseCommand } from "./Command";
3
+
4
+ /**
5
+ * Command to add a child node to a parent
6
+ */
7
+ export class AddNodeCommand extends BaseCommand {
8
+ readonly type = "AddNode";
9
+ readonly updatable = false;
10
+
11
+ private parent: THREE.Object3D;
12
+ private child: THREE.Object3D;
13
+ private index: number;
14
+
15
+ constructor(
16
+ parent: THREE.Object3D,
17
+ child: THREE.Object3D,
18
+ index?: number
19
+ ) {
20
+ super(parent, false);
21
+ this.parent = parent;
22
+ this.child = child;
23
+ this.index = index ?? parent.children.length;
24
+ }
25
+
26
+ execute(): void {
27
+ this.parent.add(this.child);
28
+ // Reorder to correct index if needed
29
+ if (this.index < this.parent.children.length - 1) {
30
+ const children = this.parent.children;
31
+ children.splice(children.indexOf(this.child), 1);
32
+ children.splice(this.index, 0, this.child);
33
+ }
34
+ }
35
+
36
+ undo(): void {
37
+ this.parent.remove(this.child);
38
+ }
39
+
40
+ /**
41
+ * Get the added child
42
+ */
43
+ getChild(): THREE.Object3D {
44
+ return this.child;
45
+ }
46
+
47
+ /**
48
+ * Get the parent
49
+ */
50
+ getParent(): THREE.Object3D {
51
+ return this.parent;
52
+ }
53
+ }
@@ -0,0 +1,63 @@
1
+ import * as THREE from "three";
2
+
3
+ /**
4
+ * Base interface for all editor commands
5
+ * Commands implement the command pattern for undo/redo support
6
+ */
7
+ export interface Command {
8
+ /**
9
+ * Unique identifier for this command type
10
+ */
11
+ readonly type: string;
12
+
13
+ /**
14
+ * The object this command operates on
15
+ */
16
+ readonly object: THREE.Object3D;
17
+
18
+ /**
19
+ * Execute the command (apply the new state)
20
+ */
21
+ execute(): void;
22
+
23
+ /**
24
+ * Undo the command (restore the previous state)
25
+ */
26
+ undo(): void;
27
+
28
+ /**
29
+ * Whether this command can be merged with subsequent commands of the same type
30
+ * Used for rapid updates (e.g., continuous dragging)
31
+ */
32
+ readonly updatable: boolean;
33
+
34
+ /**
35
+ * Update this command with a new end state
36
+ * Only called if updatable is true and commands are of same type on same object
37
+ */
38
+ update?(command: Command): void;
39
+
40
+ /**
41
+ * Timestamp when this command was created
42
+ */
43
+ readonly timestamp: number;
44
+ }
45
+
46
+ /**
47
+ * Base class providing common functionality for commands
48
+ */
49
+ export abstract class BaseCommand implements Command {
50
+ abstract readonly type: string;
51
+ readonly object: THREE.Object3D;
52
+ readonly updatable: boolean;
53
+ readonly timestamp: number;
54
+
55
+ constructor(object: THREE.Object3D, updatable: boolean = false) {
56
+ this.object = object;
57
+ this.updatable = updatable;
58
+ this.timestamp = Date.now();
59
+ }
60
+
61
+ abstract execute(): void;
62
+ abstract undo(): void;
63
+ }
@@ -0,0 +1,59 @@
1
+ import * as THREE from "three";
2
+ import { BaseCommand } from "./Command";
3
+
4
+ /**
5
+ * Command to remove a node from its parent
6
+ */
7
+ export class RemoveNodeCommand extends BaseCommand {
8
+ readonly type = "RemoveNode";
9
+ readonly updatable = false;
10
+
11
+ private parent: THREE.Object3D;
12
+ private child: THREE.Object3D;
13
+ private index: number;
14
+
15
+ constructor(child: THREE.Object3D) {
16
+ super(child, false);
17
+ if (!child.parent) {
18
+ throw new Error("Cannot remove node without parent");
19
+ }
20
+ this.parent = child.parent;
21
+ this.child = child;
22
+ this.index = this.parent.children.indexOf(child);
23
+ }
24
+
25
+ execute(): void {
26
+ this.parent.remove(this.child);
27
+ }
28
+
29
+ undo(): void {
30
+ this.parent.add(this.child);
31
+ // Restore to original index
32
+ if (this.index < this.parent.children.length) {
33
+ const children = this.parent.children;
34
+ children.splice(children.indexOf(this.child), 1);
35
+ children.splice(this.index, 0, this.child);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Get the removed child
41
+ */
42
+ getChild(): THREE.Object3D {
43
+ return this.child;
44
+ }
45
+
46
+ /**
47
+ * Get the parent
48
+ */
49
+ getParent(): THREE.Object3D {
50
+ return this.parent;
51
+ }
52
+
53
+ /**
54
+ * Get the original index
55
+ */
56
+ getIndex(): number {
57
+ return this.index;
58
+ }
59
+ }
@@ -0,0 +1,51 @@
1
+ import * as THREE from "three";
2
+ import { BaseCommand, type Command } from "./Command";
3
+
4
+ /**
5
+ * Command to set an object's position
6
+ */
7
+ export class SetPositionCommand extends BaseCommand {
8
+ readonly type = "SetPosition";
9
+ readonly updatable = true;
10
+
11
+ private newPosition: THREE.Vector3;
12
+ private oldPosition: THREE.Vector3;
13
+
14
+ constructor(
15
+ object: THREE.Object3D,
16
+ newPosition: THREE.Vector3,
17
+ oldPosition: THREE.Vector3
18
+ ) {
19
+ super(object, true);
20
+ this.newPosition = newPosition.clone();
21
+ this.oldPosition = oldPosition.clone();
22
+ }
23
+
24
+ execute(): void {
25
+ this.object.position.copy(this.newPosition);
26
+ }
27
+
28
+ undo(): void {
29
+ this.object.position.copy(this.oldPosition);
30
+ }
31
+
32
+ update(command: Command): void {
33
+ if (command instanceof SetPositionCommand) {
34
+ this.newPosition.copy(command.newPosition);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get the new position
40
+ */
41
+ getNewPosition(): THREE.Vector3 {
42
+ return this.newPosition.clone();
43
+ }
44
+
45
+ /**
46
+ * Get the old position
47
+ */
48
+ getOldPosition(): THREE.Vector3 {
49
+ return this.oldPosition.clone();
50
+ }
51
+ }
@@ -0,0 +1,100 @@
1
+ import * as THREE from "three";
2
+ import { BaseCommand, type Command } from "./Command";
3
+
4
+ /**
5
+ * Command to set a property on an object
6
+ */
7
+ export class SetPropertyCommand extends BaseCommand {
8
+ readonly type = "SetProperty";
9
+ readonly updatable = true;
10
+
11
+ private propertyPath: string;
12
+ private newValue: unknown;
13
+ private oldValue: unknown;
14
+
15
+ constructor(
16
+ object: THREE.Object3D,
17
+ propertyPath: string,
18
+ newValue: unknown,
19
+ oldValue: unknown
20
+ ) {
21
+ super(object, true);
22
+ this.propertyPath = propertyPath;
23
+ this.newValue = this.cloneValue(newValue);
24
+ this.oldValue = this.cloneValue(oldValue);
25
+ }
26
+
27
+ /**
28
+ * Clone a value if it's an object/array
29
+ */
30
+ private cloneValue(value: unknown): unknown {
31
+ if (value === null || value === undefined) return value;
32
+ if (typeof value === "object") {
33
+ return JSON.parse(JSON.stringify(value));
34
+ }
35
+ return value;
36
+ }
37
+
38
+ /**
39
+ * Get a nested property by path (e.g., "userData.size.x")
40
+ */
41
+ private getProperty(obj: unknown, path: string): unknown {
42
+ const parts = path.split(".");
43
+ let current = obj as Record<string, unknown>;
44
+ for (const part of parts) {
45
+ if (current === null || current === undefined) return undefined;
46
+ current = current[part] as Record<string, unknown>;
47
+ }
48
+ return current;
49
+ }
50
+
51
+ /**
52
+ * Set a nested property by path
53
+ */
54
+ private setProperty(obj: unknown, path: string, value: unknown): void {
55
+ const parts = path.split(".");
56
+ let current = obj as Record<string, unknown>;
57
+ for (let i = 0; i < parts.length - 1; i++) {
58
+ if (current[parts[i]] === undefined) {
59
+ current[parts[i]] = {};
60
+ }
61
+ current = current[parts[i]] as Record<string, unknown>;
62
+ }
63
+ current[parts[parts.length - 1]] = value;
64
+ }
65
+
66
+ execute(): void {
67
+ this.setProperty(this.object, this.propertyPath, this.cloneValue(this.newValue));
68
+ }
69
+
70
+ undo(): void {
71
+ this.setProperty(this.object, this.propertyPath, this.cloneValue(this.oldValue));
72
+ }
73
+
74
+ update(command: Command): void {
75
+ if (command instanceof SetPropertyCommand && command.propertyPath === this.propertyPath) {
76
+ this.newValue = command.newValue;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get the property path
82
+ */
83
+ getPropertyPath(): string {
84
+ return this.propertyPath;
85
+ }
86
+
87
+ /**
88
+ * Get the new value
89
+ */
90
+ getNewValue(): unknown {
91
+ return this.cloneValue(this.newValue);
92
+ }
93
+
94
+ /**
95
+ * Get the old value
96
+ */
97
+ getOldValue(): unknown {
98
+ return this.cloneValue(this.oldValue);
99
+ }
100
+ }
@@ -0,0 +1,51 @@
1
+ import * as THREE from "three";
2
+ import { BaseCommand, type Command } from "./Command";
3
+
4
+ /**
5
+ * Command to set an object's rotation
6
+ */
7
+ export class SetRotationCommand extends BaseCommand {
8
+ readonly type = "SetRotation";
9
+ readonly updatable = true;
10
+
11
+ private newRotation: THREE.Euler;
12
+ private oldRotation: THREE.Euler;
13
+
14
+ constructor(
15
+ object: THREE.Object3D,
16
+ newRotation: THREE.Euler,
17
+ oldRotation: THREE.Euler
18
+ ) {
19
+ super(object, true);
20
+ this.newRotation = newRotation.clone();
21
+ this.oldRotation = oldRotation.clone();
22
+ }
23
+
24
+ execute(): void {
25
+ this.object.rotation.copy(this.newRotation);
26
+ }
27
+
28
+ undo(): void {
29
+ this.object.rotation.copy(this.oldRotation);
30
+ }
31
+
32
+ update(command: Command): void {
33
+ if (command instanceof SetRotationCommand) {
34
+ this.newRotation.copy(command.newRotation);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get the new rotation
40
+ */
41
+ getNewRotation(): THREE.Euler {
42
+ return this.newRotation.clone();
43
+ }
44
+
45
+ /**
46
+ * Get the old rotation
47
+ */
48
+ getOldRotation(): THREE.Euler {
49
+ return this.oldRotation.clone();
50
+ }
51
+ }
@@ -0,0 +1,51 @@
1
+ import * as THREE from "three";
2
+ import { BaseCommand, type Command } from "./Command";
3
+
4
+ /**
5
+ * Command to set an object's scale
6
+ */
7
+ export class SetScaleCommand extends BaseCommand {
8
+ readonly type = "SetScale";
9
+ readonly updatable = true;
10
+
11
+ private newScale: THREE.Vector3;
12
+ private oldScale: THREE.Vector3;
13
+
14
+ constructor(
15
+ object: THREE.Object3D,
16
+ newScale: THREE.Vector3,
17
+ oldScale: THREE.Vector3
18
+ ) {
19
+ super(object, true);
20
+ this.newScale = newScale.clone();
21
+ this.oldScale = oldScale.clone();
22
+ }
23
+
24
+ execute(): void {
25
+ this.object.scale.copy(this.newScale);
26
+ }
27
+
28
+ undo(): void {
29
+ this.object.scale.copy(this.oldScale);
30
+ }
31
+
32
+ update(command: Command): void {
33
+ if (command instanceof SetScaleCommand) {
34
+ this.newScale.copy(command.newScale);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get the new scale
40
+ */
41
+ getNewScale(): THREE.Vector3 {
42
+ return this.newScale.clone();
43
+ }
44
+
45
+ /**
46
+ * Get the old scale
47
+ */
48
+ getOldScale(): THREE.Vector3 {
49
+ return this.oldScale.clone();
50
+ }
51
+ }