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.
- package/LICENSE +21 -0
- package/dist/blockymodel-web.js +2717 -0
- package/dist/blockymodel-web.js.map +1 -0
- package/dist/blockymodel-web.umd.cjs +122 -0
- package/dist/blockymodel-web.umd.cjs.map +1 -0
- package/dist/editor/Editor.d.ts +94 -0
- package/dist/editor/Editor.d.ts.map +1 -0
- package/dist/editor/History.d.ts +52 -0
- package/dist/editor/History.d.ts.map +1 -0
- package/dist/editor/SelectionManager.d.ts +57 -0
- package/dist/editor/SelectionManager.d.ts.map +1 -0
- package/dist/editor/Serializer.d.ts +44 -0
- package/dist/editor/Serializer.d.ts.map +1 -0
- package/dist/editor/TransformManager.d.ts +73 -0
- package/dist/editor/TransformManager.d.ts.map +1 -0
- package/dist/editor/commands/AddNodeCommand.d.ts +24 -0
- package/dist/editor/commands/AddNodeCommand.d.ts.map +1 -0
- package/dist/editor/commands/Command.d.ts +50 -0
- package/dist/editor/commands/Command.d.ts.map +1 -0
- package/dist/editor/commands/RemoveNodeCommand.d.ts +28 -0
- package/dist/editor/commands/RemoveNodeCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetPositionCommand.d.ts +24 -0
- package/dist/editor/commands/SetPositionCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetPropertyCommand.d.ts +41 -0
- package/dist/editor/commands/SetPropertyCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetRotationCommand.d.ts +24 -0
- package/dist/editor/commands/SetRotationCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetScaleCommand.d.ts +24 -0
- package/dist/editor/commands/SetScaleCommand.d.ts.map +1 -0
- package/dist/editor/index.d.ts +15 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/loaders/BlockyModelLoader.d.ts +48 -0
- package/dist/loaders/BlockyModelLoader.d.ts.map +1 -0
- package/dist/types/blockymodel.d.ts +72 -0
- package/dist/types/blockymodel.d.ts.map +1 -0
- package/dist/ui/HierarchyPanel.d.ts +73 -0
- package/dist/ui/HierarchyPanel.d.ts.map +1 -0
- package/dist/ui/PropertyPanel.d.ts +59 -0
- package/dist/ui/PropertyPanel.d.ts.map +1 -0
- package/dist/ui/UVEditor.d.ts +56 -0
- package/dist/ui/UVEditor.d.ts.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/viewer/ViewerController.d.ts +71 -0
- package/dist/viewer/ViewerController.d.ts.map +1 -0
- package/package.json +63 -0
- package/src/editor/Editor.ts +196 -0
- package/src/editor/History.ts +123 -0
- package/src/editor/SelectionManager.ts +183 -0
- package/src/editor/Serializer.ts +212 -0
- package/src/editor/TransformManager.ts +270 -0
- package/src/editor/commands/AddNodeCommand.ts +53 -0
- package/src/editor/commands/Command.ts +63 -0
- package/src/editor/commands/RemoveNodeCommand.ts +59 -0
- package/src/editor/commands/SetPositionCommand.ts +51 -0
- package/src/editor/commands/SetPropertyCommand.ts +100 -0
- package/src/editor/commands/SetRotationCommand.ts +51 -0
- package/src/editor/commands/SetScaleCommand.ts +51 -0
- package/src/editor/index.ts +19 -0
- package/src/index.ts +49 -0
- package/src/loaders/BlockyModelLoader.ts +281 -0
- package/src/main.ts +290 -0
- package/src/styles.css +597 -0
- package/src/types/blockymodel.ts +82 -0
- package/src/ui/HierarchyPanel.ts +343 -0
- package/src/ui/PropertyPanel.ts +434 -0
- package/src/ui/UVEditor.ts +336 -0
- package/src/ui/index.ts +4 -0
- package/src/viewer/ViewerController.ts +295 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { BlockyModel } from "../types/blockymodel";
|
|
3
|
+
/**
|
|
4
|
+
* Loader for .blockymodel files
|
|
5
|
+
* Parses JSON and builds Three.js Object3D hierarchy
|
|
6
|
+
*/
|
|
7
|
+
export declare class BlockyModelLoader extends THREE.Loader {
|
|
8
|
+
constructor(manager?: THREE.LoadingManager);
|
|
9
|
+
/**
|
|
10
|
+
* Load a .blockymodel file from URL
|
|
11
|
+
*/
|
|
12
|
+
load(url: string): Promise<THREE.Group>;
|
|
13
|
+
/**
|
|
14
|
+
* Load from File object (for drag-drop / file input)
|
|
15
|
+
*/
|
|
16
|
+
loadFromFile(file: File): Promise<THREE.Group>;
|
|
17
|
+
/**
|
|
18
|
+
* Parse BlockyModel JSON into Three.js scene graph
|
|
19
|
+
*/
|
|
20
|
+
parse(json: BlockyModel): THREE.Group;
|
|
21
|
+
/**
|
|
22
|
+
* Recursively parse a BlockyNode into Three.js Object3D
|
|
23
|
+
*/
|
|
24
|
+
private parseNode;
|
|
25
|
+
/**
|
|
26
|
+
* Create the appropriate Object3D based on shape type
|
|
27
|
+
*/
|
|
28
|
+
private createNodeObject;
|
|
29
|
+
/**
|
|
30
|
+
* Create a box mesh from shape data
|
|
31
|
+
*/
|
|
32
|
+
private createBox;
|
|
33
|
+
/**
|
|
34
|
+
* Create a quad (plane) mesh from shape data
|
|
35
|
+
*/
|
|
36
|
+
private createQuad;
|
|
37
|
+
/**
|
|
38
|
+
* Create material based on shading mode
|
|
39
|
+
* Phase 1: Basic color material, texture mapping in Phase 2
|
|
40
|
+
*/
|
|
41
|
+
private createMaterial;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Helper to apply a texture to all meshes in a loaded model
|
|
45
|
+
* Phase 1: Simple texture application without UV mapping
|
|
46
|
+
*/
|
|
47
|
+
export declare function applyTextureToModel(model: THREE.Group, texture: THREE.Texture): void;
|
|
48
|
+
//# sourceMappingURL=BlockyModelLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockyModelLoader.d.ts","sourceRoot":"","sources":["../../src/loaders/BlockyModelLoader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EACV,WAAW,EAIZ,MAAM,sBAAsB,CAAC;AAO9B;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK,CAAC,MAAM;gBACrC,OAAO,CAAC,EAAE,KAAK,CAAC,cAAc;IAI1C;;OAEG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAS7C;;OAEG;IACG,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAMpD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,WAAW,GAAG,KAAK,CAAC,KAAK;IAcrC;;OAEG;IACH,OAAO,CAAC,SAAS;IAyBjB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;OAEG;IACH,OAAO,CAAC,SAAS;IAkCjB;;OAEG;IACH,OAAO,CAAC,UAAU;IA6DlB;;;OAGG;IACH,OAAO,CAAC,cAAc;CAqCvB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,KAAK,CAAC,OAAO,GACrB,IAAI,CAgBN"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlockyModel format type definitions
|
|
3
|
+
* Based on Hytale's native 3D model format
|
|
4
|
+
*/
|
|
5
|
+
export interface Vec3 {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
z: number;
|
|
9
|
+
}
|
|
10
|
+
export interface Vec2 {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
}
|
|
14
|
+
export interface Quaternion {
|
|
15
|
+
w: number;
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
z: number;
|
|
19
|
+
}
|
|
20
|
+
export interface FaceUV {
|
|
21
|
+
offset: Vec2;
|
|
22
|
+
mirror: {
|
|
23
|
+
x: boolean;
|
|
24
|
+
y: boolean;
|
|
25
|
+
};
|
|
26
|
+
angle: 0 | 90 | 180 | 270;
|
|
27
|
+
}
|
|
28
|
+
export interface TextureLayout {
|
|
29
|
+
front?: FaceUV;
|
|
30
|
+
back?: FaceUV;
|
|
31
|
+
left?: FaceUV;
|
|
32
|
+
right?: FaceUV;
|
|
33
|
+
top?: FaceUV;
|
|
34
|
+
bottom?: FaceUV;
|
|
35
|
+
}
|
|
36
|
+
export type ShapeType = "box" | "quad" | "none";
|
|
37
|
+
export type ShadingMode = "standard" | "flat" | "fullbright" | "reflective";
|
|
38
|
+
export type NormalDirection = "+X" | "-X" | "+Y" | "-Y" | "+Z" | "-Z";
|
|
39
|
+
export interface ShapeSettings {
|
|
40
|
+
size?: Vec3;
|
|
41
|
+
normal?: NormalDirection;
|
|
42
|
+
isPiece?: boolean;
|
|
43
|
+
isStaticBox?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface BlockyShape {
|
|
46
|
+
type: ShapeType;
|
|
47
|
+
offset: Vec3;
|
|
48
|
+
stretch: Vec3;
|
|
49
|
+
settings: ShapeSettings;
|
|
50
|
+
textureLayout: TextureLayout;
|
|
51
|
+
unwrapMode: "custom" | "auto";
|
|
52
|
+
visible: boolean;
|
|
53
|
+
doubleSided: boolean;
|
|
54
|
+
shadingMode: ShadingMode;
|
|
55
|
+
}
|
|
56
|
+
export interface BlockyNode {
|
|
57
|
+
id: string;
|
|
58
|
+
name: string;
|
|
59
|
+
position?: Vec3;
|
|
60
|
+
orientation?: Quaternion;
|
|
61
|
+
shape: BlockyShape;
|
|
62
|
+
children: BlockyNode[];
|
|
63
|
+
}
|
|
64
|
+
export interface BlockyModel {
|
|
65
|
+
lod?: "auto" | string;
|
|
66
|
+
format?: "character" | "prop";
|
|
67
|
+
nodes: BlockyNode[];
|
|
68
|
+
}
|
|
69
|
+
export declare const DEFAULT_POSITION: Vec3;
|
|
70
|
+
export declare const DEFAULT_ORIENTATION: Quaternion;
|
|
71
|
+
export declare const DEFAULT_STRETCH: Vec3;
|
|
72
|
+
//# sourceMappingURL=blockymodel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockymodel.d.ts","sourceRoot":"","sources":["../../src/types/blockymodel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,EAAE,OAAO,CAAC;QAAC,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,GAAG,YAAY,CAAC;AAE5E,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,QAAQ,EAAE,aAAa,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,UAAU,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC9B,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAGD,eAAO,MAAM,gBAAgB,EAAE,IAA2B,CAAC;AAC3D,eAAO,MAAM,mBAAmB,EAAE,UAAuC,CAAC;AAC1E,eAAO,MAAM,eAAe,EAAE,IAA2B,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Editor } from "../editor/Editor";
|
|
2
|
+
/**
|
|
3
|
+
* Hierarchy panel showing the model tree structure
|
|
4
|
+
*/
|
|
5
|
+
export declare class HierarchyPanel {
|
|
6
|
+
private editor;
|
|
7
|
+
private container;
|
|
8
|
+
private treeContainer;
|
|
9
|
+
private contextMenu;
|
|
10
|
+
private contextTarget;
|
|
11
|
+
constructor(editor: Editor, containerId: string);
|
|
12
|
+
/**
|
|
13
|
+
* Build the hierarchy panel UI
|
|
14
|
+
*/
|
|
15
|
+
private buildUI;
|
|
16
|
+
/**
|
|
17
|
+
* Setup event listeners
|
|
18
|
+
*/
|
|
19
|
+
private setupEventListeners;
|
|
20
|
+
/**
|
|
21
|
+
* Refresh the hierarchy tree
|
|
22
|
+
*/
|
|
23
|
+
refresh(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Build tree recursively
|
|
26
|
+
*/
|
|
27
|
+
private buildTree;
|
|
28
|
+
/**
|
|
29
|
+
* Get icon for object type
|
|
30
|
+
*/
|
|
31
|
+
private getIcon;
|
|
32
|
+
/**
|
|
33
|
+
* Toggle expand/collapse
|
|
34
|
+
*/
|
|
35
|
+
private toggleExpand;
|
|
36
|
+
/**
|
|
37
|
+
* Update selection highlight
|
|
38
|
+
*/
|
|
39
|
+
private updateSelection;
|
|
40
|
+
/**
|
|
41
|
+
* Show context menu
|
|
42
|
+
*/
|
|
43
|
+
private showContextMenu;
|
|
44
|
+
/**
|
|
45
|
+
* Hide context menu
|
|
46
|
+
*/
|
|
47
|
+
private hideContextMenu;
|
|
48
|
+
/**
|
|
49
|
+
* Handle context menu action
|
|
50
|
+
*/
|
|
51
|
+
private handleContextAction;
|
|
52
|
+
/**
|
|
53
|
+
* Add a child box
|
|
54
|
+
*/
|
|
55
|
+
private addChildBox;
|
|
56
|
+
/**
|
|
57
|
+
* Add a child group
|
|
58
|
+
*/
|
|
59
|
+
private addChildGroup;
|
|
60
|
+
/**
|
|
61
|
+
* Duplicate a node
|
|
62
|
+
*/
|
|
63
|
+
private duplicateNode;
|
|
64
|
+
/**
|
|
65
|
+
* Delete a node
|
|
66
|
+
*/
|
|
67
|
+
private deleteNode;
|
|
68
|
+
/**
|
|
69
|
+
* Cleanup
|
|
70
|
+
*/
|
|
71
|
+
dispose(): void;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=HierarchyPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HierarchyPanel.d.ts","sourceRoot":"","sources":["../../src/ui/HierarchyPanel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAA+B;gBAExC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAY/C;;OAEG;IACH,OAAO,CAAC,OAAO;IAsBf;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0C3B;;OAEG;IACH,OAAO,IAAI,IAAI;IAWf;;OAEG;IACH,OAAO,CAAC,SAAS;IA4DjB;;OAEG;IACH,OAAO,CAAC,OAAO;IAWf;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACH,OAAO,CAAC,eAAe;IAkBvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAkBvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAYrB;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { Editor } from "../editor/Editor";
|
|
3
|
+
/**
|
|
4
|
+
* Property panel for editing selected object properties
|
|
5
|
+
*/
|
|
6
|
+
export declare class PropertyPanel {
|
|
7
|
+
private editor;
|
|
8
|
+
private container;
|
|
9
|
+
private currentObject;
|
|
10
|
+
private isUpdating;
|
|
11
|
+
private nameInput;
|
|
12
|
+
private posInputs;
|
|
13
|
+
private rotInputs;
|
|
14
|
+
private scaleInputs;
|
|
15
|
+
private sizeInputs;
|
|
16
|
+
private offsetInputs;
|
|
17
|
+
private visibleCheckbox;
|
|
18
|
+
private doubleSidedCheckbox;
|
|
19
|
+
private shadingModeSelect;
|
|
20
|
+
private shapeTypeSpan;
|
|
21
|
+
constructor(editor: Editor, containerId: string);
|
|
22
|
+
/**
|
|
23
|
+
* Build the property panel UI
|
|
24
|
+
*/
|
|
25
|
+
private buildUI;
|
|
26
|
+
/**
|
|
27
|
+
* Setup event listeners
|
|
28
|
+
*/
|
|
29
|
+
private setupEventListeners;
|
|
30
|
+
/**
|
|
31
|
+
* Setup Vec3 input handlers
|
|
32
|
+
*/
|
|
33
|
+
private setupVec3Input;
|
|
34
|
+
/**
|
|
35
|
+
* Set the object to display properties for
|
|
36
|
+
*/
|
|
37
|
+
setObject(object: THREE.Object3D | null): void;
|
|
38
|
+
/**
|
|
39
|
+
* Update UI from current object
|
|
40
|
+
*/
|
|
41
|
+
private updateFromObject;
|
|
42
|
+
/**
|
|
43
|
+
* Rebuild geometry after size/offset changes
|
|
44
|
+
*/
|
|
45
|
+
private rebuildGeometry;
|
|
46
|
+
/**
|
|
47
|
+
* Show property sections
|
|
48
|
+
*/
|
|
49
|
+
private showProperties;
|
|
50
|
+
/**
|
|
51
|
+
* Show empty state
|
|
52
|
+
*/
|
|
53
|
+
private showEmpty;
|
|
54
|
+
/**
|
|
55
|
+
* Cleanup
|
|
56
|
+
*/
|
|
57
|
+
dispose(): void;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=PropertyPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PropertyPanel.d.ts","sourceRoot":"","sources":["../../src/ui/PropertyPanel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAO/C;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,UAAU,CAAkB;IAGpC,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,SAAS,CAAqE;IACtF,OAAO,CAAC,SAAS,CAAqE;IACtF,OAAO,CAAC,WAAW,CAAqE;IACxF,OAAO,CAAC,UAAU,CAAqE;IACvF,OAAO,CAAC,YAAY,CAAqE;IACzF,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,mBAAmB,CAAoB;IAC/C,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,aAAa,CAAe;gBAExB,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAY/C;;OAEG;IACH,OAAO,CAAC,OAAO;IAoHf;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmH3B;;OAEG;IACH,OAAO,CAAC,cAAc;IAkBtB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI;IAW9C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA0DxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAwBvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,SAAS;IAUjB;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { Editor } from "../editor/Editor";
|
|
3
|
+
/**
|
|
4
|
+
* UV Editor panel for per-face texture UV editing
|
|
5
|
+
*/
|
|
6
|
+
export declare class UVEditor {
|
|
7
|
+
private editor;
|
|
8
|
+
private container;
|
|
9
|
+
private currentObject;
|
|
10
|
+
private isUpdating;
|
|
11
|
+
private faceInputs;
|
|
12
|
+
constructor(editor: Editor, containerId: string);
|
|
13
|
+
/**
|
|
14
|
+
* Build the UV editor UI
|
|
15
|
+
*/
|
|
16
|
+
private buildUI;
|
|
17
|
+
/**
|
|
18
|
+
* Setup event listeners
|
|
19
|
+
*/
|
|
20
|
+
private setupEventListeners;
|
|
21
|
+
/**
|
|
22
|
+
* Handle face UV change
|
|
23
|
+
*/
|
|
24
|
+
private handleFaceChange;
|
|
25
|
+
/**
|
|
26
|
+
* Get texture layout from current object
|
|
27
|
+
*/
|
|
28
|
+
private getTextureLayout;
|
|
29
|
+
/**
|
|
30
|
+
* Set the object to edit UVs for
|
|
31
|
+
*/
|
|
32
|
+
setObject(object: THREE.Object3D | null): void;
|
|
33
|
+
/**
|
|
34
|
+
* Update UI from current object
|
|
35
|
+
*/
|
|
36
|
+
private updateFromObject;
|
|
37
|
+
/**
|
|
38
|
+
* Apply texture layout to geometry UVs
|
|
39
|
+
* Note: This is a simplified implementation - full UV mapping would require
|
|
40
|
+
* knowing the texture atlas dimensions
|
|
41
|
+
*/
|
|
42
|
+
private applyTextureLayout;
|
|
43
|
+
/**
|
|
44
|
+
* Show content
|
|
45
|
+
*/
|
|
46
|
+
private showContent;
|
|
47
|
+
/**
|
|
48
|
+
* Show empty state
|
|
49
|
+
*/
|
|
50
|
+
private showEmpty;
|
|
51
|
+
/**
|
|
52
|
+
* Cleanup
|
|
53
|
+
*/
|
|
54
|
+
dispose(): void;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=UVEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UVEditor.d.ts","sourceRoot":"","sources":["../../src/ui/UVEditor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB/C;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,UAAU,CAMH;gBAEH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAY/C;;OAEG;IACH,OAAO,CAAC,OAAO;IA0Df;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA2C3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkCxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI;IAY9C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA4D1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAQjB;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
3
|
+
export interface ViewerOptions {
|
|
4
|
+
container: HTMLElement;
|
|
5
|
+
backgroundColor?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Main viewer controller
|
|
9
|
+
* Manages Three.js scene, camera, controls, and model loading
|
|
10
|
+
*/
|
|
11
|
+
export declare class ViewerController {
|
|
12
|
+
readonly scene: THREE.Scene;
|
|
13
|
+
readonly camera: THREE.PerspectiveCamera;
|
|
14
|
+
readonly renderer: THREE.WebGLRenderer;
|
|
15
|
+
readonly controls: OrbitControls;
|
|
16
|
+
readonly domElement: HTMLElement;
|
|
17
|
+
private loader;
|
|
18
|
+
private textureLoader;
|
|
19
|
+
private currentModel;
|
|
20
|
+
private animationId;
|
|
21
|
+
constructor(options: ViewerOptions);
|
|
22
|
+
private setupLighting;
|
|
23
|
+
private setupHelpers;
|
|
24
|
+
private handleResize;
|
|
25
|
+
private animate;
|
|
26
|
+
/**
|
|
27
|
+
* Load a .blockymodel file
|
|
28
|
+
*/
|
|
29
|
+
loadModel(file: File): Promise<THREE.Group>;
|
|
30
|
+
/**
|
|
31
|
+
* Get the currently loaded model
|
|
32
|
+
*/
|
|
33
|
+
getModel(): THREE.Group | null;
|
|
34
|
+
/**
|
|
35
|
+
* Load a texture and apply to current model
|
|
36
|
+
*/
|
|
37
|
+
loadTexture(file: File): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Fit camera to view the entire model
|
|
40
|
+
*/
|
|
41
|
+
fitCameraToModel(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Reset camera to default position
|
|
44
|
+
*/
|
|
45
|
+
resetCamera(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Set background color
|
|
48
|
+
*/
|
|
49
|
+
setBackgroundColor(color: number | string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Toggle wireframe mode on all meshes
|
|
52
|
+
*/
|
|
53
|
+
toggleWireframe(enabled: boolean): void;
|
|
54
|
+
/**
|
|
55
|
+
* Get model hierarchy for debugging
|
|
56
|
+
*/
|
|
57
|
+
getModelHierarchy(): object | null;
|
|
58
|
+
/**
|
|
59
|
+
* Count meshes in model
|
|
60
|
+
*/
|
|
61
|
+
private countMeshes;
|
|
62
|
+
/**
|
|
63
|
+
* Dispose of model resources
|
|
64
|
+
*/
|
|
65
|
+
private disposeModel;
|
|
66
|
+
/**
|
|
67
|
+
* Clean up all resources
|
|
68
|
+
*/
|
|
69
|
+
dispose(): void;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=ViewerController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ViewerController.d.ts","sourceRoot":"","sources":["../../src/viewer/ViewerController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAG7E,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,WAAW,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,SAAgB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;IACnC,SAAgB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAC;IAChD,SAAgB,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC;IAC9C,SAAgB,QAAQ,EAAE,aAAa,CAAC;IACxC,SAAgB,UAAU,EAAE,WAAW,CAAC;IAExC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,WAAW,CAAuB;gBAE9B,OAAO,EAAE,aAAa;IAiDlC,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,OAAO,CAIb;IAEF;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAuBjD;;OAEG;IACH,QAAQ,IAAI,KAAK,CAAC,KAAK,GAAG,IAAI;IAI9B;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB5C;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAqBxB;;OAEG;IACH,WAAW,IAAI,IAAI;IAUnB;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIhD;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IASvC;;OAEG;IACH,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAalC;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,IAAI,IAAI;CAYhB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "blockymodel-web",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Web-based 3D editor for BlockyModel format with transform gizmos, hierarchy panel, property editing, and undo/redo",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/blockymodel-web.umd.cjs",
|
|
7
|
+
"module": "./dist/blockymodel-web.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/blockymodel-web.js",
|
|
13
|
+
"require": "./dist/blockymodel-web.umd.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "vite",
|
|
22
|
+
"build": "tsc && vite build",
|
|
23
|
+
"build:lib": "vite build --mode lib && tsc -p tsconfig.build.json",
|
|
24
|
+
"preview": "vite preview",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest",
|
|
27
|
+
"test:run": "vitest run",
|
|
28
|
+
"test:coverage": "vitest run --coverage",
|
|
29
|
+
"prepublishOnly": "npm run build:lib"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"blockymodel",
|
|
33
|
+
"hytale",
|
|
34
|
+
"3d-editor",
|
|
35
|
+
"threejs",
|
|
36
|
+
"voxel",
|
|
37
|
+
"model-editor",
|
|
38
|
+
"webgl"
|
|
39
|
+
],
|
|
40
|
+
"author": "Zacx <zacxdev@gmail.com>",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/ZacxDev/blockymodel-web.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/ZacxDev/blockymodel-web/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/ZacxDev/blockymodel-web#readme",
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"three": ">=0.150.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^25.2.0",
|
|
55
|
+
"@types/three": "^0.170.0",
|
|
56
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
57
|
+
"jsdom": "^28.0.0",
|
|
58
|
+
"three": "^0.170.0",
|
|
59
|
+
"typescript": "^5.7.0",
|
|
60
|
+
"vite": "^6.0.0",
|
|
61
|
+
"vitest": "^4.0.18"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { SelectionManager } from "./SelectionManager";
|
|
3
|
+
import { TransformManager } from "./TransformManager";
|
|
4
|
+
import { History } from "./History";
|
|
5
|
+
import type { Command } from "./commands/Command";
|
|
6
|
+
import type { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
7
|
+
|
|
8
|
+
export type EditorEvent =
|
|
9
|
+
| "selectionChanged"
|
|
10
|
+
| "objectChanged"
|
|
11
|
+
| "historyChanged"
|
|
12
|
+
| "modeChanged"
|
|
13
|
+
| "modelLoaded";
|
|
14
|
+
|
|
15
|
+
export interface EditorOptions {
|
|
16
|
+
scene: THREE.Scene;
|
|
17
|
+
camera: THREE.Camera;
|
|
18
|
+
renderer: THREE.WebGLRenderer;
|
|
19
|
+
controls: OrbitControls;
|
|
20
|
+
domElement: HTMLElement;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type EventCallback = (...args: unknown[]) => void;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Central editor state manager
|
|
27
|
+
* Coordinates selection, transforms, history, and emits events for UI updates
|
|
28
|
+
*/
|
|
29
|
+
export class Editor {
|
|
30
|
+
public readonly scene: THREE.Scene;
|
|
31
|
+
public readonly camera: THREE.Camera;
|
|
32
|
+
public readonly renderer: THREE.WebGLRenderer;
|
|
33
|
+
public readonly controls: OrbitControls;
|
|
34
|
+
public readonly domElement: HTMLElement;
|
|
35
|
+
|
|
36
|
+
public readonly selection: SelectionManager;
|
|
37
|
+
public readonly transform: TransformManager;
|
|
38
|
+
public readonly history: History;
|
|
39
|
+
|
|
40
|
+
private currentModel: THREE.Group | null = null;
|
|
41
|
+
private eventListeners: Map<EditorEvent, Set<EventCallback>> = new Map();
|
|
42
|
+
|
|
43
|
+
constructor(options: EditorOptions) {
|
|
44
|
+
this.scene = options.scene;
|
|
45
|
+
this.camera = options.camera;
|
|
46
|
+
this.renderer = options.renderer;
|
|
47
|
+
this.controls = options.controls;
|
|
48
|
+
this.domElement = options.domElement;
|
|
49
|
+
|
|
50
|
+
// Initialize subsystems
|
|
51
|
+
this.history = new History();
|
|
52
|
+
this.selection = new SelectionManager(this);
|
|
53
|
+
this.transform = new TransformManager(this);
|
|
54
|
+
|
|
55
|
+
// Forward selection events
|
|
56
|
+
this.selection.on("selectionChanged", (object: THREE.Object3D | null) => {
|
|
57
|
+
this.emit("selectionChanged", object);
|
|
58
|
+
this.transform.attach(object);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Forward transform events
|
|
62
|
+
this.transform.on("objectChanged", (...args: unknown[]) => {
|
|
63
|
+
const object = args[0] as THREE.Object3D;
|
|
64
|
+
const command = args[1] as Command;
|
|
65
|
+
this.history.execute(command);
|
|
66
|
+
this.emit("objectChanged", object);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Forward history events
|
|
70
|
+
this.history.on("historyChanged", () => {
|
|
71
|
+
this.emit("historyChanged");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Listen for mode changes
|
|
75
|
+
this.transform.on("modeChanged", (...args: unknown[]) => {
|
|
76
|
+
const mode = args[0] as string;
|
|
77
|
+
this.emit("modeChanged", mode);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Set the current model being edited
|
|
83
|
+
*/
|
|
84
|
+
setModel(model: THREE.Group | null): void {
|
|
85
|
+
this.currentModel = model;
|
|
86
|
+
this.selection.deselect();
|
|
87
|
+
this.history.clear();
|
|
88
|
+
this.emit("modelLoaded", model);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get the current model
|
|
93
|
+
*/
|
|
94
|
+
getModel(): THREE.Group | null {
|
|
95
|
+
return this.currentModel;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the currently selected object
|
|
100
|
+
*/
|
|
101
|
+
getSelected(): THREE.Object3D | null {
|
|
102
|
+
return this.selection.getSelected();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Select an object
|
|
107
|
+
*/
|
|
108
|
+
select(object: THREE.Object3D | null): void {
|
|
109
|
+
this.selection.select(object);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Execute a command (adds to history)
|
|
114
|
+
*/
|
|
115
|
+
execute(command: Command): void {
|
|
116
|
+
this.history.execute(command);
|
|
117
|
+
this.emit("objectChanged", this.getSelected());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Undo the last command
|
|
122
|
+
*/
|
|
123
|
+
undo(): void {
|
|
124
|
+
this.history.undo();
|
|
125
|
+
this.emit("objectChanged", this.getSelected());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Redo the last undone command
|
|
130
|
+
*/
|
|
131
|
+
redo(): void {
|
|
132
|
+
this.history.redo();
|
|
133
|
+
this.emit("objectChanged", this.getSelected());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if undo is available
|
|
138
|
+
*/
|
|
139
|
+
canUndo(): boolean {
|
|
140
|
+
return this.history.canUndo();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Check if redo is available
|
|
145
|
+
*/
|
|
146
|
+
canRedo(): boolean {
|
|
147
|
+
return this.history.canRedo();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Set transform mode
|
|
152
|
+
*/
|
|
153
|
+
setTransformMode(mode: "translate" | "rotate" | "scale"): void {
|
|
154
|
+
this.transform.setMode(mode);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get current transform mode
|
|
159
|
+
*/
|
|
160
|
+
getTransformMode(): "translate" | "rotate" | "scale" {
|
|
161
|
+
return this.transform.getMode();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Add event listener
|
|
166
|
+
*/
|
|
167
|
+
on(event: EditorEvent, callback: EventCallback): void {
|
|
168
|
+
if (!this.eventListeners.has(event)) {
|
|
169
|
+
this.eventListeners.set(event, new Set());
|
|
170
|
+
}
|
|
171
|
+
this.eventListeners.get(event)!.add(callback);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Remove event listener
|
|
176
|
+
*/
|
|
177
|
+
off(event: EditorEvent, callback: EventCallback): void {
|
|
178
|
+
this.eventListeners.get(event)?.delete(callback);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Emit an event
|
|
183
|
+
*/
|
|
184
|
+
emit(event: EditorEvent, ...args: unknown[]): void {
|
|
185
|
+
this.eventListeners.get(event)?.forEach((callback) => callback(...args));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Clean up resources
|
|
190
|
+
*/
|
|
191
|
+
dispose(): void {
|
|
192
|
+
this.selection.dispose();
|
|
193
|
+
this.transform.dispose();
|
|
194
|
+
this.eventListeners.clear();
|
|
195
|
+
}
|
|
196
|
+
}
|