@thanhthbm/scene-editor 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 (136) hide show
  1. package/README.md +169 -0
  2. package/dist/index.css +921 -0
  3. package/dist/index.js +14076 -0
  4. package/package.json +49 -0
  5. package/src/editor/css/main.css +1030 -0
  6. package/src/editor/js/Animation.js +618 -0
  7. package/src/editor/js/AnimationResizer.js +73 -0
  8. package/src/editor/js/Command.js +40 -0
  9. package/src/editor/js/Config.js +86 -0
  10. package/src/editor/js/Editor.js +1006 -0
  11. package/src/editor/js/EditorControls.js +489 -0
  12. package/src/editor/js/GLTFImportDialog.js +112 -0
  13. package/src/editor/js/History.js +321 -0
  14. package/src/editor/js/Loader.js +1162 -0
  15. package/src/editor/js/LoaderUtils.js +90 -0
  16. package/src/editor/js/Menubar.Add.js +559 -0
  17. package/src/editor/js/Menubar.Edit.js +155 -0
  18. package/src/editor/js/Menubar.File.js +585 -0
  19. package/src/editor/js/Menubar.Help.js +73 -0
  20. package/src/editor/js/Menubar.Render.js +858 -0
  21. package/src/editor/js/Menubar.Status.js +51 -0
  22. package/src/editor/js/Menubar.View.js +193 -0
  23. package/src/editor/js/Menubar.js +29 -0
  24. package/src/editor/js/Player.js +53 -0
  25. package/src/editor/js/Resizer.js +65 -0
  26. package/src/editor/js/Script.js +532 -0
  27. package/src/editor/js/Selector.js +129 -0
  28. package/src/editor/js/Sidebar.Geometry.BoxGeometry.js +121 -0
  29. package/src/editor/js/Sidebar.Geometry.BufferGeometry.js +124 -0
  30. package/src/editor/js/Sidebar.Geometry.CapsuleGeometry.js +109 -0
  31. package/src/editor/js/Sidebar.Geometry.CircleGeometry.js +97 -0
  32. package/src/editor/js/Sidebar.Geometry.CylinderGeometry.js +121 -0
  33. package/src/editor/js/Sidebar.Geometry.DodecahedronGeometry.js +73 -0
  34. package/src/editor/js/Sidebar.Geometry.ExtrudeGeometry.js +196 -0
  35. package/src/editor/js/Sidebar.Geometry.IcosahedronGeometry.js +73 -0
  36. package/src/editor/js/Sidebar.Geometry.LatheGeometry.js +98 -0
  37. package/src/editor/js/Sidebar.Geometry.Modifiers.js +73 -0
  38. package/src/editor/js/Sidebar.Geometry.OctahedronGeometry.js +74 -0
  39. package/src/editor/js/Sidebar.Geometry.PlaneGeometry.js +97 -0
  40. package/src/editor/js/Sidebar.Geometry.RingGeometry.js +121 -0
  41. package/src/editor/js/Sidebar.Geometry.ShapeGeometry.js +76 -0
  42. package/src/editor/js/Sidebar.Geometry.SphereGeometry.js +133 -0
  43. package/src/editor/js/Sidebar.Geometry.TetrahedronGeometry.js +74 -0
  44. package/src/editor/js/Sidebar.Geometry.TextGeometry.js +136 -0
  45. package/src/editor/js/Sidebar.Geometry.TorusGeometry.js +109 -0
  46. package/src/editor/js/Sidebar.Geometry.TorusKnotGeometry.js +121 -0
  47. package/src/editor/js/Sidebar.Geometry.TubeGeometry.js +135 -0
  48. package/src/editor/js/Sidebar.Geometry.js +471 -0
  49. package/src/editor/js/Sidebar.Material.BooleanProperty.js +60 -0
  50. package/src/editor/js/Sidebar.Material.ColorProperty.js +87 -0
  51. package/src/editor/js/Sidebar.Material.ConstantProperty.js +62 -0
  52. package/src/editor/js/Sidebar.Material.MapProperty.js +277 -0
  53. package/src/editor/js/Sidebar.Material.NumberProperty.js +60 -0
  54. package/src/editor/js/Sidebar.Material.Program.js +73 -0
  55. package/src/editor/js/Sidebar.Material.RangeValueProperty.js +63 -0
  56. package/src/editor/js/Sidebar.Material.js +751 -0
  57. package/src/editor/js/Sidebar.Object.js +892 -0
  58. package/src/editor/js/Sidebar.Project.App.js +218 -0
  59. package/src/editor/js/Sidebar.Project.Materials.js +82 -0
  60. package/src/editor/js/Sidebar.Project.Renderer.js +193 -0
  61. package/src/editor/js/Sidebar.Project.Resources.js +242 -0
  62. package/src/editor/js/Sidebar.Project.js +21 -0
  63. package/src/editor/js/Sidebar.Properties.js +73 -0
  64. package/src/editor/js/Sidebar.Scene.js +643 -0
  65. package/src/editor/js/Sidebar.Script.js +129 -0
  66. package/src/editor/js/Sidebar.Settings.History.js +146 -0
  67. package/src/editor/js/Sidebar.Settings.Shortcuts.js +198 -0
  68. package/src/editor/js/Sidebar.Settings.js +58 -0
  69. package/src/editor/js/Sidebar.js +41 -0
  70. package/src/editor/js/Storage.js +98 -0
  71. package/src/editor/js/Strings.js +2718 -0
  72. package/src/editor/js/TextureParametersDialog.js +293 -0
  73. package/src/editor/js/Toolbar.js +75 -0
  74. package/src/editor/js/Viewport.Controls.js +98 -0
  75. package/src/editor/js/Viewport.Info.js +144 -0
  76. package/src/editor/js/Viewport.Pathtracer.js +27 -0
  77. package/src/editor/js/Viewport.ViewHelper.js +41 -0
  78. package/src/editor/js/Viewport.XR.js +7 -0
  79. package/src/editor/js/Viewport.js +982 -0
  80. package/src/editor/js/commands/AddObjectCommand.js +68 -0
  81. package/src/editor/js/commands/AddScriptCommand.js +75 -0
  82. package/src/editor/js/commands/Commands.js +24 -0
  83. package/src/editor/js/commands/MoveObjectCommand.js +117 -0
  84. package/src/editor/js/commands/MultiCmdsCommand.js +85 -0
  85. package/src/editor/js/commands/RemoveObjectCommand.js +88 -0
  86. package/src/editor/js/commands/RemoveScriptCommand.js +81 -0
  87. package/src/editor/js/commands/SetColorCommand.js +73 -0
  88. package/src/editor/js/commands/SetGeometryCommand.js +86 -0
  89. package/src/editor/js/commands/SetGeometryValueCommand.js +70 -0
  90. package/src/editor/js/commands/SetMaterialColorCommand.js +87 -0
  91. package/src/editor/js/commands/SetMaterialCommand.js +80 -0
  92. package/src/editor/js/commands/SetMaterialMapCommand.js +144 -0
  93. package/src/editor/js/commands/SetMaterialRangeCommand.js +92 -0
  94. package/src/editor/js/commands/SetMaterialValueCommand.js +91 -0
  95. package/src/editor/js/commands/SetMaterialVectorCommand.js +88 -0
  96. package/src/editor/js/commands/SetPositionCommand.js +84 -0
  97. package/src/editor/js/commands/SetRotationCommand.js +84 -0
  98. package/src/editor/js/commands/SetScaleCommand.js +84 -0
  99. package/src/editor/js/commands/SetSceneCommand.js +104 -0
  100. package/src/editor/js/commands/SetScriptValueCommand.js +80 -0
  101. package/src/editor/js/commands/SetShadowValueCommand.js +73 -0
  102. package/src/editor/js/commands/SetTextureParametersCommand.js +143 -0
  103. package/src/editor/js/commands/SetUuidCommand.js +70 -0
  104. package/src/editor/js/commands/SetValueCommand.js +75 -0
  105. package/src/editor/js/libs/acorn/acorn.js +3236 -0
  106. package/src/editor/js/libs/acorn/acorn_loose.js +1299 -0
  107. package/src/editor/js/libs/acorn/walk.js +344 -0
  108. package/src/editor/js/libs/app/index.html +51 -0
  109. package/src/editor/js/libs/app.js +14 -0
  110. package/src/editor/js/libs/codemirror/addon/dialog.css +32 -0
  111. package/src/editor/js/libs/codemirror/addon/dialog.js +163 -0
  112. package/src/editor/js/libs/codemirror/addon/show-hint.css +36 -0
  113. package/src/editor/js/libs/codemirror/addon/show-hint.js +529 -0
  114. package/src/editor/js/libs/codemirror/addon/tern.css +87 -0
  115. package/src/editor/js/libs/codemirror/addon/tern.js +750 -0
  116. package/src/editor/js/libs/codemirror/codemirror.css +344 -0
  117. package/src/editor/js/libs/codemirror/codemirror.js +9849 -0
  118. package/src/editor/js/libs/codemirror/mode/glsl.js +233 -0
  119. package/src/editor/js/libs/codemirror/mode/javascript.js +959 -0
  120. package/src/editor/js/libs/codemirror/theme/monokai.css +41 -0
  121. package/src/editor/js/libs/esprima.js +6401 -0
  122. package/src/editor/js/libs/jsonlint.js +453 -0
  123. package/src/editor/js/libs/signals.min.js +14 -0
  124. package/src/editor/js/libs/tern-threejs/build-defs.js +233 -0
  125. package/src/editor/js/libs/ternjs/comment.js +87 -0
  126. package/src/editor/js/libs/ternjs/def.js +588 -0
  127. package/src/editor/js/libs/ternjs/doc_comment.js +401 -0
  128. package/src/editor/js/libs/ternjs/infer.js +1635 -0
  129. package/src/editor/js/libs/ternjs/polyfill.js +80 -0
  130. package/src/editor/js/libs/ternjs/signal.js +26 -0
  131. package/src/editor/js/libs/ternjs/tern.js +993 -0
  132. package/src/editor/js/libs/ui.js +1305 -0
  133. package/src/editor/js/libs/ui.three.js +946 -0
  134. package/src/events.js +273 -0
  135. package/src/index.js +284 -0
  136. package/src/presets/machines.js +239 -0
@@ -0,0 +1,946 @@
1
+ import * as THREE from 'three';
2
+
3
+ import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
4
+
5
+ import { UISpan, UIDiv, UIRow, UIButton, UICheckbox, UIText, UINumber } from './ui.js';
6
+ import { MoveObjectCommand } from '../commands/MoveObjectCommand.js';
7
+
8
+ const cache = new Map();
9
+
10
+ class UITexture extends UISpan {
11
+
12
+ constructor( editor ) {
13
+
14
+ super();
15
+
16
+ const scope = this;
17
+
18
+ const form = document.createElement( 'form' );
19
+
20
+ const input = document.createElement( 'input' );
21
+ input.type = 'file';
22
+ input.addEventListener( 'change', function ( event ) {
23
+
24
+ loadFile( event.target.files[ 0 ] );
25
+
26
+ } );
27
+ form.appendChild( input );
28
+
29
+ const canvas = document.createElement( 'canvas' );
30
+ canvas.width = 32;
31
+ canvas.height = 16;
32
+ canvas.style.cursor = 'pointer';
33
+ canvas.style.marginRight = '5px';
34
+ canvas.style.border = '1px solid #888';
35
+ canvas.addEventListener( 'click', function () {
36
+
37
+ input.click();
38
+
39
+ } );
40
+ canvas.addEventListener( 'drop', function ( event ) {
41
+
42
+ event.preventDefault();
43
+ event.stopPropagation();
44
+ loadFile( event.dataTransfer.files[ 0 ] );
45
+
46
+ } );
47
+ this.dom.appendChild( canvas );
48
+
49
+ async function loadFile( file ) {
50
+
51
+ const extension = file.name.split( '.' ).pop().toLowerCase();
52
+ const reader = new FileReader();
53
+
54
+ const hash = `${file.lastModified}_${file.size}_${file.name}`;
55
+
56
+ function deliver( texture ) {
57
+
58
+ if ( ! cache.has( hash ) ) cache.set( hash, texture );
59
+
60
+ const cached = cache.get( hash );
61
+ const clone = cached.clone();
62
+ clone.sourceFile = cached.sourceFile;
63
+
64
+ scope.setValue( clone );
65
+
66
+ if ( scope.onChangeCallback ) scope.onChangeCallback( clone );
67
+
68
+ }
69
+
70
+ if ( cache.has( hash ) ) {
71
+
72
+ deliver( cache.get( hash ) );
73
+
74
+ } else if ( extension === 'hdr' || extension === 'pic' ) {
75
+
76
+ reader.addEventListener( 'load', async function ( event ) {
77
+
78
+ // assuming RGBE/Radiance HDR image format
79
+
80
+ const { HDRLoader } = await import( 'three/addons/loaders/HDRLoader.js' );
81
+
82
+ const loader = new HDRLoader();
83
+ loader.load( event.target.result, function ( hdrTexture ) {
84
+
85
+ hdrTexture.sourceFile = file.name;
86
+
87
+ deliver( hdrTexture );
88
+
89
+ } );
90
+
91
+ } );
92
+
93
+ reader.readAsDataURL( file );
94
+
95
+ } else if ( extension === 'tga' ) {
96
+
97
+ reader.addEventListener( 'load', async function ( event ) {
98
+
99
+ const { TGALoader } = await import( 'three/addons/loaders/TGALoader.js' );
100
+
101
+ const loader = new TGALoader();
102
+ loader.load( event.target.result, function ( texture ) {
103
+
104
+ texture.colorSpace = THREE.SRGBColorSpace;
105
+ texture.sourceFile = file.name;
106
+
107
+ deliver( texture );
108
+
109
+ } );
110
+
111
+ }, false );
112
+
113
+ reader.readAsDataURL( file );
114
+
115
+ } else if ( extension === 'ktx2' ) {
116
+
117
+ reader.addEventListener( 'load', async function ( event ) {
118
+
119
+ const { KTX2Loader } = await import( 'three/addons/loaders/KTX2Loader.js' );
120
+
121
+ const arrayBuffer = event.target.result;
122
+ const blobURL = URL.createObjectURL( new Blob( [ arrayBuffer ] ) );
123
+ const ktx2Loader = new KTX2Loader();
124
+ ktx2Loader.setTranscoderPath( '../../examples/jsm/libs/basis/' );
125
+ editor.signals.rendererDetectKTX2Support.dispatch( ktx2Loader );
126
+
127
+ ktx2Loader.load( blobURL, function ( texture ) {
128
+
129
+ texture.colorSpace = THREE.SRGBColorSpace;
130
+ texture.sourceFile = file.name;
131
+ texture.needsUpdate = true;
132
+
133
+ deliver( texture );
134
+ ktx2Loader.dispose();
135
+
136
+ } );
137
+
138
+ } );
139
+
140
+ reader.readAsArrayBuffer( file );
141
+
142
+ } else if ( extension === 'exr' ) {
143
+
144
+ reader.addEventListener( 'load', async function ( event ) {
145
+
146
+ const { EXRLoader } = await import( 'three/addons/loaders/EXRLoader.js' );
147
+
148
+ const arrayBuffer = event.target.result;
149
+ const blobURL = URL.createObjectURL( new Blob( [ arrayBuffer ] ) );
150
+ const exrLoader = new EXRLoader();
151
+
152
+ exrLoader.load( blobURL, function ( texture ) {
153
+
154
+ texture.sourceFile = file.name;
155
+ texture.needsUpdate = true;
156
+
157
+ deliver( texture );
158
+
159
+ } );
160
+
161
+ } );
162
+
163
+ reader.readAsArrayBuffer( file );
164
+
165
+ } else if ( file.type.match( 'image.*' ) ) {
166
+
167
+ reader.addEventListener( 'load', function ( event ) {
168
+
169
+ const image = document.createElement( 'img' );
170
+ image.addEventListener( 'load', function () {
171
+
172
+ const texture = new THREE.Texture( this );
173
+ texture.sourceFile = file.name;
174
+ texture.needsUpdate = true;
175
+
176
+ deliver( texture );
177
+
178
+ }, false );
179
+
180
+ image.src = event.target.result;
181
+
182
+ }, false );
183
+
184
+ reader.readAsDataURL( file );
185
+
186
+ }
187
+
188
+ form.reset();
189
+
190
+ }
191
+
192
+ this.texture = null;
193
+ this.onChangeCallback = null;
194
+
195
+ }
196
+
197
+ getValue() {
198
+
199
+ return this.texture;
200
+
201
+ }
202
+
203
+ setValue( texture ) {
204
+
205
+ const canvas = this.dom.children[ 0 ];
206
+ const context = canvas.getContext( '2d' );
207
+
208
+ // Seems like context can be null if the canvas is not visible
209
+ if ( context ) {
210
+
211
+ // Always clear the context before set new texture, because new texture may has transparency
212
+ context.clearRect( 0, 0, canvas.width, canvas.height );
213
+
214
+ }
215
+
216
+ if ( texture !== null ) {
217
+
218
+ const image = texture.image;
219
+
220
+ if ( image !== undefined && image !== null && image.width > 0 ) {
221
+
222
+ canvas.title = texture.sourceFile;
223
+ const scale = canvas.width / image.width;
224
+
225
+ if ( texture.isDataTexture || texture.isCompressedTexture ) {
226
+
227
+ const canvas2 = renderToCanvas( texture );
228
+ context.drawImage( canvas2, 0, 0, image.width * scale, image.height * scale );
229
+
230
+ } else {
231
+
232
+ context.drawImage( image, 0, 0, image.width * scale, image.height * scale );
233
+
234
+ }
235
+
236
+ } else {
237
+
238
+ canvas.title = texture.sourceFile + ' (error)';
239
+
240
+ }
241
+
242
+ } else {
243
+
244
+ canvas.title = 'empty';
245
+
246
+ }
247
+
248
+ this.texture = texture;
249
+
250
+ }
251
+
252
+ setColorSpace( colorSpace ) {
253
+
254
+ const texture = this.getValue();
255
+
256
+ if ( texture !== null ) {
257
+
258
+ texture.colorSpace = colorSpace;
259
+
260
+ }
261
+
262
+ return this;
263
+
264
+ }
265
+
266
+ onChange( callback ) {
267
+
268
+ this.onChangeCallback = callback;
269
+
270
+ return this;
271
+
272
+ }
273
+
274
+ }
275
+
276
+ class UIOutliner extends UIDiv {
277
+
278
+ constructor( editor ) {
279
+
280
+ super();
281
+
282
+ this.dom.className = 'Outliner';
283
+ this.dom.tabIndex = 0; // keyup event is ignored without setting tabIndex
284
+
285
+ const scope = this;
286
+
287
+ // hack
288
+ this.scene = editor.scene;
289
+
290
+ // Prevent native scroll behavior
291
+ this.dom.addEventListener( 'keydown', function ( event ) {
292
+
293
+ switch ( event.code ) {
294
+
295
+ case 'ArrowUp':
296
+ case 'ArrowDown':
297
+ event.preventDefault();
298
+ event.stopPropagation();
299
+ break;
300
+
301
+ }
302
+
303
+ } );
304
+
305
+ // Keybindings to support arrow navigation
306
+ this.dom.addEventListener( 'keyup', function ( event ) {
307
+
308
+ switch ( event.code ) {
309
+
310
+ case 'ArrowUp':
311
+ scope.selectIndex( scope.selectedIndex - 1 );
312
+ break;
313
+ case 'ArrowDown':
314
+ scope.selectIndex( scope.selectedIndex + 1 );
315
+ break;
316
+
317
+ }
318
+
319
+ } );
320
+
321
+ this.editor = editor;
322
+
323
+ this.options = [];
324
+ this.selectedIndex = - 1;
325
+ this.selectedValue = null;
326
+ this.selectedValues = [];
327
+
328
+ }
329
+
330
+ selectIndex( index ) {
331
+
332
+ if ( index >= 0 && index < this.options.length ) {
333
+
334
+ this.setValue( this.options[ index ].value );
335
+
336
+ const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );
337
+ this.dom.dispatchEvent( changeEvent );
338
+
339
+ }
340
+
341
+ }
342
+
343
+ setMultiValue( values ) {
344
+
345
+ this.selectedValues = values.slice();
346
+ this.selectedValue = values.length ? values[ values.length - 1 ] : null;
347
+ for ( let i = 0; i < this.options.length; i ++ ) {
348
+
349
+ const el = this.options[ i ];
350
+ if ( values.indexOf( el.value ) !== - 1 ) {
351
+
352
+ el.classList.add( 'active' );
353
+ this.selectedIndex = i;
354
+
355
+ } else {
356
+
357
+ el.classList.remove( 'active' );
358
+
359
+ }
360
+
361
+ }
362
+
363
+ }
364
+
365
+ toggleMultiValue( value ) {
366
+
367
+ if ( ! this.selectedValues || this.selectedValues.length === 0 ) {
368
+
369
+ // start a fresh multi-select from current single selection
370
+ this.selectedValues = ( this.selectedValue !== null ) ? [ this.selectedValue ] : [];
371
+
372
+ }
373
+ const idx = this.selectedValues.indexOf( value );
374
+ if ( idx === - 1 ) this.selectedValues.push( value );
375
+ else this.selectedValues.splice( idx, 1 );
376
+ this.selectedValue = this.selectedValues.length ? this.selectedValues[ this.selectedValues.length - 1 ] : null;
377
+ for ( let i = 0; i < this.options.length; i ++ ) {
378
+
379
+ const el = this.options[ i ];
380
+ if ( this.selectedValues.indexOf( el.value ) !== - 1 ) {
381
+
382
+ el.classList.add( 'active' );
383
+ if ( el.value === this.selectedValue ) this.selectedIndex = i;
384
+
385
+ } else el.classList.remove( 'active' );
386
+
387
+ }
388
+
389
+ }
390
+
391
+ setOptions( options ) {
392
+
393
+ const scope = this;
394
+
395
+ while ( scope.dom.children.length > 0 ) {
396
+
397
+ scope.dom.removeChild( scope.dom.firstChild );
398
+
399
+ }
400
+
401
+ function onClick( event ) {
402
+
403
+ const isMac = navigator.platform.toUpperCase().indexOf( 'MAC' ) >= 0;
404
+ const multi = isMac ? event.metaKey : event.ctrlKey;
405
+ const range = event.shiftKey;
406
+
407
+ if ( multi ) {
408
+
409
+ scope.toggleMultiValue( this.value );
410
+
411
+ } else if ( range && scope.options.length > 0 && scope.selectedIndex >= 0 ) {
412
+
413
+ const start = scope.selectedIndex;
414
+ const end = scope.options.indexOf( this );
415
+ const from = Math.min( start, end );
416
+ const to = Math.max( start, end );
417
+ const ids = [];
418
+ for ( let k = from; k <= to; k ++ ) ids.push( scope.options[ k ].value );
419
+ scope.setMultiValue( ids );
420
+
421
+ } else {
422
+
423
+ scope.setValue( this.value );
424
+
425
+ }
426
+
427
+ const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );
428
+ scope.dom.dispatchEvent( changeEvent );
429
+
430
+ }
431
+
432
+ // Drag
433
+
434
+ let currentDrag;
435
+
436
+ function onDrag() {
437
+
438
+ currentDrag = this;
439
+
440
+ }
441
+
442
+ function onDragStart( event ) {
443
+
444
+ event.dataTransfer.setData( 'text', 'foo' );
445
+
446
+ }
447
+
448
+ function onDragOver( event ) {
449
+
450
+ if ( this === currentDrag ) return;
451
+
452
+ const area = event.offsetY / this.clientHeight;
453
+
454
+ if ( area < 0.25 ) {
455
+
456
+ this.className = 'option dragTop';
457
+
458
+ } else if ( area > 0.75 ) {
459
+
460
+ this.className = 'option dragBottom';
461
+
462
+ } else {
463
+
464
+ this.className = 'option drag';
465
+
466
+ }
467
+
468
+ }
469
+
470
+ function onDragLeave() {
471
+
472
+ if ( this === currentDrag ) return;
473
+
474
+ this.className = 'option';
475
+
476
+ }
477
+
478
+ function onDrop( event ) {
479
+
480
+ if ( this === currentDrag || currentDrag === undefined ) return;
481
+
482
+ this.className = 'option';
483
+
484
+ const scene = scope.scene;
485
+ const object = scene.getObjectById( currentDrag.value );
486
+
487
+ const area = event.offsetY / this.clientHeight;
488
+
489
+ if ( area < 0.25 ) {
490
+
491
+ const nextObject = scene.getObjectById( this.value );
492
+ moveObject( object, nextObject.parent, nextObject );
493
+
494
+ } else if ( area > 0.75 ) {
495
+
496
+ let nextObject, parent;
497
+
498
+ if ( this.nextSibling !== null ) {
499
+
500
+ nextObject = scene.getObjectById( this.nextSibling.value );
501
+ parent = nextObject.parent;
502
+
503
+ } else {
504
+
505
+ // end of list (no next object)
506
+
507
+ nextObject = null;
508
+ parent = scene.getObjectById( this.value ).parent;
509
+
510
+ }
511
+
512
+ moveObject( object, parent, nextObject );
513
+
514
+ } else {
515
+
516
+ const parentObject = scene.getObjectById( this.value );
517
+ moveObject( object, parentObject );
518
+
519
+ }
520
+
521
+ }
522
+
523
+ function moveObject( object, newParent, nextObject ) {
524
+
525
+ if ( nextObject === null ) nextObject = undefined;
526
+
527
+ let newParentIsChild = false;
528
+
529
+ object.traverse( function ( child ) {
530
+
531
+ if ( child === newParent ) newParentIsChild = true;
532
+
533
+ } );
534
+
535
+ if ( newParentIsChild ) return;
536
+
537
+ const editor = scope.editor;
538
+ editor.execute( new MoveObjectCommand( editor, object, newParent, nextObject ) );
539
+
540
+ const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );
541
+ scope.dom.dispatchEvent( changeEvent );
542
+
543
+ }
544
+
545
+ //
546
+
547
+ scope.options = [];
548
+
549
+ for ( let i = 0; i < options.length; i ++ ) {
550
+
551
+ const div = options[ i ];
552
+ div.className = 'option';
553
+ scope.dom.appendChild( div );
554
+
555
+ scope.options.push( div );
556
+
557
+ div.addEventListener( 'click', onClick );
558
+
559
+ if ( div.draggable === true ) {
560
+
561
+ div.addEventListener( 'drag', onDrag );
562
+ div.addEventListener( 'dragstart', onDragStart ); // Firefox needs this
563
+
564
+ div.addEventListener( 'dragover', onDragOver );
565
+ div.addEventListener( 'dragleave', onDragLeave );
566
+ div.addEventListener( 'drop', onDrop );
567
+
568
+ }
569
+
570
+
571
+ }
572
+
573
+ return scope;
574
+
575
+ }
576
+
577
+ getValue() {
578
+
579
+ return this.selectedValue;
580
+
581
+ }
582
+
583
+ setValue( value ) {
584
+
585
+ this.selectedValues = ( value !== null ) ? [ value ] : [];
586
+ for ( let i = 0; i < this.options.length; i ++ ) {
587
+
588
+ const element = this.options[ i ];
589
+
590
+ if ( element.value === value ) {
591
+
592
+ element.classList.add( 'active' );
593
+
594
+ // scroll into view
595
+
596
+ const y = element.offsetTop - this.dom.offsetTop;
597
+ const bottomY = y + element.offsetHeight;
598
+ const minScroll = bottomY - this.dom.offsetHeight;
599
+
600
+ if ( this.dom.scrollTop > y ) {
601
+
602
+ this.dom.scrollTop = y;
603
+
604
+ } else if ( this.dom.scrollTop < minScroll ) {
605
+
606
+ this.dom.scrollTop = minScroll;
607
+
608
+ }
609
+
610
+ this.selectedIndex = i;
611
+
612
+ } else {
613
+
614
+ element.classList.remove( 'active' );
615
+
616
+ }
617
+
618
+ }
619
+
620
+ this.selectedValue = value;
621
+
622
+ return this;
623
+
624
+ }
625
+
626
+ }
627
+
628
+ class UIPoints extends UISpan {
629
+
630
+ constructor() {
631
+
632
+ super();
633
+
634
+ this.dom.style.display = 'inline-block';
635
+
636
+ this.pointsList = new UIDiv();
637
+ this.add( this.pointsList );
638
+
639
+ this.pointsUI = [];
640
+ this.lastPointIdx = 0;
641
+ this.onChangeCallback = null;
642
+
643
+ this.update = () => { // bind lexical this
644
+
645
+ if ( this.onChangeCallback !== null ) {
646
+
647
+ this.onChangeCallback();
648
+
649
+ }
650
+
651
+ };
652
+
653
+ }
654
+
655
+ onChange( callback ) {
656
+
657
+ this.onChangeCallback = callback;
658
+
659
+ return this;
660
+
661
+ }
662
+
663
+ clear() {
664
+
665
+ for ( let i = this.pointsUI.length - 1; i >= 0; -- i ) {
666
+
667
+ this.deletePointRow( i, false );
668
+
669
+ }
670
+
671
+ this.lastPointIdx = 0;
672
+
673
+ }
674
+
675
+ deletePointRow( idx, needsUpdate = true ) {
676
+
677
+ if ( ! this.pointsUI[ idx ] ) return;
678
+
679
+ this.pointsList.remove( this.pointsUI[ idx ].row );
680
+
681
+ this.pointsUI.splice( idx, 1 );
682
+
683
+ if ( needsUpdate === true ) {
684
+
685
+ this.update();
686
+
687
+ }
688
+
689
+ this.lastPointIdx --;
690
+
691
+ }
692
+
693
+ }
694
+
695
+ class UIPoints2 extends UIPoints {
696
+
697
+ constructor() {
698
+
699
+ super();
700
+
701
+ const row = new UIRow();
702
+ this.add( row );
703
+
704
+ const addPointButton = new UIButton( '+' );
705
+ addPointButton.onClick( () => {
706
+
707
+ if ( this.pointsUI.length === 0 ) {
708
+
709
+ this.pointsList.add( this.createPointRow( 0, 0 ) );
710
+
711
+ } else {
712
+
713
+ const point = this.pointsUI[ this.pointsUI.length - 1 ];
714
+
715
+ this.pointsList.add( this.createPointRow( point.x.getValue(), point.y.getValue() ) );
716
+
717
+ }
718
+
719
+ this.update();
720
+
721
+ } );
722
+ row.add( addPointButton );
723
+
724
+ }
725
+
726
+ getValue() {
727
+
728
+ const points = [];
729
+
730
+ let count = 0;
731
+
732
+ for ( let i = 0; i < this.pointsUI.length; i ++ ) {
733
+
734
+ const pointUI = this.pointsUI[ i ];
735
+
736
+ if ( ! pointUI ) continue;
737
+
738
+ points.push( new THREE.Vector2( pointUI.x.getValue(), pointUI.y.getValue() ) );
739
+ ++ count;
740
+ pointUI.lbl.setValue( count );
741
+
742
+ }
743
+
744
+ return points;
745
+
746
+ }
747
+
748
+ setValue( points, needsUpdate = true ) {
749
+
750
+ this.clear();
751
+
752
+ for ( let i = 0; i < points.length; i ++ ) {
753
+
754
+ const point = points[ i ];
755
+ this.pointsList.add( this.createPointRow( point.x, point.y ) );
756
+
757
+ }
758
+
759
+ if ( needsUpdate === true ) this.update();
760
+
761
+ return this;
762
+
763
+ }
764
+
765
+ createPointRow( x, y ) {
766
+
767
+ const pointRow = new UIDiv();
768
+ const lbl = new UIText( this.lastPointIdx + 1 ).setWidth( '20px' );
769
+ const txtX = new UINumber( x ).setWidth( '30px' ).onChange( this.update );
770
+ const txtY = new UINumber( y ).setWidth( '30px' ).onChange( this.update );
771
+
772
+ const scope = this;
773
+ const btn = new UIButton( '-' ).onClick( function () {
774
+
775
+ if ( scope.isEditing ) return;
776
+
777
+ const idx = scope.pointsList.getIndexOfChild( pointRow );
778
+ scope.deletePointRow( idx );
779
+
780
+ } );
781
+
782
+ this.pointsUI.push( { row: pointRow, lbl: lbl, x: txtX, y: txtY } );
783
+ ++ this.lastPointIdx;
784
+ pointRow.add( lbl, txtX, txtY, btn );
785
+
786
+ return pointRow;
787
+
788
+ }
789
+
790
+ }
791
+
792
+ class UIPoints3 extends UIPoints {
793
+
794
+ constructor() {
795
+
796
+ super();
797
+
798
+ const row = new UIRow();
799
+ this.add( row );
800
+
801
+ const addPointButton = new UIButton( '+' );
802
+ addPointButton.onClick( () => {
803
+
804
+ if ( this.pointsUI.length === 0 ) {
805
+
806
+ this.pointsList.add( this.createPointRow( 0, 0, 0 ) );
807
+
808
+ } else {
809
+
810
+ const point = this.pointsUI[ this.pointsUI.length - 1 ];
811
+
812
+ this.pointsList.add( this.createPointRow( point.x.getValue(), point.y.getValue(), point.z.getValue() ) );
813
+
814
+ }
815
+
816
+ this.update();
817
+
818
+ } );
819
+ row.add( addPointButton );
820
+
821
+ }
822
+
823
+ getValue() {
824
+
825
+ const points = [];
826
+ let count = 0;
827
+
828
+ for ( let i = 0; i < this.pointsUI.length; i ++ ) {
829
+
830
+ const pointUI = this.pointsUI[ i ];
831
+
832
+ if ( ! pointUI ) continue;
833
+
834
+ points.push( new THREE.Vector3( pointUI.x.getValue(), pointUI.y.getValue(), pointUI.z.getValue() ) );
835
+ ++ count;
836
+ pointUI.lbl.setValue( count );
837
+
838
+ }
839
+
840
+ return points;
841
+
842
+ }
843
+
844
+ setValue( points, needsUpdate = true ) {
845
+
846
+ this.clear();
847
+
848
+ for ( let i = 0; i < points.length; i ++ ) {
849
+
850
+ const point = points[ i ];
851
+ this.pointsList.add( this.createPointRow( point.x, point.y, point.z ) );
852
+
853
+ }
854
+
855
+ if ( needsUpdate === true ) this.update();
856
+
857
+ return this;
858
+
859
+ }
860
+
861
+ createPointRow( x, y, z ) {
862
+
863
+ const pointRow = new UIDiv();
864
+ const lbl = new UIText( this.lastPointIdx + 1 ).setWidth( '20px' );
865
+ const txtX = new UINumber( x ).setWidth( '30px' ).onChange( this.update );
866
+ const txtY = new UINumber( y ).setWidth( '30px' ).onChange( this.update );
867
+ const txtZ = new UINumber( z ).setWidth( '30px' ).onChange( this.update );
868
+
869
+ const scope = this;
870
+ const btn = new UIButton( '-' ).onClick( function () {
871
+
872
+ if ( scope.isEditing ) return;
873
+
874
+ const idx = scope.pointsList.getIndexOfChild( pointRow );
875
+ scope.deletePointRow( idx );
876
+
877
+ } );
878
+
879
+ this.pointsUI.push( { row: pointRow, lbl: lbl, x: txtX, y: txtY, z: txtZ } );
880
+ ++ this.lastPointIdx;
881
+ pointRow.add( lbl, txtX, txtY, txtZ, btn );
882
+
883
+ return pointRow;
884
+
885
+ }
886
+
887
+ }
888
+
889
+ class UIBoolean extends UISpan {
890
+
891
+ constructor( boolean, text ) {
892
+
893
+ super();
894
+
895
+ this.setMarginRight( '4px' );
896
+
897
+ this.checkbox = new UICheckbox( boolean );
898
+ this.text = new UIText( text ).setMarginLeft( '3px' );
899
+
900
+ this.add( this.checkbox );
901
+ this.add( this.text );
902
+
903
+ }
904
+
905
+ getValue() {
906
+
907
+ return this.checkbox.getValue();
908
+
909
+ }
910
+
911
+ setValue( value ) {
912
+
913
+ return this.checkbox.setValue( value );
914
+
915
+ }
916
+
917
+ }
918
+
919
+ let renderer, fsQuad;
920
+
921
+ function renderToCanvas( texture ) {
922
+
923
+ if ( renderer === undefined ) {
924
+
925
+ renderer = new THREE.WebGLRenderer();
926
+
927
+ }
928
+
929
+ if ( fsQuad === undefined ) {
930
+
931
+ fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial() );
932
+
933
+ }
934
+
935
+ const image = texture.image;
936
+
937
+ renderer.setSize( image.width, image.height, false );
938
+
939
+ fsQuad.material.map = texture;
940
+ fsQuad.render( renderer );
941
+
942
+ return renderer.domElement;
943
+
944
+ }
945
+
946
+ export { UITexture, UIOutliner, UIPoints, UIPoints2, UIPoints3, UIBoolean, renderToCanvas };