@viniciusgoncalves/three-toolkit 0.1.5 → 0.1.7

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/README.md CHANGED
@@ -0,0 +1,8 @@
1
+ # @viniciusgoncalves/three-toolkit
2
+
3
+ Toolkit of utilities and extensions for ThreeJS.
4
+
5
+ ## Instalation
6
+
7
+ ```bash
8
+ npm install @viniciusgoncalves/three-toolkit
package/dist/index.cjs CHANGED
@@ -24,82 +24,193 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
- // src/selector.ts
28
- var import_three = require("three");
29
- var import_core_toolkit = require("core-toolkit");
30
- var Selector = class {
31
- // #endregion
32
- constructor(canvas, camera, objects = []) {
33
- this.enabled = true;
34
- this.selection = [];
35
- // #region Parameters
36
- this.filterVisible = true;
37
- this.clearSelectionOnSelectNothing = true;
38
- // #endregion
39
- // #region Callbacks
40
- this.trySelectCallback = (event) => {
41
- this.trySelect(event);
42
- };
43
- this.onAddToSelection = [];
44
- this.onRemoveFromSelection = [];
45
- this.canvas = canvas;
46
- this.canvas.addEventListener("pointerdown", this.trySelectCallback);
47
- this.camera = camera;
48
- this.objects = objects;
49
- this.raycaster = new import_three.Raycaster();
27
+ // src/selector/Selector.ts
28
+ var import_three3 = require("three");
29
+
30
+ // src/selector/SelectionModel.ts
31
+ var SelectionModel = class {
32
+ constructor() {
33
+ this.objects = [];
34
+ this.onAdd = [];
35
+ this.onRemove = [];
50
36
  }
51
- setCanvas(canvas) {
52
- this.canvas.removeEventListener("pointerdown", this.trySelectCallback);
53
- this.canvas = canvas;
54
- this.canvas.addEventListener("pointerdown", this.trySelectCallback);
55
- return this;
37
+ add(object) {
38
+ if (this.objects.some((o) => o.uuid === object.uuid)) return;
39
+ this.objects.push(object);
40
+ this.onAdd.forEach((cb) => cb(object));
56
41
  }
57
- setCamera(camera) {
58
- this.camera = camera;
59
- return this;
42
+ remove(object) {
43
+ const index = this.objects.findIndex((o) => o.uuid === object.uuid);
44
+ if (index === -1) return;
45
+ const [removed] = this.objects.splice(index, 1);
46
+ this.onRemove.forEach((cb) => cb(removed));
60
47
  }
61
- setObjects(objects) {
62
- this.objects.splice(0);
63
- this.objects.push(...objects);
64
- return this;
48
+ clear() {
49
+ [...this.objects].forEach((o) => this.remove(o));
65
50
  }
66
- setContext(canvas, camera, objects) {
67
- return this.setCanvas(canvas).setCamera(camera).setObjects(objects);
51
+ get isEmpty() {
52
+ return this.objects.length === 0;
68
53
  }
69
- addObjects(...objects) {
70
- this.objects.push(...objects);
71
- return this;
54
+ };
55
+
56
+ // src/selector/SelectionBounds.ts
57
+ var import_three = require("three");
58
+ var SelectionBounds = class {
59
+ static calculate(objects) {
60
+ if (objects.length === 0) return null;
61
+ const box = new import_three.Box3();
62
+ for (const obj of objects) {
63
+ box.expandByObject(obj);
64
+ }
65
+ return box.isEmpty() ? null : box;
72
66
  }
73
- addToSelection(object) {
74
- if (this.selection.some((object2) => object2.uuid === object2.uuid)) return this;
75
- this.selection.push(object);
76
- for (const callback of this.onAddToSelection) callback(object);
77
- return this;
67
+ };
68
+
69
+ // src/selector/SelectionGizmo.ts
70
+ var import_three2 = require("three");
71
+ var SelectionGizmo = class {
72
+ constructor(scene) {
73
+ this.gizmo = this.createGizmo();
74
+ scene.add(this.gizmo);
78
75
  }
79
- removeFromSelection(object) {
80
- const index = this.selection.findIndex((object2) => object2.uuid === object2.uuid);
81
- if (index === -1) return this;
82
- this.selection.push(object);
83
- for (const callback of this.onRemoveFromSelection) callback(object);
84
- return this;
76
+ createGizmo() {
77
+ const geometry = new import_three2.BufferGeometry();
78
+ geometry.setAttribute("position", new import_three2.BufferAttribute(new Float32Array(24 * 3), 3));
79
+ const material = new import_three2.LineBasicMaterial({ color: 16711680 });
80
+ const gizmo = new import_three2.LineSegments(geometry, material);
81
+ gizmo.visible = false;
82
+ gizmo.frustumCulled = false;
83
+ gizmo.name = "gizmo:selector";
84
+ return gizmo;
85
85
  }
86
- clearSelection() {
87
- this.selection.forEach((object) => this.removeFromSelection(object));
88
- return this;
86
+ update(box) {
87
+ if (!box) {
88
+ this.gizmo.visible = false;
89
+ return;
90
+ }
91
+ const { min, max } = box;
92
+ const vertices = [
93
+ min.x,
94
+ min.y,
95
+ min.z,
96
+ max.x,
97
+ min.y,
98
+ min.z,
99
+ max.x,
100
+ min.y,
101
+ min.z,
102
+ max.x,
103
+ max.y,
104
+ min.z,
105
+ max.x,
106
+ max.y,
107
+ min.z,
108
+ min.x,
109
+ max.y,
110
+ min.z,
111
+ min.x,
112
+ max.y,
113
+ min.z,
114
+ min.x,
115
+ min.y,
116
+ min.z,
117
+ min.x,
118
+ min.y,
119
+ max.z,
120
+ max.x,
121
+ min.y,
122
+ max.z,
123
+ max.x,
124
+ min.y,
125
+ max.z,
126
+ max.x,
127
+ max.y,
128
+ max.z,
129
+ max.x,
130
+ max.y,
131
+ max.z,
132
+ min.x,
133
+ max.y,
134
+ max.z,
135
+ min.x,
136
+ max.y,
137
+ max.z,
138
+ min.x,
139
+ min.y,
140
+ max.z,
141
+ min.x,
142
+ min.y,
143
+ min.z,
144
+ min.x,
145
+ min.y,
146
+ max.z,
147
+ max.x,
148
+ min.y,
149
+ min.z,
150
+ max.x,
151
+ min.y,
152
+ max.z,
153
+ max.x,
154
+ max.y,
155
+ min.z,
156
+ max.x,
157
+ max.y,
158
+ max.z,
159
+ min.x,
160
+ max.y,
161
+ min.z,
162
+ min.x,
163
+ max.y,
164
+ max.z
165
+ ];
166
+ const attr = this.gizmo.geometry.getAttribute("position");
167
+ attr.array.set(vertices);
168
+ attr.needsUpdate = true;
169
+ this.gizmo.visible = true;
170
+ }
171
+ };
172
+
173
+ // src/selector/Selector.ts
174
+ var Selector = class {
175
+ constructor(canvas, camera, scene, params = {
176
+ allowInvisible: false,
177
+ allowMultipleSelection: true,
178
+ allowAutoDeselection: true,
179
+ enableGizmo: true,
180
+ enableGizmoCalculateBoundingBox: true
181
+ }) {
182
+ this.canvas = canvas;
183
+ this.camera = camera;
184
+ this.scene = scene;
185
+ this.params = params;
186
+ this.enabled = true;
187
+ this.raycaster = new import_three3.Raycaster();
188
+ this.selection = new SelectionModel();
189
+ this.gizmo = new SelectionGizmo(scene);
190
+ this.canvas.addEventListener("pointerdown", (e) => this.trySelect(e));
89
191
  }
90
192
  trySelect(event) {
91
- if (!this.enabled || this.objects.length === 0) return null;
92
- const ndc = import_core_toolkit.Coordinates.getNormalizedDeviceCoordinates(event, this.canvas);
93
- const coordinates = new import_three.Vector2(ndc[0], ndc[1]);
94
- this.raycaster.setFromCamera(coordinates, this.camera);
95
- const objs = this.filterVisible ? this.objects.filter((obj) => obj.visible) : this.objects;
96
- const intersections = this.raycaster.intersectObjects(objs);
97
- if (intersections.length === 0) {
98
- if (this.clearSelectionOnSelectNothing) this.clearSelection();
193
+ if (!this.enabled) return null;
194
+ const rect = this.canvas.getBoundingClientRect();
195
+ const mouse = new import_three3.Vector2(
196
+ (event.clientX - rect.left) / rect.width * 2 - 1,
197
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
198
+ );
199
+ this.raycaster.setFromCamera(mouse, this.camera);
200
+ const objs = this.params.allowInvisible ? this.scene.children : this.scene.children.filter((o) => o.visible);
201
+ const hits = this.raycaster.intersectObjects(objs, true);
202
+ if (hits.length === 0) {
203
+ if (this.params.allowAutoDeselection) this.selection.clear();
204
+ this.updateGizmo();
99
205
  return null;
100
206
  }
101
- this.addToSelection(intersections[0].object);
102
- return intersections[0].object;
207
+ this.selection.add(hits[0].object);
208
+ this.updateGizmo();
209
+ return hits[0].object;
210
+ }
211
+ updateGizmo() {
212
+ const box = SelectionBounds.calculate(this.selection.objects);
213
+ this.gizmo.update(box);
103
214
  }
104
215
  };
105
216
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/selector.ts"],"sourcesContent":["export { Selector } from './selector.js';\n","import { Camera, Object3D, Raycaster, Vector2 } from 'three';\nimport { Coordinates } from 'core-toolkit';\n\nexport class Selector {\n public enabled: boolean = true;\n\n public readonly selection: Object3D[] = [];\n private canvas: HTMLCanvasElement;\n private camera: Camera;\n private readonly objects: Object3D[];\n private readonly raycaster: Raycaster;\n\n // #region Parameters\n public filterVisible: boolean = true;\n public clearSelectionOnSelectNothing: boolean = true;\n // #endregion\n\n // #region Callbacks\n private trySelectCallback = (event: PointerEvent) => {\n this.trySelect(event);\n };\n public onAddToSelection: ((object: Object3D) => void)[] = [];\n public onRemoveFromSelection: ((object: Object3D) => void)[] = [];\n // #endregion\n\n public constructor(canvas: HTMLCanvasElement, camera: Camera, objects: Object3D[] = []) {\n this.canvas = canvas;\n this.canvas.addEventListener('pointerdown', this.trySelectCallback);\n\n this.camera = camera;\n this.objects = objects;\n\n this.raycaster = new Raycaster();\n }\n\n public setCanvas(canvas: HTMLCanvasElement): Selector {\n this.canvas.removeEventListener('pointerdown', this.trySelectCallback);\n this.canvas = canvas;\n this.canvas.addEventListener('pointerdown', this.trySelectCallback);\n return this;\n }\n\n public setCamera(camera: Camera): Selector {\n this.camera = camera;\n return this;\n }\n\n public setObjects(objects: Object3D[]): Selector {\n this.objects.splice(0);\n this.objects.push(...objects);\n return this;\n }\n\n public setContext(canvas: HTMLCanvasElement, camera: Camera, objects: Object3D[]): Selector {\n return this.setCanvas(canvas).setCamera(camera).setObjects(objects);\n }\n\n public addObjects(...objects: Object3D[]): Selector {\n this.objects.push(...objects);\n return this;\n }\n\n public addToSelection(object: Object3D): Selector {\n if (this.selection.some((object) => object.uuid === object.uuid)) return this;\n\n this.selection.push(object);\n for (const callback of this.onAddToSelection) callback(object);\n return this;\n }\n\n public removeFromSelection(object: Object3D): Selector {\n const index = this.selection.findIndex((object) => object.uuid === object.uuid);\n if (index === -1) return this;\n\n this.selection.push(object);\n for (const callback of this.onRemoveFromSelection) callback(object);\n return this;\n }\n\n public clearSelection(): Selector {\n this.selection.forEach((object) => this.removeFromSelection(object));\n return this;\n }\n\n private trySelect(event: PointerEvent): Object3D | null {\n if (!this.enabled || this.objects.length === 0) return null;\n\n const ndc = Coordinates.getNormalizedDeviceCoordinates(event, this.canvas);\n const coordinates = new Vector2(ndc[0], ndc[1]);\n this.raycaster.setFromCamera(coordinates, this.camera);\n\n const objs = this.filterVisible ? this.objects.filter((obj) => obj.visible) : this.objects;\n const intersections = this.raycaster.intersectObjects(objs);\n\n if (intersections.length === 0) {\n if (this.clearSelectionOnSelectNothing) this.clearSelection();\n return null;\n }\n\n this.addToSelection(intersections[0].object);\n return intersections[0].object;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAqD;AACrD,0BAA4B;AAErB,IAAM,WAAN,MAAe;AAAA;AAAA,EAsBb,YAAY,QAA2B,QAAgB,UAAsB,CAAC,GAAG;AArBxF,SAAO,UAAmB;AAE1B,SAAgB,YAAwB,CAAC;AAOzC;AAAA,SAAO,gBAAyB;AAChC,SAAO,gCAAyC;AAIhD;AAAA;AAAA,SAAQ,oBAAoB,CAAC,UAAwB;AACnD,WAAK,UAAU,KAAK;AAAA,IACtB;AACA,SAAO,mBAAmD,CAAC;AAC3D,SAAO,wBAAwD,CAAC;AAI9D,SAAK,SAAS;AACd,SAAK,OAAO,iBAAiB,eAAe,KAAK,iBAAiB;AAElE,SAAK,SAAS;AACd,SAAK,UAAU;AAEf,SAAK,YAAY,IAAI,uBAAU;AAAA,EACjC;AAAA,EAEO,UAAU,QAAqC;AACpD,SAAK,OAAO,oBAAoB,eAAe,KAAK,iBAAiB;AACrE,SAAK,SAAS;AACd,SAAK,OAAO,iBAAiB,eAAe,KAAK,iBAAiB;AAClE,WAAO;AAAA,EACT;AAAA,EAEO,UAAU,QAA0B;AACzC,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEO,WAAW,SAA+B;AAC/C,SAAK,QAAQ,OAAO,CAAC;AACrB,SAAK,QAAQ,KAAK,GAAG,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA,EAEO,WAAW,QAA2B,QAAgB,SAA+B;AAC1F,WAAO,KAAK,UAAU,MAAM,EAAE,UAAU,MAAM,EAAE,WAAW,OAAO;AAAA,EACpE;AAAA,EAEO,cAAc,SAA+B;AAClD,SAAK,QAAQ,KAAK,GAAG,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA,EAEO,eAAe,QAA4B;AAChD,QAAI,KAAK,UAAU,KAAK,CAACA,YAAWA,QAAO,SAASA,QAAO,IAAI,EAAG,QAAO;AAEzE,SAAK,UAAU,KAAK,MAAM;AAC1B,eAAW,YAAY,KAAK,iBAAkB,UAAS,MAAM;AAC7D,WAAO;AAAA,EACT;AAAA,EAEO,oBAAoB,QAA4B;AACrD,UAAM,QAAQ,KAAK,UAAU,UAAU,CAACA,YAAWA,QAAO,SAASA,QAAO,IAAI;AAC9E,QAAI,UAAU,GAAI,QAAO;AAEzB,SAAK,UAAU,KAAK,MAAM;AAC1B,eAAW,YAAY,KAAK,sBAAuB,UAAS,MAAM;AAClE,WAAO;AAAA,EACT;AAAA,EAEO,iBAA2B;AAChC,SAAK,UAAU,QAAQ,CAAC,WAAW,KAAK,oBAAoB,MAAM,CAAC;AACnE,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAsC;AACtD,QAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,WAAW,EAAG,QAAO;AAEvD,UAAM,MAAM,gCAAY,+BAA+B,OAAO,KAAK,MAAM;AACzE,UAAM,cAAc,IAAI,qBAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9C,SAAK,UAAU,cAAc,aAAa,KAAK,MAAM;AAErD,UAAM,OAAO,KAAK,gBAAgB,KAAK,QAAQ,OAAO,CAAC,QAAQ,IAAI,OAAO,IAAI,KAAK;AACnF,UAAM,gBAAgB,KAAK,UAAU,iBAAiB,IAAI;AAE1D,QAAI,cAAc,WAAW,GAAG;AAC9B,UAAI,KAAK,8BAA+B,MAAK,eAAe;AAC5D,aAAO;AAAA,IACT;AAEA,SAAK,eAAe,cAAc,CAAC,EAAE,MAAM;AAC3C,WAAO,cAAc,CAAC,EAAE;AAAA,EAC1B;AACF;","names":["object"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/selector/Selector.ts","../src/selector/SelectionModel.ts","../src/selector/SelectionBounds.ts","../src/selector/SelectionGizmo.ts"],"sourcesContent":["export { Selector } from './selector/Selector.js';\n","import { Camera, Object3D, Raycaster, Scene, Vector2 } from 'three';\nimport { SelectionModel } from './SelectionModel.js';\nimport { SelectionBounds } from './SelectionBounds.js';\nimport { SelectionGizmo } from './SelectionGizmo.js';\nimport { ISelectorParameters } from './SelectorParameters.js';\n\nexport class Selector {\n private enabled = true;\n\n private raycaster = new Raycaster();\n private selection = new SelectionModel();\n private gizmo: SelectionGizmo;\n\n public constructor(\n private canvas: HTMLCanvasElement,\n private camera: Camera,\n private scene: Scene,\n private params: ISelectorParameters = {\n allowInvisible: false,\n allowMultipleSelection: true,\n allowAutoDeselection: true,\n enableGizmo: true,\n enableGizmoCalculateBoundingBox: true,\n },\n ) {\n this.gizmo = new SelectionGizmo(scene);\n this.canvas.addEventListener('pointerdown', (e) => this.trySelect(e));\n }\n\n private trySelect(event: PointerEvent): Object3D | null {\n if (!this.enabled) return null;\n\n const rect = this.canvas.getBoundingClientRect();\n const mouse = new Vector2(\n ((event.clientX - rect.left) / rect.width) * 2 - 1,\n -((event.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n this.raycaster.setFromCamera(mouse, this.camera);\n\n const objs = this.params.allowInvisible\n ? this.scene.children\n : this.scene.children.filter((o) => o.visible);\n\n const hits = this.raycaster.intersectObjects(objs, true);\n\n if (hits.length === 0) {\n if (this.params.allowAutoDeselection) this.selection.clear();\n this.updateGizmo();\n return null;\n }\n\n this.selection.add(hits[0].object);\n this.updateGizmo();\n return hits[0].object;\n }\n\n private updateGizmo(): void {\n const box = SelectionBounds.calculate(this.selection.objects);\n this.gizmo.update(box);\n }\n}\n","import { Object3D } from 'three';\n\nexport class SelectionModel {\n readonly objects: Object3D[] = [];\n\n onAdd: ((o: Object3D) => void)[] = [];\n onRemove: ((o: Object3D) => void)[] = [];\n\n public add(object: Object3D): void {\n if (this.objects.some((o) => o.uuid === object.uuid)) return;\n this.objects.push(object);\n this.onAdd.forEach((cb) => cb(object));\n }\n\n public remove(object: Object3D): void {\n const index = this.objects.findIndex((o) => o.uuid === object.uuid);\n if (index === -1) return;\n\n const [removed] = this.objects.splice(index, 1);\n this.onRemove.forEach((cb) => cb(removed));\n }\n\n public clear(): void {\n [...this.objects].forEach((o) => this.remove(o));\n }\n\n public get isEmpty(): boolean {\n return this.objects.length === 0;\n }\n}\n","import { Box3, Object3D } from 'three';\n\nexport class SelectionBounds {\n public static calculate(objects: Object3D[]): Box3 | null {\n if (objects.length === 0) return null;\n\n const box = new Box3();\n for (const obj of objects) {\n box.expandByObject(obj);\n }\n\n return box.isEmpty() ? null : box;\n }\n}\n","import {\n Box3,\n BufferAttribute,\n BufferGeometry,\n LineBasicMaterial,\n LineSegments,\n Scene,\n} from 'three';\n\nexport class SelectionGizmo {\n private gizmo: LineSegments;\n\n public constructor(scene: Scene) {\n this.gizmo = this.createGizmo();\n scene.add(this.gizmo);\n }\n\n private createGizmo(): LineSegments {\n const geometry = new BufferGeometry();\n geometry.setAttribute('position', new BufferAttribute(new Float32Array(24 * 3), 3));\n\n const material = new LineBasicMaterial({ color: 0xff0000 });\n const gizmo = new LineSegments(geometry, material);\n\n gizmo.visible = false;\n gizmo.frustumCulled = false;\n gizmo.name = 'gizmo:selector';\n\n return gizmo;\n }\n\n public update(box: Box3 | null): void {\n if (!box) {\n this.gizmo.visible = false;\n return;\n }\n\n const { min, max } = box;\n\n const vertices = [\n min.x,\n min.y,\n min.z,\n max.x,\n min.y,\n min.z,\n max.x,\n min.y,\n min.z,\n max.x,\n max.y,\n min.z,\n max.x,\n max.y,\n min.z,\n min.x,\n max.y,\n min.z,\n min.x,\n max.y,\n min.z,\n min.x,\n min.y,\n min.z,\n\n min.x,\n min.y,\n max.z,\n max.x,\n min.y,\n max.z,\n max.x,\n min.y,\n max.z,\n max.x,\n max.y,\n max.z,\n max.x,\n max.y,\n max.z,\n min.x,\n max.y,\n max.z,\n min.x,\n max.y,\n max.z,\n min.x,\n min.y,\n max.z,\n\n min.x,\n min.y,\n min.z,\n min.x,\n min.y,\n max.z,\n max.x,\n min.y,\n min.z,\n max.x,\n min.y,\n max.z,\n max.x,\n max.y,\n min.z,\n max.x,\n max.y,\n max.z,\n min.x,\n max.y,\n min.z,\n min.x,\n max.y,\n max.z,\n ];\n\n const attr = this.gizmo.geometry.getAttribute('position') as BufferAttribute;\n attr.array.set(vertices);\n attr.needsUpdate = true;\n\n this.gizmo.visible = true;\n }\n}\n"],"mappings":"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","names":["import_three","import_three"]}
package/dist/index.d.cts CHANGED
@@ -1,27 +1,25 @@
1
- import { Object3D, Camera } from 'three';
1
+ import { Camera, Scene } from 'three';
2
+
3
+ interface ISelectorParameters {
4
+ allowInvisible: boolean;
5
+ allowMultipleSelection: boolean;
6
+ allowAutoDeselection: boolean;
7
+ enableGizmo: boolean;
8
+ enableGizmoCalculateBoundingBox: boolean;
9
+ }
2
10
 
3
11
  declare class Selector {
4
- enabled: boolean;
5
- readonly selection: Object3D[];
6
12
  private canvas;
7
13
  private camera;
8
- private readonly objects;
9
- private readonly raycaster;
10
- filterVisible: boolean;
11
- clearSelectionOnSelectNothing: boolean;
12
- private trySelectCallback;
13
- onAddToSelection: ((object: Object3D) => void)[];
14
- onRemoveFromSelection: ((object: Object3D) => void)[];
15
- constructor(canvas: HTMLCanvasElement, camera: Camera, objects?: Object3D[]);
16
- setCanvas(canvas: HTMLCanvasElement): Selector;
17
- setCamera(camera: Camera): Selector;
18
- setObjects(objects: Object3D[]): Selector;
19
- setContext(canvas: HTMLCanvasElement, camera: Camera, objects: Object3D[]): Selector;
20
- addObjects(...objects: Object3D[]): Selector;
21
- addToSelection(object: Object3D): Selector;
22
- removeFromSelection(object: Object3D): Selector;
23
- clearSelection(): Selector;
14
+ private scene;
15
+ private params;
16
+ private enabled;
17
+ private raycaster;
18
+ private selection;
19
+ private gizmo;
20
+ constructor(canvas: HTMLCanvasElement, camera: Camera, scene: Scene, params?: ISelectorParameters);
24
21
  private trySelect;
22
+ private updateGizmo;
25
23
  }
26
24
 
27
25
  export { Selector };
package/dist/index.d.ts CHANGED
@@ -1,27 +1,25 @@
1
- import { Object3D, Camera } from 'three';
1
+ import { Camera, Scene } from 'three';
2
+
3
+ interface ISelectorParameters {
4
+ allowInvisible: boolean;
5
+ allowMultipleSelection: boolean;
6
+ allowAutoDeselection: boolean;
7
+ enableGizmo: boolean;
8
+ enableGizmoCalculateBoundingBox: boolean;
9
+ }
2
10
 
3
11
  declare class Selector {
4
- enabled: boolean;
5
- readonly selection: Object3D[];
6
12
  private canvas;
7
13
  private camera;
8
- private readonly objects;
9
- private readonly raycaster;
10
- filterVisible: boolean;
11
- clearSelectionOnSelectNothing: boolean;
12
- private trySelectCallback;
13
- onAddToSelection: ((object: Object3D) => void)[];
14
- onRemoveFromSelection: ((object: Object3D) => void)[];
15
- constructor(canvas: HTMLCanvasElement, camera: Camera, objects?: Object3D[]);
16
- setCanvas(canvas: HTMLCanvasElement): Selector;
17
- setCamera(camera: Camera): Selector;
18
- setObjects(objects: Object3D[]): Selector;
19
- setContext(canvas: HTMLCanvasElement, camera: Camera, objects: Object3D[]): Selector;
20
- addObjects(...objects: Object3D[]): Selector;
21
- addToSelection(object: Object3D): Selector;
22
- removeFromSelection(object: Object3D): Selector;
23
- clearSelection(): Selector;
14
+ private scene;
15
+ private params;
16
+ private enabled;
17
+ private raycaster;
18
+ private selection;
19
+ private gizmo;
20
+ constructor(canvas: HTMLCanvasElement, camera: Camera, scene: Scene, params?: ISelectorParameters);
24
21
  private trySelect;
22
+ private updateGizmo;
25
23
  }
26
24
 
27
25
  export { Selector };
package/dist/index.js CHANGED
@@ -1,79 +1,195 @@
1
- // src/selector.ts
1
+ // src/selector/Selector.ts
2
2
  import { Raycaster, Vector2 } from "three";
3
- import { Coordinates } from "core-toolkit";
4
- var Selector = class {
5
- // #endregion
6
- constructor(canvas, camera, objects = []) {
7
- this.enabled = true;
8
- this.selection = [];
9
- // #region Parameters
10
- this.filterVisible = true;
11
- this.clearSelectionOnSelectNothing = true;
12
- // #endregion
13
- // #region Callbacks
14
- this.trySelectCallback = (event) => {
15
- this.trySelect(event);
16
- };
17
- this.onAddToSelection = [];
18
- this.onRemoveFromSelection = [];
19
- this.canvas = canvas;
20
- this.canvas.addEventListener("pointerdown", this.trySelectCallback);
21
- this.camera = camera;
22
- this.objects = objects;
23
- this.raycaster = new Raycaster();
3
+
4
+ // src/selector/SelectionModel.ts
5
+ var SelectionModel = class {
6
+ constructor() {
7
+ this.objects = [];
8
+ this.onAdd = [];
9
+ this.onRemove = [];
24
10
  }
25
- setCanvas(canvas) {
26
- this.canvas.removeEventListener("pointerdown", this.trySelectCallback);
27
- this.canvas = canvas;
28
- this.canvas.addEventListener("pointerdown", this.trySelectCallback);
29
- return this;
11
+ add(object) {
12
+ if (this.objects.some((o) => o.uuid === object.uuid)) return;
13
+ this.objects.push(object);
14
+ this.onAdd.forEach((cb) => cb(object));
30
15
  }
31
- setCamera(camera) {
32
- this.camera = camera;
33
- return this;
16
+ remove(object) {
17
+ const index = this.objects.findIndex((o) => o.uuid === object.uuid);
18
+ if (index === -1) return;
19
+ const [removed] = this.objects.splice(index, 1);
20
+ this.onRemove.forEach((cb) => cb(removed));
34
21
  }
35
- setObjects(objects) {
36
- this.objects.splice(0);
37
- this.objects.push(...objects);
38
- return this;
22
+ clear() {
23
+ [...this.objects].forEach((o) => this.remove(o));
39
24
  }
40
- setContext(canvas, camera, objects) {
41
- return this.setCanvas(canvas).setCamera(camera).setObjects(objects);
25
+ get isEmpty() {
26
+ return this.objects.length === 0;
42
27
  }
43
- addObjects(...objects) {
44
- this.objects.push(...objects);
45
- return this;
28
+ };
29
+
30
+ // src/selector/SelectionBounds.ts
31
+ import { Box3 } from "three";
32
+ var SelectionBounds = class {
33
+ static calculate(objects) {
34
+ if (objects.length === 0) return null;
35
+ const box = new Box3();
36
+ for (const obj of objects) {
37
+ box.expandByObject(obj);
38
+ }
39
+ return box.isEmpty() ? null : box;
46
40
  }
47
- addToSelection(object) {
48
- if (this.selection.some((object2) => object2.uuid === object2.uuid)) return this;
49
- this.selection.push(object);
50
- for (const callback of this.onAddToSelection) callback(object);
51
- return this;
41
+ };
42
+
43
+ // src/selector/SelectionGizmo.ts
44
+ import {
45
+ BufferAttribute,
46
+ BufferGeometry,
47
+ LineBasicMaterial,
48
+ LineSegments
49
+ } from "three";
50
+ var SelectionGizmo = class {
51
+ constructor(scene) {
52
+ this.gizmo = this.createGizmo();
53
+ scene.add(this.gizmo);
52
54
  }
53
- removeFromSelection(object) {
54
- const index = this.selection.findIndex((object2) => object2.uuid === object2.uuid);
55
- if (index === -1) return this;
56
- this.selection.push(object);
57
- for (const callback of this.onRemoveFromSelection) callback(object);
58
- return this;
55
+ createGizmo() {
56
+ const geometry = new BufferGeometry();
57
+ geometry.setAttribute("position", new BufferAttribute(new Float32Array(24 * 3), 3));
58
+ const material = new LineBasicMaterial({ color: 16711680 });
59
+ const gizmo = new LineSegments(geometry, material);
60
+ gizmo.visible = false;
61
+ gizmo.frustumCulled = false;
62
+ gizmo.name = "gizmo:selector";
63
+ return gizmo;
59
64
  }
60
- clearSelection() {
61
- this.selection.forEach((object) => this.removeFromSelection(object));
62
- return this;
65
+ update(box) {
66
+ if (!box) {
67
+ this.gizmo.visible = false;
68
+ return;
69
+ }
70
+ const { min, max } = box;
71
+ const vertices = [
72
+ min.x,
73
+ min.y,
74
+ min.z,
75
+ max.x,
76
+ min.y,
77
+ min.z,
78
+ max.x,
79
+ min.y,
80
+ min.z,
81
+ max.x,
82
+ max.y,
83
+ min.z,
84
+ max.x,
85
+ max.y,
86
+ min.z,
87
+ min.x,
88
+ max.y,
89
+ min.z,
90
+ min.x,
91
+ max.y,
92
+ min.z,
93
+ min.x,
94
+ min.y,
95
+ min.z,
96
+ min.x,
97
+ min.y,
98
+ max.z,
99
+ max.x,
100
+ min.y,
101
+ max.z,
102
+ max.x,
103
+ min.y,
104
+ max.z,
105
+ max.x,
106
+ max.y,
107
+ max.z,
108
+ max.x,
109
+ max.y,
110
+ max.z,
111
+ min.x,
112
+ max.y,
113
+ max.z,
114
+ min.x,
115
+ max.y,
116
+ max.z,
117
+ min.x,
118
+ min.y,
119
+ max.z,
120
+ min.x,
121
+ min.y,
122
+ min.z,
123
+ min.x,
124
+ min.y,
125
+ max.z,
126
+ max.x,
127
+ min.y,
128
+ min.z,
129
+ max.x,
130
+ min.y,
131
+ max.z,
132
+ max.x,
133
+ max.y,
134
+ min.z,
135
+ max.x,
136
+ max.y,
137
+ max.z,
138
+ min.x,
139
+ max.y,
140
+ min.z,
141
+ min.x,
142
+ max.y,
143
+ max.z
144
+ ];
145
+ const attr = this.gizmo.geometry.getAttribute("position");
146
+ attr.array.set(vertices);
147
+ attr.needsUpdate = true;
148
+ this.gizmo.visible = true;
149
+ }
150
+ };
151
+
152
+ // src/selector/Selector.ts
153
+ var Selector = class {
154
+ constructor(canvas, camera, scene, params = {
155
+ allowInvisible: false,
156
+ allowMultipleSelection: true,
157
+ allowAutoDeselection: true,
158
+ enableGizmo: true,
159
+ enableGizmoCalculateBoundingBox: true
160
+ }) {
161
+ this.canvas = canvas;
162
+ this.camera = camera;
163
+ this.scene = scene;
164
+ this.params = params;
165
+ this.enabled = true;
166
+ this.raycaster = new Raycaster();
167
+ this.selection = new SelectionModel();
168
+ this.gizmo = new SelectionGizmo(scene);
169
+ this.canvas.addEventListener("pointerdown", (e) => this.trySelect(e));
63
170
  }
64
171
  trySelect(event) {
65
- if (!this.enabled || this.objects.length === 0) return null;
66
- const ndc = Coordinates.getNormalizedDeviceCoordinates(event, this.canvas);
67
- const coordinates = new Vector2(ndc[0], ndc[1]);
68
- this.raycaster.setFromCamera(coordinates, this.camera);
69
- const objs = this.filterVisible ? this.objects.filter((obj) => obj.visible) : this.objects;
70
- const intersections = this.raycaster.intersectObjects(objs);
71
- if (intersections.length === 0) {
72
- if (this.clearSelectionOnSelectNothing) this.clearSelection();
172
+ if (!this.enabled) return null;
173
+ const rect = this.canvas.getBoundingClientRect();
174
+ const mouse = new Vector2(
175
+ (event.clientX - rect.left) / rect.width * 2 - 1,
176
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
177
+ );
178
+ this.raycaster.setFromCamera(mouse, this.camera);
179
+ const objs = this.params.allowInvisible ? this.scene.children : this.scene.children.filter((o) => o.visible);
180
+ const hits = this.raycaster.intersectObjects(objs, true);
181
+ if (hits.length === 0) {
182
+ if (this.params.allowAutoDeselection) this.selection.clear();
183
+ this.updateGizmo();
73
184
  return null;
74
185
  }
75
- this.addToSelection(intersections[0].object);
76
- return intersections[0].object;
186
+ this.selection.add(hits[0].object);
187
+ this.updateGizmo();
188
+ return hits[0].object;
189
+ }
190
+ updateGizmo() {
191
+ const box = SelectionBounds.calculate(this.selection.objects);
192
+ this.gizmo.update(box);
77
193
  }
78
194
  };
79
195
  export {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/selector.ts"],"sourcesContent":["import { Camera, Object3D, Raycaster, Vector2 } from 'three';\nimport { Coordinates } from 'core-toolkit';\n\nexport class Selector {\n public enabled: boolean = true;\n\n public readonly selection: Object3D[] = [];\n private canvas: HTMLCanvasElement;\n private camera: Camera;\n private readonly objects: Object3D[];\n private readonly raycaster: Raycaster;\n\n // #region Parameters\n public filterVisible: boolean = true;\n public clearSelectionOnSelectNothing: boolean = true;\n // #endregion\n\n // #region Callbacks\n private trySelectCallback = (event: PointerEvent) => {\n this.trySelect(event);\n };\n public onAddToSelection: ((object: Object3D) => void)[] = [];\n public onRemoveFromSelection: ((object: Object3D) => void)[] = [];\n // #endregion\n\n public constructor(canvas: HTMLCanvasElement, camera: Camera, objects: Object3D[] = []) {\n this.canvas = canvas;\n this.canvas.addEventListener('pointerdown', this.trySelectCallback);\n\n this.camera = camera;\n this.objects = objects;\n\n this.raycaster = new Raycaster();\n }\n\n public setCanvas(canvas: HTMLCanvasElement): Selector {\n this.canvas.removeEventListener('pointerdown', this.trySelectCallback);\n this.canvas = canvas;\n this.canvas.addEventListener('pointerdown', this.trySelectCallback);\n return this;\n }\n\n public setCamera(camera: Camera): Selector {\n this.camera = camera;\n return this;\n }\n\n public setObjects(objects: Object3D[]): Selector {\n this.objects.splice(0);\n this.objects.push(...objects);\n return this;\n }\n\n public setContext(canvas: HTMLCanvasElement, camera: Camera, objects: Object3D[]): Selector {\n return this.setCanvas(canvas).setCamera(camera).setObjects(objects);\n }\n\n public addObjects(...objects: Object3D[]): Selector {\n this.objects.push(...objects);\n return this;\n }\n\n public addToSelection(object: Object3D): Selector {\n if (this.selection.some((object) => object.uuid === object.uuid)) return this;\n\n this.selection.push(object);\n for (const callback of this.onAddToSelection) callback(object);\n return this;\n }\n\n public removeFromSelection(object: Object3D): Selector {\n const index = this.selection.findIndex((object) => object.uuid === object.uuid);\n if (index === -1) return this;\n\n this.selection.push(object);\n for (const callback of this.onRemoveFromSelection) callback(object);\n return this;\n }\n\n public clearSelection(): Selector {\n this.selection.forEach((object) => this.removeFromSelection(object));\n return this;\n }\n\n private trySelect(event: PointerEvent): Object3D | null {\n if (!this.enabled || this.objects.length === 0) return null;\n\n const ndc = Coordinates.getNormalizedDeviceCoordinates(event, this.canvas);\n const coordinates = new Vector2(ndc[0], ndc[1]);\n this.raycaster.setFromCamera(coordinates, this.camera);\n\n const objs = this.filterVisible ? this.objects.filter((obj) => obj.visible) : this.objects;\n const intersections = this.raycaster.intersectObjects(objs);\n\n if (intersections.length === 0) {\n if (this.clearSelectionOnSelectNothing) this.clearSelection();\n return null;\n }\n\n this.addToSelection(intersections[0].object);\n return intersections[0].object;\n }\n}\n"],"mappings":";AAAA,SAA2B,WAAW,eAAe;AACrD,SAAS,mBAAmB;AAErB,IAAM,WAAN,MAAe;AAAA;AAAA,EAsBb,YAAY,QAA2B,QAAgB,UAAsB,CAAC,GAAG;AArBxF,SAAO,UAAmB;AAE1B,SAAgB,YAAwB,CAAC;AAOzC;AAAA,SAAO,gBAAyB;AAChC,SAAO,gCAAyC;AAIhD;AAAA;AAAA,SAAQ,oBAAoB,CAAC,UAAwB;AACnD,WAAK,UAAU,KAAK;AAAA,IACtB;AACA,SAAO,mBAAmD,CAAC;AAC3D,SAAO,wBAAwD,CAAC;AAI9D,SAAK,SAAS;AACd,SAAK,OAAO,iBAAiB,eAAe,KAAK,iBAAiB;AAElE,SAAK,SAAS;AACd,SAAK,UAAU;AAEf,SAAK,YAAY,IAAI,UAAU;AAAA,EACjC;AAAA,EAEO,UAAU,QAAqC;AACpD,SAAK,OAAO,oBAAoB,eAAe,KAAK,iBAAiB;AACrE,SAAK,SAAS;AACd,SAAK,OAAO,iBAAiB,eAAe,KAAK,iBAAiB;AAClE,WAAO;AAAA,EACT;AAAA,EAEO,UAAU,QAA0B;AACzC,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEO,WAAW,SAA+B;AAC/C,SAAK,QAAQ,OAAO,CAAC;AACrB,SAAK,QAAQ,KAAK,GAAG,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA,EAEO,WAAW,QAA2B,QAAgB,SAA+B;AAC1F,WAAO,KAAK,UAAU,MAAM,EAAE,UAAU,MAAM,EAAE,WAAW,OAAO;AAAA,EACpE;AAAA,EAEO,cAAc,SAA+B;AAClD,SAAK,QAAQ,KAAK,GAAG,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA,EAEO,eAAe,QAA4B;AAChD,QAAI,KAAK,UAAU,KAAK,CAACA,YAAWA,QAAO,SAASA,QAAO,IAAI,EAAG,QAAO;AAEzE,SAAK,UAAU,KAAK,MAAM;AAC1B,eAAW,YAAY,KAAK,iBAAkB,UAAS,MAAM;AAC7D,WAAO;AAAA,EACT;AAAA,EAEO,oBAAoB,QAA4B;AACrD,UAAM,QAAQ,KAAK,UAAU,UAAU,CAACA,YAAWA,QAAO,SAASA,QAAO,IAAI;AAC9E,QAAI,UAAU,GAAI,QAAO;AAEzB,SAAK,UAAU,KAAK,MAAM;AAC1B,eAAW,YAAY,KAAK,sBAAuB,UAAS,MAAM;AAClE,WAAO;AAAA,EACT;AAAA,EAEO,iBAA2B;AAChC,SAAK,UAAU,QAAQ,CAAC,WAAW,KAAK,oBAAoB,MAAM,CAAC;AACnE,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAsC;AACtD,QAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,WAAW,EAAG,QAAO;AAEvD,UAAM,MAAM,YAAY,+BAA+B,OAAO,KAAK,MAAM;AACzE,UAAM,cAAc,IAAI,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9C,SAAK,UAAU,cAAc,aAAa,KAAK,MAAM;AAErD,UAAM,OAAO,KAAK,gBAAgB,KAAK,QAAQ,OAAO,CAAC,QAAQ,IAAI,OAAO,IAAI,KAAK;AACnF,UAAM,gBAAgB,KAAK,UAAU,iBAAiB,IAAI;AAE1D,QAAI,cAAc,WAAW,GAAG;AAC9B,UAAI,KAAK,8BAA+B,MAAK,eAAe;AAC5D,aAAO;AAAA,IACT;AAEA,SAAK,eAAe,cAAc,CAAC,EAAE,MAAM;AAC3C,WAAO,cAAc,CAAC,EAAE;AAAA,EAC1B;AACF;","names":["object"]}
1
+ {"version":3,"sources":["../src/selector/Selector.ts","../src/selector/SelectionModel.ts","../src/selector/SelectionBounds.ts","../src/selector/SelectionGizmo.ts"],"sourcesContent":["import { Camera, Object3D, Raycaster, Scene, Vector2 } from 'three';\nimport { SelectionModel } from './SelectionModel.js';\nimport { SelectionBounds } from './SelectionBounds.js';\nimport { SelectionGizmo } from './SelectionGizmo.js';\nimport { ISelectorParameters } from './SelectorParameters.js';\n\nexport class Selector {\n private enabled = true;\n\n private raycaster = new Raycaster();\n private selection = new SelectionModel();\n private gizmo: SelectionGizmo;\n\n public constructor(\n private canvas: HTMLCanvasElement,\n private camera: Camera,\n private scene: Scene,\n private params: ISelectorParameters = {\n allowInvisible: false,\n allowMultipleSelection: true,\n allowAutoDeselection: true,\n enableGizmo: true,\n enableGizmoCalculateBoundingBox: true,\n },\n ) {\n this.gizmo = new SelectionGizmo(scene);\n this.canvas.addEventListener('pointerdown', (e) => this.trySelect(e));\n }\n\n private trySelect(event: PointerEvent): Object3D | null {\n if (!this.enabled) return null;\n\n const rect = this.canvas.getBoundingClientRect();\n const mouse = new Vector2(\n ((event.clientX - rect.left) / rect.width) * 2 - 1,\n -((event.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n this.raycaster.setFromCamera(mouse, this.camera);\n\n const objs = this.params.allowInvisible\n ? this.scene.children\n : this.scene.children.filter((o) => o.visible);\n\n const hits = this.raycaster.intersectObjects(objs, true);\n\n if (hits.length === 0) {\n if (this.params.allowAutoDeselection) this.selection.clear();\n this.updateGizmo();\n return null;\n }\n\n this.selection.add(hits[0].object);\n this.updateGizmo();\n return hits[0].object;\n }\n\n private updateGizmo(): void {\n const box = SelectionBounds.calculate(this.selection.objects);\n this.gizmo.update(box);\n }\n}\n","import { Object3D } from 'three';\n\nexport class SelectionModel {\n readonly objects: Object3D[] = [];\n\n onAdd: ((o: Object3D) => void)[] = [];\n onRemove: ((o: Object3D) => void)[] = [];\n\n public add(object: Object3D): void {\n if (this.objects.some((o) => o.uuid === object.uuid)) return;\n this.objects.push(object);\n this.onAdd.forEach((cb) => cb(object));\n }\n\n public remove(object: Object3D): void {\n const index = this.objects.findIndex((o) => o.uuid === object.uuid);\n if (index === -1) return;\n\n const [removed] = this.objects.splice(index, 1);\n this.onRemove.forEach((cb) => cb(removed));\n }\n\n public clear(): void {\n [...this.objects].forEach((o) => this.remove(o));\n }\n\n public get isEmpty(): boolean {\n return this.objects.length === 0;\n }\n}\n","import { Box3, Object3D } from 'three';\n\nexport class SelectionBounds {\n public static calculate(objects: Object3D[]): Box3 | null {\n if (objects.length === 0) return null;\n\n const box = new Box3();\n for (const obj of objects) {\n box.expandByObject(obj);\n }\n\n return box.isEmpty() ? null : box;\n }\n}\n","import {\n Box3,\n BufferAttribute,\n BufferGeometry,\n LineBasicMaterial,\n LineSegments,\n Scene,\n} from 'three';\n\nexport class SelectionGizmo {\n private gizmo: LineSegments;\n\n public constructor(scene: Scene) {\n this.gizmo = this.createGizmo();\n scene.add(this.gizmo);\n }\n\n private createGizmo(): LineSegments {\n const geometry = new BufferGeometry();\n geometry.setAttribute('position', new BufferAttribute(new Float32Array(24 * 3), 3));\n\n const material = new LineBasicMaterial({ color: 0xff0000 });\n const gizmo = new LineSegments(geometry, material);\n\n gizmo.visible = false;\n gizmo.frustumCulled = false;\n gizmo.name = 'gizmo:selector';\n\n return gizmo;\n }\n\n public update(box: Box3 | null): void {\n if (!box) {\n this.gizmo.visible = false;\n return;\n }\n\n const { min, max } = box;\n\n const vertices = [\n min.x,\n min.y,\n min.z,\n max.x,\n min.y,\n min.z,\n max.x,\n min.y,\n min.z,\n max.x,\n max.y,\n min.z,\n max.x,\n max.y,\n min.z,\n min.x,\n max.y,\n min.z,\n min.x,\n max.y,\n min.z,\n min.x,\n min.y,\n min.z,\n\n min.x,\n min.y,\n max.z,\n max.x,\n min.y,\n max.z,\n max.x,\n min.y,\n max.z,\n max.x,\n max.y,\n max.z,\n max.x,\n max.y,\n max.z,\n min.x,\n max.y,\n max.z,\n min.x,\n max.y,\n max.z,\n min.x,\n min.y,\n max.z,\n\n min.x,\n min.y,\n min.z,\n min.x,\n min.y,\n max.z,\n max.x,\n min.y,\n min.z,\n max.x,\n min.y,\n max.z,\n max.x,\n max.y,\n min.z,\n max.x,\n max.y,\n max.z,\n min.x,\n max.y,\n min.z,\n min.x,\n max.y,\n max.z,\n ];\n\n const attr = this.gizmo.geometry.getAttribute('position') as BufferAttribute;\n attr.array.set(vertices);\n attr.needsUpdate = true;\n\n this.gizmo.visible = true;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@viniciusgoncalves/three-toolkit",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
+ "description": "Toolkit of utilities and extensions for ThreeJS",
4
5
  "main": "dist/index.cjs",
5
- "module": "dist/index.mjs",
6
+ "module": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "type": "module",
8
9
  "files": [
@@ -16,44 +17,44 @@
16
17
  }
17
18
  },
18
19
  "scripts": {
19
- "build": "tsup",
20
+ "dev": "vite serve examples",
21
+ "build": "tsup src/index.ts --format esm,cjs --dts",
20
22
  "build:watch": "tsup --watch",
21
23
  "lint": "eslint \"src/**/*.{ts,tsx}\" --fix",
22
24
  "format": "prettier --write \"src/**/*.{ts,tsx,js,json,md}\"",
23
25
  "fix": "npm run format && npm run lint",
24
- "clean": "rimraf dist"
26
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\""
25
27
  },
26
28
  "keywords": [
29
+ "three",
27
30
  "threejs",
28
31
  "toolkit",
29
32
  "typescript"
30
33
  ],
31
34
  "author": "Vinicius Gonçalves <viniciusgoncalves2000@hotmail.com>",
32
35
  "license": "MIT",
33
- "description": "",
34
36
  "peerDependencies": {
35
37
  "three": ">=0.180.0"
36
38
  },
37
39
  "devDependencies": {
38
40
  "@eslint/js": "^9.39.1",
39
41
  "@types/node": "^24.10.1",
42
+ "@types/three": "^0.180.0",
40
43
  "@typescript-eslint/eslint-plugin": "^8.48.0",
41
44
  "@typescript-eslint/parser": "^8.48.0",
42
- "@types/three": ">=0.180.0",
43
45
  "eslint": "^9.39.1",
44
46
  "eslint-config-prettier": "^10.1.8",
45
47
  "eslint-plugin-import": "^2.32.0",
46
48
  "prettier": "^3.7.2",
49
+ "three": "^0.180.0",
47
50
  "tsup": "^8.5.1",
48
51
  "typescript": "^5.9.3",
49
52
  "typescript-eslint": "^8.48.0",
50
- "three": ">=0.180.0"
53
+ "vite": "^7.3.1"
51
54
  },
52
55
  "repository": {
53
56
  "type": "git",
54
57
  "url": "git+https://github.com/ViniciusGoncalves00/threejs-toolkit.git"
55
58
  },
56
- "dependencies": {
57
- "core-toolkit": "^0.1.1"
58
- }
59
+ "sideEffects": false
59
60
  }