ui.three.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /**
  2. * @author mrdoob / http://mrdoob.com/
  3. */
  4. UI.Texture = function ( mapping ) {
  5. UI.Element.call( this );
  6. var scope = this;
  7. var dom = document.createElement( 'span' );
  8. var input = document.createElement( 'input' );
  9. input.type = 'file';
  10. input.addEventListener( 'change', function ( event ) {
  11. loadFile( event.target.files[ 0 ] );
  12. } );
  13. var canvas = document.createElement( 'canvas' );
  14. canvas.width = 32;
  15. canvas.height = 16;
  16. canvas.style.cursor = 'pointer';
  17. canvas.style.marginRight = '5px';
  18. canvas.style.border = '1px solid #888';
  19. canvas.addEventListener( 'click', function ( event ) {
  20. input.click();
  21. }, false );
  22. canvas.addEventListener( 'drop', function ( event ) {
  23. event.preventDefault();
  24. event.stopPropagation();
  25. loadFile( event.dataTransfer.files[ 0 ] );
  26. }, false );
  27. dom.appendChild( canvas );
  28. var name = document.createElement( 'input' );
  29. name.disabled = true;
  30. name.style.width = '64px';
  31. name.style.border = '1px solid #ccc';
  32. dom.appendChild( name );
  33. var form = document.createElement( 'form' );
  34. form.appendChild( input );
  35. form.style.display = 'none';
  36. dom.appendChild( form );
  37. var loadFile = function ( file ) {
  38. if ( file.type.match( 'image.*' ) ) {
  39. var reader = new FileReader();
  40. if ( file.type === 'image/targa' ) {
  41. reader.addEventListener( 'load', function ( event ) {
  42. var canvas = new THREE.TGALoader().parse( event.target.result );
  43. var texture = new THREE.CanvasTexture( canvas, mapping );
  44. texture.sourceFile = file.name;
  45. form.reset();
  46. scope.setValue( texture );
  47. if ( scope.onChangeCallback ) scope.onChangeCallback();
  48. }, false );
  49. reader.readAsArrayBuffer( file );
  50. } else {
  51. reader.addEventListener( 'load', function ( event ) {
  52. var image = document.createElement( 'img' );
  53. image.addEventListener( 'load', function( event ) {
  54. var texture = new THREE.Texture( this, mapping );
  55. texture.sourceFile = file.name;
  56. texture.needsUpdate = true;
  57. scope.setValue( texture );
  58. form.reset();
  59. if ( scope.onChangeCallback ) scope.onChangeCallback();
  60. }, false );
  61. image.src = event.target.result;
  62. }, false );
  63. reader.readAsDataURL( file );
  64. }
  65. }
  66. };
  67. this.dom = dom;
  68. this.texture = null;
  69. this.onChangeCallback = null;
  70. return this;
  71. };
  72. UI.Texture.prototype = Object.create( UI.Element.prototype );
  73. UI.Texture.prototype.constructor = UI.Texture;
  74. UI.Texture.prototype.getValue = function () {
  75. return this.texture;
  76. };
  77. UI.Texture.prototype.setValue = function ( texture ) {
  78. var canvas = this.dom.children[ 0 ];
  79. var name = this.dom.children[ 1 ];
  80. var context = canvas.getContext( '2d' );
  81. if ( texture !== null ) {
  82. var image = texture.image;
  83. if ( image !== undefined && image.width > 0 ) {
  84. name.value = texture.sourceFile;
  85. var scale = canvas.width / image.width;
  86. context.drawImage( image, 0, 0, image.width * scale, image.height * scale );
  87. } else {
  88. name.value = texture.sourceFile + ' (error)';
  89. context.clearRect( 0, 0, canvas.width, canvas.height );
  90. }
  91. } else {
  92. name.value = '';
  93. if ( context !== null ) {
  94. // Seems like context can be null if the canvas is not visible
  95. context.clearRect( 0, 0, canvas.width, canvas.height );
  96. }
  97. }
  98. this.texture = texture;
  99. };
  100. UI.Texture.prototype.onChange = function ( callback ) {
  101. this.onChangeCallback = callback;
  102. return this;
  103. };
  104. // Outliner
  105. UI.Outliner = function ( editor ) {
  106. UI.Element.call( this );
  107. var scope = this;
  108. var dom = document.createElement( 'div' );
  109. dom.className = 'Outliner';
  110. dom.tabIndex = 0; // keyup event is ignored without setting tabIndex
  111. // hack
  112. this.scene = editor.scene;
  113. // Prevent native scroll behavior
  114. dom.addEventListener( 'keydown', function ( event ) {
  115. switch ( event.keyCode ) {
  116. case 38: // up
  117. case 40: // down
  118. event.preventDefault();
  119. event.stopPropagation();
  120. break;
  121. }
  122. }, false );
  123. // Keybindings to support arrow navigation
  124. dom.addEventListener( 'keyup', function ( event ) {
  125. switch ( event.keyCode ) {
  126. case 38: // up
  127. scope.selectIndex( scope.selectedIndex - 1 );
  128. break;
  129. case 40: // down
  130. scope.selectIndex( scope.selectedIndex + 1 );
  131. break;
  132. }
  133. }, false );
  134. this.dom = dom;
  135. this.options = [];
  136. this.selectedIndex = - 1;
  137. this.selectedValue = null;
  138. return this;
  139. };
  140. UI.Outliner.prototype = Object.create( UI.Element.prototype );
  141. UI.Outliner.prototype.constructor = UI.Outliner;
  142. UI.Outliner.prototype.selectIndex = function ( index ) {
  143. if ( index >= 0 && index < this.options.length ) {
  144. this.setValue( this.options[ index ].value );
  145. var changeEvent = document.createEvent( 'HTMLEvents' );
  146. changeEvent.initEvent( 'change', true, true );
  147. this.dom.dispatchEvent( changeEvent );
  148. }
  149. };
  150. UI.Outliner.prototype.setOptions = function ( options ) {
  151. var scope = this;
  152. while ( scope.dom.children.length > 0 ) {
  153. scope.dom.removeChild( scope.dom.firstChild );
  154. }
  155. function onClick() {
  156. scope.setValue( this.value );
  157. var changeEvent = document.createEvent( 'HTMLEvents' );
  158. changeEvent.initEvent( 'change', true, true );
  159. scope.dom.dispatchEvent( changeEvent );
  160. }
  161. // Drag
  162. var currentDrag;
  163. function onDrag( event ) {
  164. currentDrag = this;
  165. }
  166. function onDragStart( event ) {
  167. event.dataTransfer.setData( 'text', 'foo' );
  168. }
  169. function onDragOver( event ) {
  170. if ( this === currentDrag ) return;
  171. var area = event.offsetY / this.clientHeight;
  172. if ( area < 0.25 ) {
  173. this.className = 'option dragTop';
  174. } else if ( area > 0.75 ) {
  175. this.className = 'option dragBottom';
  176. } else {
  177. this.className = 'option drag';
  178. }
  179. }
  180. function onDragLeave() {
  181. if ( this === currentDrag ) return;
  182. this.className = 'option';
  183. }
  184. function onDrop( event ) {
  185. if ( this === currentDrag ) return;
  186. this.className = 'option';
  187. var scene = scope.scene;
  188. var object = scene.getObjectById( currentDrag.value );
  189. var area = event.offsetY / this.clientHeight;
  190. if ( area < 0.25 ) {
  191. var nextObject = scene.getObjectById( this.value );
  192. moveObject( object, nextObject.parent, nextObject );
  193. } else if ( area > 0.75 ) {
  194. var nextObject = scene.getObjectById( this.nextSibling.value );
  195. moveObject( object, nextObject.parent, nextObject );
  196. } else {
  197. var parentObject = scene.getObjectById( this.value );
  198. moveObject( object, parentObject );
  199. }
  200. }
  201. function moveObject( object, newParent, nextObject ) {
  202. if ( nextObject === null ) nextObject = undefined;
  203. var newParentIsChild = false;
  204. object.traverse( function ( child ) {
  205. if ( child === newParent ) newParentIsChild = true;
  206. } );
  207. if ( newParentIsChild ) return;
  208. editor.execute( new MoveObjectCommand( object, newParent, nextObject ) );
  209. var changeEvent = document.createEvent( 'HTMLEvents' );
  210. changeEvent.initEvent( 'change', true, true );
  211. scope.dom.dispatchEvent( changeEvent );
  212. }
  213. //
  214. scope.options = [];
  215. for ( var i = 0; i < options.length; i ++ ) {
  216. var div = options[ i ];
  217. div.className = 'option';
  218. scope.dom.appendChild( div );
  219. scope.options.push( div );
  220. div.addEventListener( 'click', onClick, false );
  221. if ( div.draggable === true ) {
  222. div.addEventListener( 'drag', onDrag, false );
  223. div.addEventListener( 'dragstart', onDragStart, false ); // Firefox needs this
  224. div.addEventListener( 'dragover', onDragOver, false );
  225. div.addEventListener( 'dragleave', onDragLeave, false );
  226. div.addEventListener( 'drop', onDrop, false );
  227. }
  228. }
  229. return scope;
  230. };
  231. UI.Outliner.prototype.getValue = function () {
  232. return this.selectedValue;
  233. };
  234. UI.Outliner.prototype.setValue = function ( value ) {
  235. for ( var i = 0; i < this.options.length; i ++ ) {
  236. var element = this.options[ i ];
  237. if ( element.value === value ) {
  238. element.classList.add( 'active' );
  239. // scroll into view
  240. var y = element.offsetTop - this.dom.offsetTop;
  241. var bottomY = y + element.offsetHeight;
  242. var minScroll = bottomY - this.dom.offsetHeight;
  243. if ( this.dom.scrollTop > y ) {
  244. this.dom.scrollTop = y;
  245. } else if ( this.dom.scrollTop < minScroll ) {
  246. this.dom.scrollTop = minScroll;
  247. }
  248. this.selectedIndex = i;
  249. } else {
  250. element.classList.remove( 'active' );
  251. }
  252. }
  253. this.selectedValue = value;
  254. return this;
  255. };
  256. UI.THREE = {};
  257. UI.THREE.Boolean = function ( boolean, text ) {
  258. UI.Span.call( this );
  259. this.setMarginRight( '10px' );
  260. this.checkbox = new UI.Checkbox( boolean );
  261. this.text = new UI.Text( text ).setMarginLeft( '3px' );
  262. this.add( this.checkbox );
  263. this.add( this.text );
  264. };
  265. UI.THREE.Boolean.prototype = Object.create( UI.Span.prototype );
  266. UI.THREE.Boolean.prototype.constructor = UI.THREE.Boolean;
  267. UI.THREE.Boolean.prototype.getValue = function () {
  268. return this.checkbox.getValue();
  269. };
  270. UI.THREE.Boolean.prototype.setValue = function ( value ) {
  271. return this.checkbox.setValue( value );
  272. };