/home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/controls/BIMClipControl2.js
API Docs for:

File: /home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/controls/BIMClipControl2.js

  1. /**
  2.  
  3. Helper that visualizes the position and direction of a plane.
  4.  
  5. @class ClipControl
  6. @constructor
  7. @param cfg {*} Configuration
  8. @param [cfg.pos=[0,0,0]] {Float32Array} World-space position.
  9. @param [cfg.dir=[0,0,1]] {Float32Array} World-space direction vector.
  10. @param [cfg.color=[0.4,0.4,0.4]] {Float32Array} Emmissive color
  11. @param [cfg.solid=true] {Boolean} Indicates whether or not this helper is filled with color or just wireframe.
  12. @param [cfg.visible=true] {Boolean} Indicates whether or not this helper is visible.
  13. @param [cfg.planeSize] {Float32Array} The width and height of the ClipControl plane indicator.
  14. @param [cfg.autoPlaneSize=false] {Boolean} Indicates whether or not this ClipControl's
  15. {{#crossLink "ClipControl/planeSize:property"}}{{/crossLink}} is automatically sized to fit within
  16. the {{#crossLink "Scene/aabb:property"}}Scene's boundary{{/crossLink}}.
  17. */
  18. (function () {
  19.  
  20. "use strict";
  21.  
  22. xeogl.ClipControl = xeogl.Component.extend({
  23.  
  24. type: "xeogl.ClipControl",
  25.  
  26. _init: function (cfg) {
  27.  
  28. this._solid = false;
  29. this._visible = false;
  30. this._clipStartDir = xeogl.math.vec3();
  31. this._clipPos = xeogl.math.vec3();
  32.  
  33. this._gumballGroup = null;
  34. this._planeScale = null;
  35.  
  36. this._initEntities();
  37.  
  38. this.planeSize = cfg.planeSize;
  39. this.autoPlaneSize = cfg.autoPlaneSize;
  40. this.color = cfg.color;
  41. this.solid = cfg.solid;
  42. this.clip = cfg.clip;
  43. this.visible = cfg.visible;
  44. this.active = true;
  45.  
  46. this._initEvents();
  47. },
  48.  
  49. _update: (function () {
  50. var arrowPositions = new Float32Array(6);
  51. return function () {
  52.  
  53. var pos = this._pos;
  54. var dir = this._dir;
  55.  
  56. // Rebuild arrow geometry
  57.  
  58. arrowPositions[0] = pos[0];
  59. arrowPositions[1] = pos[1];
  60. arrowPositions[2] = pos[2];
  61. arrowPositions[3] = pos[0] + dir[0];
  62. arrowPositions[4] = pos[1] + dir[1];
  63. arrowPositions[5] = pos[2] + dir[2];
  64.  
  65. //this._display.arrow.geometry.positions = arrowPositions;
  66. }
  67. })(),
  68.  
  69. _props: {
  70.  
  71. /**
  72. Indicates whether this ClipControl is active or not.
  73.  
  74. @property active
  75. @default true
  76. @type Boolean
  77. */
  78. active: {
  79. set: function (value) {
  80. value = value !== false;
  81. if (value === this._active) {
  82. return;
  83. }
  84. this._active = value;
  85. },
  86. get: function () {
  87. return this._active;
  88. }
  89. },
  90.  
  91. /**
  92. The {{#crossLink "Clip"}}{{/crossLink}} attached to this ClipControl.
  93.  
  94. @property clip
  95. @type Clip
  96. */
  97. clip: {
  98. set: function (value) {
  99. var self = this;
  100. this._attach({name: "clip", type: "xeogl.Clip", component: value});
  101. var clip = this._attached.clip;
  102. if (clip) { // Reset rotation and translation basis
  103. this._setGumballDir(clip.dir);
  104. this._setGumballPos(clip.pos);
  105. }
  106. },
  107. get: function () {
  108. return this._attached.clip;
  109. }
  110. },
  111.  
  112. /**
  113. * The width and height of the ClipControl plane indicator.
  114. *
  115. * Values assigned to this property will be overridden by an auto-computed value when
  116. * {{#crossLink "ClipControl/autoPlaneSize:property"}}{{/crossLink}} is true.
  117. *
  118. * @property planeSize
  119. * @default [1,1]
  120. * @type {Float32Array}
  121. */
  122. planeSize: {
  123. set: function (value) {
  124. (this._planeSize = this._planeSize || new xeogl.math.vec2()).set(value || [1, 1]);
  125. // this._planeScale.xyz = [this._planeSize[0], this._planeSize[1], 1.0];
  126. },
  127. get: function () {
  128. return this._planeSize;
  129. }
  130. },
  131.  
  132. /**
  133. * Indicates whether this ClipControl's {{#crossLink "ClipControl/planeSize:property"}}{{/crossLink}} is automatically
  134. * generated or not.
  135. *
  136. * When auto-generated, {{#crossLink "ClipControl/planeSize:property"}}{{/crossLink}} will automatically size
  137. * to fit within the {{#crossLink "Scene/aabb:property"}}Scene's boundary{{/crossLink}}.
  138. *
  139. * @property autoPlaneSize
  140. * @default false
  141. * @type {Boolean}
  142. */
  143. autoPlaneSize: {
  144. set: function (value) {
  145. value = !!value;
  146. if (this._autoPlaneSize === value) {
  147. return;
  148. }
  149. this._autoPlaneSize = value;
  150. if (this._autoPlaneSize) {
  151. if (!this._onSceneAABB) {
  152. this._onSceneAABB = this.scene.on("boundary", function () {
  153. var aabbDiag = xeogl.math.getAABB3Diag(this.scene.aabb);
  154. var clipSize = (aabbDiag * 0.50);
  155. this.planeSize = [clipSize, clipSize];
  156. }, this);
  157. }
  158. } else {
  159. if (this._onSceneAABB) {
  160. this.scene.off(this._onSceneAABB);
  161. this._onSceneAABB = null;
  162. }
  163. }
  164. },
  165. get: function () {
  166. return this._autoPlaneSize;
  167. }
  168. },
  169.  
  170. /**
  171. * Emissive color of this ClipControl.
  172. *
  173. * @property color
  174. * @default [0.4,0.4,0.4]
  175. * @type {Float32Array}
  176. */
  177. color: {
  178. set: function (value) {
  179. (this._color = this._color || new xeogl.math.vec3()).set(value || [0.4, 0.4, 0.4]);
  180. this._display.planeWire.material.emissive = this._color;
  181. },
  182. get: function () {
  183. return this._color;
  184. }
  185. },
  186.  
  187. /**
  188. Indicates whether this ClipControl is filled with color or just wireframe.
  189.  
  190. @property solid
  191. @default true
  192. @type Boolean
  193. */
  194. solid: {
  195. set: function (value) {
  196. this._solid = value !== false;
  197. this._display.planeSolid.visible = this._solid && this._visible;
  198. },
  199. get: function () {
  200. return this._solid;
  201. }
  202. },
  203.  
  204. /**
  205. * Visibility of this ClipControl.
  206. *
  207. * @property visible
  208. * @type Boolean
  209. * @default true
  210. */
  211. visible: {
  212. set: function (value) {
  213. value = !!value;
  214. if (this._visible === value) {
  215. return;
  216. }
  217. this._visible = value;
  218. for (var id in this._display) {
  219. if (this._display.hasOwnProperty(id)) {
  220. this._display[id].visible = value;
  221. }
  222. }
  223. },
  224. get: function () {
  225. return this._visible;
  226. }
  227. }
  228. },
  229.  
  230. _setGumballPos: function (xyz) {
  231. this._clipPos.set(xyz);
  232. this._gumballGroup.position = xyz;
  233. },
  234.  
  235. _setGumballDir: (function () {
  236. var zeroVec = new Float32Array([0, 0, 1]);
  237. var quat = new Float32Array(4);
  238. return function (xyz) {
  239. this._clipStartDir.set(xyz);
  240. xeogl.math.vec3PairToQuaternion(zeroVec, xyz, quat);
  241. this._gumballGroup.quaternion = quat;
  242. };
  243. })(),
  244.  
  245. _initEntities: function () {
  246.  
  247. // Option for xeogl.Group.addChild(), to prevent child xeogl.Objects from inheriting
  248. // state from their parent xeogl.Group, such as 'pickable', 'visible', 'collidable' etc.
  249. // Although, the children's transforms are relative to the xeogl.Group.
  250. const DONT_INHERIT_GROUP_STATE = false;
  251.  
  252. // Positions, rotates & scales all Meshes as a group;
  253. // Meshes still have their relative transforms.
  254. var gumballGroup = this._gumballGroup = new xeogl.Group(this, {
  255. //scale: [10, 10, 0],
  256. position: [0, 0, 0]
  257. });
  258.  
  259. var radius = 1.2;
  260. var hoopRadius = radius - 0.2;
  261.  
  262. var geometries = {
  263.  
  264. arrowHead: new xeogl.CylinderGeometry(this, {
  265. radiusTop: 0.001,
  266. radiusBottom: 0.10,
  267. height: 0.2,
  268. radialSegments: 16,
  269. heightSegments: 1,
  270. openEnded: false
  271. }),
  272. curve: new xeogl.TorusGeometry(this, {
  273. radius: hoopRadius,
  274. tube: 0.0175,
  275. radialSegments: 64,
  276. tubeSegments: 14,
  277. arc: (Math.PI * 2.0) / 4.0
  278. }),
  279. hoop: new xeogl.TorusGeometry(this, {
  280. radius: hoopRadius,
  281. tube: 0.0175,
  282. radialSegments: 64,
  283. tubeSegments: 8,
  284. arc: (Math.PI * 2.0)
  285. }),
  286. curvePickable: new xeogl.TorusGeometry(this, {
  287. radius: hoopRadius,
  288. tube: 0.06,
  289. radialSegments: 64,
  290. tubeSegments: 14,
  291. arc: (Math.PI * 2.0) / 4.0
  292. }),
  293. axis: new xeogl.CylinderGeometry(scene, {
  294. radiusTop: 0.0175,
  295. radiusBottom: 0.0175,
  296. height: radius,
  297. radialSegments: 20,
  298. heightSegments: 1,
  299. openEnded: false
  300. })
  301. };
  302.  
  303. var materials = {
  304. pickable: new xeogl.PhongMaterial(this, {
  305. diffuse: [1, 1, 0],
  306. alpha: 0, // Invisible
  307. alphaMode: "blend"
  308. }),
  309. red: new xeogl.PhongMaterial(scene, {
  310. diffuse: [1, 0.3, 0.3],
  311. ambient: [0.0, 0.0, 0.0],
  312. specular: [.6, .6, .3],
  313. shininess: 80,
  314. lineWidth: 2
  315. }),
  316. transparentRed: new xeogl.PhongMaterial(scene, {
  317. diffuse: [1, 0.3, 0.3],
  318. ambient: [0.0, 0.0, 0.0],
  319. specular: [.6, .6, .3],
  320. shininess: 80,
  321. lineWidth: 2,
  322. alpha: 0.6,
  323. alphaMode: "blend"
  324. }),
  325. highlightRed: new xeogl.GhostMaterial(scene, {
  326. edges: false,
  327. fill: true,
  328. fillColor: [1, 0, 0],
  329. fillAlpha: 0.5,
  330. vertices: false
  331. }),
  332. labelRed: new xeogl.PhongMaterial(scene, {
  333. emissive: [1, 0.3, 0.3],
  334. ambient: [0.0, 0.0, 0.0],
  335. specular: [.6, .6, .3],
  336. shininess: 80,
  337. lineWidth: 3
  338. }),
  339. green: new xeogl.PhongMaterial(scene, {
  340. diffuse: [0.3, 1, 0.3],
  341. ambient: [0.0, 0.0, 0.0],
  342. specular: [.6, .6, .3],
  343. shininess: 80,
  344. lineWidth: 2
  345. }),
  346. highlightGreen: new xeogl.GhostMaterial(scene, {
  347. edges: false,
  348. fill: true,
  349. fillColor: [0, 1, 0],
  350. fillAlpha: 0.5,
  351. vertices: false
  352. }),
  353. transparentGreen: new xeogl.PhongMaterial(scene, {
  354. diffuse: [0.3, 1.0, 0.3],
  355. ambient: [0.0, 0.0, 0.0],
  356. specular: [.6, .6, .3],
  357. shininess: 80,
  358. lineWidth: 2,
  359. alpha: 0.4,
  360. alphaMode: "blend"
  361. }),
  362. labelGreen: new xeogl.PhongMaterial(scene, { // Green by convention
  363. emissive: [0.3, 1, 0.3],
  364. ambient: [0.0, 0.0, 0.0],
  365. specular: [.6, .6, .3],
  366. shininess: 80,
  367. lineWidth: 3
  368. }),
  369. blue: new xeogl.PhongMaterial(scene, { // Blue by convention
  370. diffuse: [0.3, 0.3, 1],
  371. ambient: [0.0, 0.0, 0.0],
  372. specular: [.6, .6, .3],
  373. shininess: 80,
  374. lineWidth: 2
  375. }),
  376. highlightBlue: new xeogl.GhostMaterial(scene, {
  377. edges: false,
  378. fill: true,
  379. fillColor: [0, 0, 1],
  380. fillAlpha: 0.5,
  381. vertices: false
  382. }),
  383. transparentBlue: new xeogl.PhongMaterial(scene, {
  384. diffuse: [0.3, 0.3, 1.0],
  385. ambient: [0.0, 0.0, 0.0],
  386. specular: [.6, .6, .3],
  387. shininess: 80,
  388. lineWidth: 2,
  389. alpha: 0.4,
  390. alphaMode: "blend"
  391. }),
  392. labelBlue: new xeogl.PhongMaterial(scene, {
  393. emissive: [0.3, 0.3, 1],
  394. ambient: [0.0, 0.0, 0.0],
  395. specular: [.6, .6, .3],
  396. shininess: 80,
  397. lineWidth: 3
  398. }),
  399. ball: new xeogl.PhongMaterial(scene, {
  400. diffuse: [0.5, 0.5, 0.5],
  401. ambient: [0.0, 0.0, 0.0],
  402. specular: [.6, .6, .3],
  403. shininess: 80,
  404. lineWidth: 2
  405. }),
  406. highlightBall: new xeogl.GhostMaterial(scene, {
  407. edges: false,
  408. fill: true,
  409. fillColor: [0.5, 0.5, 0.5],
  410. fillAlpha: 0.5,
  411. vertices: false
  412. }),
  413. highlightPlane: new xeogl.GhostMaterial(scene, {
  414. edges: true,
  415. edgeWidth: 3,
  416. fill: false,
  417. fillColor: [0.5, 0.5, .5],
  418. fillAlpha: 0.5,
  419. vertices: false
  420. })
  421. };
  422.  
  423. this._display = {
  424. planeWire: gumballGroup.addChild(new xeogl.Mesh(this, {
  425. geometry: new xeogl.Geometry(this, {
  426. primitive: "lines",
  427. positions: [
  428. 1.1, 1.1, 0.0, 1.1, -1.1, 0.0, // 0
  429. -1.1, -1.1, 0.0, -1.1, 1.1, 0.0, // 1
  430. 1.1, 1.1, -0.0, 1.1, -1.1, -0.0, // 2
  431. -1.1, -1.1, -0.0, -1.1, 1.1, -0.0 // 3
  432. ],
  433. indices: [0, 1, 0, 3, 1, 2, 2, 3]
  434. }),
  435. highlight: true,
  436. highlightMaterial: materials.highlightPlane,
  437. material: new xeogl.PhongMaterial(this, {
  438. emissive: [1, 0, 0],
  439. diffuse: [0, 0, 0],
  440. lineWidth: 2
  441. }),
  442. pickable: false,
  443. collidable: true,
  444. clippable: false
  445. }), DONT_INHERIT_GROUP_STATE),
  446.  
  447. planeSolid: gumballGroup.addChild(new xeogl.Mesh(this, {
  448. geometry: new xeogl.Geometry(this, {
  449. primitive: "triangles",
  450. positions: [
  451. 1.1, 1.1, 0.0, 1.1, -1.1, 0.0, // 0
  452. -1.1, -1.1, 0.0, -1.1, 1.1, 0.0, // 1
  453. 1.1, 1.1, -0.0, 1.1, -1.1, -0.0, // 2
  454. -1.1, -1.1, -0.0, -1.1, 1.1, -0.0 // 3
  455. ],
  456. indices: [0, 1, 2, 2, 3, 0]
  457. }),
  458. highlight: true,
  459. highlightMaterial: materials.highlightPlane,
  460. material: new xeogl.PhongMaterial(this, {
  461. emissive: [0, 0, 0],
  462. diffuse: [0, 0, 0],
  463. specular: [1, 1, 1],
  464. shininess: 120,
  465. alpha: 0.3,
  466. alphaMode: "blend",
  467. backfaces: true
  468. }),
  469. pickable: false,
  470. collidable: true,
  471. clippable: false,
  472. backfaces: true
  473. }), DONT_INHERIT_GROUP_STATE),
  474.  
  475. xRedCurve: gumballGroup.addChild(new xeogl.Mesh(this, { // Red hoop about Y-axis
  476. geometry: geometries.curve,
  477. material: materials.red,
  478. highlight: true,
  479. highlightMaterial: materials.highlightRed,
  480. matrix: (function () {
  481. var rotate2 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [0, 1, 0], xeogl.math.identityMat4());
  482. var rotate1 = xeogl.math.rotationMat4v(270 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  483. return xeogl.math.mulMat4(rotate1, rotate2, xeogl.math.identityMat4());
  484. })(),
  485. pickable: false,
  486. collidable: true,
  487. clippable: false,
  488. backfaces: true
  489. }), DONT_INHERIT_GROUP_STATE),
  490.  
  491. xRedCurvePickable: gumballGroup.addChild(new xeogl.Mesh(this, {
  492. geometry: geometries.curvePickable,
  493. material: materials.pickable,
  494. matrix: (function () {
  495. var rotate2 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [0, 1, 0], xeogl.math.identityMat4());
  496. var rotate1 = xeogl.math.rotationMat4v(270 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  497. return xeogl.math.mulMat4(rotate1, rotate2, xeogl.math.identityMat4());
  498. })(),
  499. pickable: true,
  500. collidable: true,
  501. clippable: false
  502. }), DONT_INHERIT_GROUP_STATE),
  503.  
  504.  
  505.  
  506. yGreenCurve: gumballGroup.addChild(new xeogl.Mesh(this, {
  507. geometry: geometries.curve,
  508. material: materials.green,
  509. highlight: true,
  510. highlightMaterial: materials.highlightGreen,
  511. rotation: [-90, 0, 0],
  512. pickable: false,
  513. collidable: true,
  514. clippable: false,
  515. backfaces: true
  516. }), DONT_INHERIT_GROUP_STATE),
  517.  
  518. yGreenCurvePickable: gumballGroup.addChild(new xeogl.Mesh(this, {
  519. geometry: geometries.curvePickable,
  520. material: materials.pickable,
  521. rotation: [-90, 0, 0],
  522. pickable: true,
  523. collidable: true,
  524. clippable: false
  525. }), DONT_INHERIT_GROUP_STATE),
  526.  
  527. zBlueCurve: gumballGroup.addChild(new xeogl.Mesh(this, { // Blue hoop about Z-axis
  528. geometry: geometries.curve,
  529. material: materials.blue,
  530. highlight: true,
  531. highlightMaterial: materials.highlightBlue,
  532. matrix: (function () {
  533. var rotate2 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  534. var rotate1 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  535. return xeogl.math.mulMat4(rotate2, rotate1, xeogl.math.identityMat4());
  536. })(),
  537. pickable: false,
  538. collidable: true,
  539. clippable: false,
  540. backfaces: true
  541. }), DONT_INHERIT_GROUP_STATE),
  542.  
  543. zBlueCurvePickable: gumballGroup.addChild(new xeogl.Mesh(this, {
  544. geometry: geometries.curvePickable,
  545. material: materials.pickable,
  546.  
  547. matrix: (function () {
  548. var rotate2 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  549. var rotate1 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  550. return xeogl.math.mulMat4(rotate2, rotate1, xeogl.math.identityMat4());
  551. })(),
  552. pickable: true,
  553. collidable: true,
  554. clippable: false
  555. }), DONT_INHERIT_GROUP_STATE),
  556.  
  557. ball: gumballGroup.addChild(new xeogl.Mesh(this, {
  558. geometry: new xeogl.SphereGeometry(this, {
  559. radius: 0.05
  560. }),
  561. highlight: true,
  562. highlightMaterial: materials.highlightBall,
  563. material: materials.ball,
  564. pickable: false,
  565. collidable: true,
  566. clippable: false
  567. }), DONT_INHERIT_GROUP_STATE),
  568.  
  569. xRedArrow: gumballGroup.addChild(new xeogl.Mesh(this, {
  570. geometry: geometries.arrowHead,
  571. material: materials.red,
  572. highlight: true,
  573. highlightMaterial: materials.highlightRed,
  574. matrix: (function () {
  575. var translate = xeogl.math.translateMat4c(0, radius + .1, 0, xeogl.math.identityMat4());
  576. var rotate = xeogl.math.rotationMat4v(-90 * xeogl.math.DEGTORAD, [0, 0, 1], xeogl.math.identityMat4());
  577. return xeogl.math.mulMat4(rotate, translate, xeogl.math.identityMat4());
  578. })(),
  579. pickable: true,
  580. collidable: true,
  581. clippable: false
  582. }), DONT_INHERIT_GROUP_STATE),
  583.  
  584. xRedShaft: gumballGroup.addChild(new xeogl.Mesh(this, {
  585. geometry: geometries.axis,
  586. material: materials.red,
  587. highlight: true,
  588. highlightMaterial: materials.highlightRed,
  589. matrix: (function () {
  590. var translate = xeogl.math.translateMat4c(0, radius / 2, 0, xeogl.math.identityMat4());
  591. var rotate = xeogl.math.rotationMat4v(-90 * xeogl.math.DEGTORAD, [0, 0, 1], xeogl.math.identityMat4());
  592. return xeogl.math.mulMat4(rotate, translate, xeogl.math.identityMat4());
  593. })(),
  594. pickable: false,
  595. collidable: true,
  596. clippable: false
  597. }), DONT_INHERIT_GROUP_STATE),
  598.  
  599. yGreenArrow: gumballGroup.addChild(new xeogl.Mesh(this, {
  600. geometry: geometries.arrowHead,
  601. material: materials.green,
  602. highlight: true,
  603. highlightMaterial: materials.highlightGreen,
  604. matrix: (function () {
  605. var translate = xeogl.math.translateMat4c(0, radius + .1, 0, xeogl.math.identityMat4());
  606. var rotate = xeogl.math.rotationMat4v(180 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  607. return xeogl.math.mulMat4(rotate, translate, xeogl.math.identityMat4());
  608. })(),
  609. pickable: true,
  610. collidable: true,
  611. clippable: false
  612. }), DONT_INHERIT_GROUP_STATE),
  613.  
  614. yGreenShaft: gumballGroup.addChild(new xeogl.Mesh(this, {
  615. geometry: geometries.axis,
  616. material: materials.green,
  617. highlight: true,
  618. highlightMaterial: materials.highlightGreen,
  619. position: [0, -radius / 2, 0],
  620. pickable: false,
  621. collidable: true,
  622. clippable: false
  623. }), DONT_INHERIT_GROUP_STATE),
  624.  
  625. zBlueArrow: gumballGroup.addChild(new xeogl.Mesh(this, {
  626. geometry: geometries.arrowHead,
  627. material: materials.blue,
  628. highlight: true,
  629. highlightMaterial: materials.highlightBlue,
  630. matrix: (function () {
  631. var translate = xeogl.math.translateMat4c(0, radius + .1, 0, xeogl.math.identityMat4());
  632. var rotate = xeogl.math.rotationMat4v(-90 * xeogl.math.DEGTORAD, [0.8, 0, 0], xeogl.math.identityMat4());
  633. return xeogl.math.mulMat4(rotate, translate, xeogl.math.identityMat4());
  634. })(),
  635. pickable: true,
  636. collidable: true,
  637. clippable: false
  638. }), DONT_INHERIT_GROUP_STATE),
  639.  
  640. zBlueShaft: gumballGroup.addChild(new xeogl.Mesh(this, {
  641. geometry: geometries.axis,
  642. material: materials.blue,
  643. highlight: true,
  644. highlightMaterial: materials.highlightBlue,
  645. matrix: (function () {
  646. var translate = xeogl.math.translateMat4c(0, radius / 2, 0, xeogl.math.identityMat4());
  647. var rotate = xeogl.math.rotationMat4v(-90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  648. return xeogl.math.mulMat4(rotate, translate, xeogl.math.identityMat4());
  649. })(),
  650. clippable: false,
  651. pickable: false,
  652. collidable: true
  653. }), DONT_INHERIT_GROUP_STATE)
  654. };
  655.  
  656. this._hoops = {
  657.  
  658. xHoop: gumballGroup.addChild(new xeogl.Mesh(this, { // Red hoop about Y-axis
  659. geometry: geometries.hoop,
  660. material: materials.transparentRed,
  661. highlight: true,
  662. highlightMaterial: materials.highlightRed,
  663. matrix: (function () {
  664. var rotate2 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [0, 1, 0], xeogl.math.identityMat4());
  665. var rotate1 = xeogl.math.rotationMat4v(270 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  666. return xeogl.math.mulMat4(rotate1, rotate2, xeogl.math.identityMat4());
  667. })(),
  668. pickable: false,
  669. collidable: true,
  670. clippable: false,
  671. visible: false
  672. }), DONT_INHERIT_GROUP_STATE),
  673.  
  674. yHoop: gumballGroup.addChild(new xeogl.Mesh(this, { // Green hoop about Y-axis
  675. geometry: geometries.hoop,
  676. material: materials.transparentGreen,
  677. highlight: true,
  678. highlightMaterial: materials.highlightGreen,
  679. rotation: [-90, 0, 0],
  680. pickable: false,
  681. collidable: true,
  682. clippable: false,
  683. visible: false
  684. }), DONT_INHERIT_GROUP_STATE),
  685.  
  686. zHoop: gumballGroup.addChild(new xeogl.Mesh(this, { // Blue hoop about Z-axis
  687. geometry: geometries.hoop,
  688. material: materials.transparentBlue,
  689. highlight: true,
  690. highlightMaterial: materials.highlightBlue,
  691. matrix: (function () {
  692. var rotate2 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  693. var rotate1 = xeogl.math.rotationMat4v(90 * xeogl.math.DEGTORAD, [1, 0, 0], xeogl.math.identityMat4());
  694. return xeogl.math.mulMat4(rotate2, rotate1, xeogl.math.identityMat4());
  695. })(),
  696. pickable: false,
  697. collidable: true,
  698. clippable: false,
  699. visible: false
  700. }), DONT_INHERIT_GROUP_STATE)
  701. };
  702. },
  703.  
  704. _initEvents: function () {
  705.  
  706. var self = this;
  707. var scene = this.scene;
  708. var math = xeogl.math;
  709. var canvas = this.scene.canvas.canvas;
  710. var over = false;
  711.  
  712. const DRAG_ACTIONS = {
  713. none: -1,
  714. xPan: 0,
  715. yPan: 1,
  716. zPan: 2,
  717. xRotate: 3,
  718. yRotate: 4,
  719. zRotate: 5
  720. };
  721.  
  722. var nextDragAction = null; // As we hover over an arrow or hoop, self is the action we would do if we then dragged it.
  723. var dragAction = null; // Action we're doing while we drag an arrow or hoop.
  724.  
  725. var lastMouse = math.vec2();
  726.  
  727. var xLocalAxis = math.vec3([1, 0, 0]);
  728. var yLocalAxis = math.vec3([0, 1, 0]);
  729. var zLocalAxis = math.vec3([0, 0, 1]);
  730.  
  731. canvas.oncontextmenu = function (e) {
  732. e.preventDefault();
  733. };
  734.  
  735. var getClickCoordsWithinElement = (function () {
  736. var coords = new Float32Array(2);
  737. return function (event) {
  738. if (!event) {
  739. event = window.event;
  740. coords[0] = event.x;
  741. coords[a] = event.y;
  742. } else {
  743. var element = event.target;
  744. var totalOffsetLeft = 0;
  745. var totalOffsetTop = 0;
  746.  
  747. while (element.offsetParent) {
  748. totalOffsetLeft += element.offsetLeft;
  749. totalOffsetTop += element.offsetTop;
  750. element = element.offsetParent;
  751. }
  752. coords[0] = event.pageX - totalOffsetLeft;
  753. coords[1] = event.pageY - totalOffsetTop;
  754. }
  755. return coords;
  756. };
  757. })();
  758.  
  759. var localToWorldVec = (function () {
  760. var math = xeogl.math;
  761. var mat = math.mat4();
  762. return function (localVec, worldVec) {
  763. math.quaternionToMat4(self._gumballGroup.quaternion, mat);
  764. math.transformVec3(mat, localVec, worldVec);
  765. math.normalizeVec3(worldVec);
  766. return worldVec;
  767. };
  768. })();
  769.  
  770. var pan = (function() {
  771. var p1 = math.vec3();
  772. var p2 = math.vec3();
  773. var worldAxis = math.vec4();
  774.  
  775. return function (localAxis, fromMouse, toMouse) {
  776. localToWorldVec(localAxis, worldAxis);
  777.  
  778. var planeNormal = getTranslationPlane(worldAxis, fromMouse, toMouse);
  779.  
  780. getMouseVectorOnPlane(fromMouse, planeNormal, p1);
  781. getMouseVectorOnPlane(toMouse, planeNormal, p2);
  782.  
  783. math.subVec3(p2, p1);
  784.  
  785. var dot = math.dotVec3(p2, worldAxis);
  786.  
  787. self._clipPos[0] += worldAxis[0] * dot;
  788. self._clipPos[1] += worldAxis[1] * dot;
  789. self._clipPos[2] += worldAxis[2] * dot;
  790.  
  791. self._gumballGroup.position = self._clipPos;
  792. if (self._attached.clip) {
  793. self._attached.clip.pos = self._clipPos;
  794. }
  795. }
  796. })();
  797.  
  798. var getTranslationPlane = (function() {
  799. var planeNormal = math.vec3();
  800. return function(worldAxis) {
  801. // find a best fit to find intersections with
  802. var absX = Math.abs(worldAxis.x);
  803. if (absX > Math.abs(worldAxis.y) && absX > Math.abs(worldAxis.z))
  804. math.cross3Vec3(worldAxis, [0, 1, 0], planeNormal);
  805. else
  806. math.cross3Vec3(worldAxis, [1, 0, 0], planeNormal);
  807.  
  808. math.cross3Vec3(planeNormal, worldAxis, planeNormal);
  809.  
  810. math.normalizeVec3(planeNormal);
  811. return planeNormal;
  812. }
  813. })();
  814.  
  815. var rotate = (function() {
  816. var p1 = math.vec4();
  817. var p2 = math.vec4();
  818. var c = math.vec4();
  819. var worldAxis = math.vec4();
  820.  
  821. return function (localAxis, fromMouse, toMouse) {
  822. localToWorldVec(localAxis, worldAxis);
  823.  
  824. var dot;
  825. var hasData = getMouseVectorOnPlane(fromMouse, worldAxis, p1);
  826. hasData = hasData && getMouseVectorOnPlane(toMouse, worldAxis, p2);
  827.  
  828. if (!hasData) {
  829. // find intersections with view plane and project down to origin
  830. var planeNormal = getTranslationPlane(worldAxis, fromMouse, toMouse);
  831.  
  832. // the "1" makes sure the plane moves closer to the camera a bit, so the angles become workable
  833. getMouseVectorOnPlane(fromMouse, planeNormal, p1, 1);
  834. getMouseVectorOnPlane(toMouse, planeNormal, p2, 1);
  835. dot = math.dotVec3(p1, worldAxis);
  836. p1[0] -= dot * worldAxis[0];
  837. p1[1] -= dot * worldAxis[1];
  838. p1[2] -= dot * worldAxis[2];
  839.  
  840. dot = math.dotVec3(p2, worldAxis);
  841. p2[0] -= dot * worldAxis[0];
  842. p2[1] -= dot * worldAxis[1];
  843. p2[2] -= dot * worldAxis[2];
  844. }
  845.  
  846. math.normalizeVec3(p1);
  847. math.normalizeVec3(p2);
  848.  
  849. dot = math.dotVec3(p1, p2);
  850. // rounding errors can cause the dot to exceed its allowed range
  851. dot = math.clamp(dot, -1.0, 1.0);
  852. var incDegrees = Math.acos(dot) * math.RADTODEG;
  853.  
  854. // console.log(incDegrees);
  855. math.cross3Vec3(p1, p2, c);
  856. // test orientation of cross with actual axis
  857. if (math.dotVec3(c, worldAxis) < 0.0)
  858. incDegrees = -incDegrees;
  859.  
  860. self._gumballGroup.rotate(localAxis, incDegrees);
  861. rotateClip();
  862. }})();
  863.  
  864. // this returns the vector that points from the gumball origin to where the mouse ray intersects the plane
  865. var getMouseVectorOnPlane = (function() {
  866. var dir = math.vec4([0, 0, 0, 1]);
  867. var matrix = math.mat4();
  868.  
  869. return function(mouse, axis, dest, offset) {
  870. offset = offset || 0;
  871. dir[0] = mouse[0] / canvas.width * 2.0 - 1.0;
  872. dir[1] = -(mouse[1] / canvas.height * 2.0 - 1.0);
  873. dir[2] = 0.0;
  874. dir[3] = 1.0;
  875.  
  876. // unproject ndc to view coords
  877. math.mulMat4(camera.projMatrix, camera.viewMatrix, matrix);
  878. math.inverseMat4(matrix);
  879. math.transformVec4(matrix, dir, dir);
  880.  
  881. // this is now "a" point on the ray in world space
  882. math.mulVec4Scalar(dir, 1.0 / dir[3]);
  883.  
  884. // the direction
  885. var rayO = camera.eye;
  886. math.subVec4(dir, rayO, dir);
  887.  
  888. // the plane origin:
  889. var origin = clip.pos;
  890.  
  891. var d = -math.dotVec3(origin, axis) - offset;
  892. var dot = math.dotVec3(axis, dir);
  893.  
  894. console.log(Math.abs(dot));
  895. if (Math.abs(dot) > 0.005) {
  896. var t = -(math.dotVec3(axis, rayO) + d) / dot;
  897. math.mulVec3Scalar(dir, t, dest);
  898. math.addVec3(dest, rayO);
  899. math.subVec3(dest, origin, dest)
  900. return true;
  901. }
  902.  
  903. return false;
  904. }
  905. })();
  906.  
  907. var rotateClip = (function () {
  908. var math = xeogl.math;
  909. var dir = math.vec3();
  910. var mat = math.mat4();
  911.  
  912. return function () {
  913. if (self._attached.clip) {
  914. math.quaternionToMat4(self._gumballGroup.quaternion, mat); // << ---
  915. math.transformVec3(mat, [0, 0, 1], dir);
  916. self._attached.clip.dir = dir;
  917. }
  918. };
  919. })();
  920.  
  921. var pick = (function () {
  922.  
  923. var lastHighlightedMesh;
  924. var lastShownMesh;
  925.  
  926. return function pick(canvasPos) {
  927.  
  928. var hit = scene.pick({
  929. canvasPos: canvasPos
  930. });
  931.  
  932. if (lastHighlightedMesh) {
  933. lastHighlightedMesh.highlight = false;
  934. }
  935.  
  936. if (lastShownMesh) {
  937. lastShownMesh.visible = false;
  938. }
  939.  
  940. if (hit) {
  941.  
  942. var id = hit.mesh.id;
  943.  
  944. var highlightMesh;
  945. var shownMesh;
  946.  
  947. switch (id) {
  948. case self._display.xRedArrow.id:
  949. highlightMesh = self._display.xRedArrow;
  950. nextDragAction = DRAG_ACTIONS.xPan;
  951. // localToWorldVec(xLocalAxis, panWorldVec);
  952. // worldToCanvasVec(panWorldVec, panCanvasVec);
  953. break;
  954.  
  955. case self._display.yGreenArrow.id:
  956. highlightMesh = self._display.yGreenArrow;
  957. nextDragAction = DRAG_ACTIONS.yPan;
  958. // localToWorldVec(yLocalAxis, panWorldVec);
  959. // worldToCanvasVec(panWorldVec, panCanvasVec);
  960. break;
  961.  
  962. case self._display.zBlueArrow.id:
  963. highlightMesh = self._display.zBlueArrow;
  964. nextDragAction = DRAG_ACTIONS.zPan;
  965. // localToWorldVec(zLocalAxis, panWorldVec);
  966. // worldToCanvasVec(panWorldVec, panCanvasVec);
  967. break;
  968.  
  969. case self._display.xRedCurvePickable.id:
  970. highlightMesh = self._display.xRedCurve;
  971. shownMesh = self._hoops.xHoop;
  972. nextDragAction = DRAG_ACTIONS.xRotate;
  973. break;
  974.  
  975. case self._display.yGreenCurvePickable.id:
  976. highlightMesh = self._display.yGreenCurve;
  977. shownMesh = self._hoops.yHoop;
  978. nextDragAction = DRAG_ACTIONS.yRotate;
  979. break;
  980.  
  981. case self._display.zBlueCurvePickable.id:
  982. highlightMesh = self._display.zBlueCurve;
  983. shownMesh = self._hoops.zHoop;
  984. nextDragAction = DRAG_ACTIONS.zRotate;
  985. break;
  986.  
  987. default:
  988. nextDragAction = DRAG_ACTIONS.none;
  989. return; // Not clicked an arrow or hoop
  990. }
  991.  
  992. if (highlightMesh) {
  993. highlightMesh.highlight = true;
  994. }
  995.  
  996. if (shownMesh) {
  997. shownMesh.visible = true;
  998. }
  999.  
  1000. lastHighlightedMesh = highlightMesh;
  1001. lastShownMesh = shownMesh;
  1002.  
  1003. } else {
  1004.  
  1005. lastHighlightedMesh = null;
  1006. lastShownMesh = null;
  1007. nextDragAction = DRAG_ACTIONS.none;
  1008. }
  1009. };
  1010. })();
  1011.  
  1012. (function () {
  1013.  
  1014. var down = false;
  1015.  
  1016. var mouseDownLeft;
  1017. var mouseDownMiddle;
  1018. var mouseDownRight;
  1019.  
  1020. canvas.addEventListener("mousemove", function (e) {
  1021.  
  1022. if (!self._active) {
  1023. return;
  1024. }
  1025.  
  1026. if (!over) {
  1027. return;
  1028. }
  1029.  
  1030. var coords = getClickCoordsWithinElement(e);
  1031.  
  1032. if (!down) {
  1033. pick(coords);
  1034. return;
  1035. }
  1036.  
  1037. var x = coords[0];
  1038. var y = coords[1];
  1039.  
  1040. updateControls(coords, lastMouse);
  1041.  
  1042. lastMouse[0] = x;
  1043. lastMouse[1] = y;
  1044. });
  1045.  
  1046. canvas.addEventListener("mousedown", function (e) {
  1047. if (!self._active) {
  1048. return;
  1049. }
  1050. if (!over) {
  1051. return;
  1052. }
  1053. switch (e.which) {
  1054.  
  1055. case 1: // Left button
  1056.  
  1057. mouseDownLeft = true;
  1058. down = true;
  1059. var coords = getClickCoordsWithinElement(e);
  1060.  
  1061. dragAction = nextDragAction;
  1062.  
  1063. lastMouse[0] = coords[0];
  1064. lastMouse[1] = coords[1];
  1065.  
  1066. break;
  1067.  
  1068. default:
  1069. break;
  1070. }
  1071. });
  1072.  
  1073. canvas.addEventListener("mouseup", function (e) {
  1074. if (!self._active) {
  1075. return;
  1076. }
  1077. switch (e.which) {
  1078. case 1: // Left button
  1079. mouseDownLeft = false;
  1080. break;
  1081. case 2: // Middle/both buttons
  1082. mouseDownMiddle = false;
  1083. break;
  1084. case 3: // Right button
  1085. mouseDownRight = false;
  1086. break;
  1087. default:
  1088. break;
  1089. }
  1090. down = false;
  1091. });
  1092.  
  1093. canvas.addEventListener("mouseenter", function () {
  1094. if (!self._active) {
  1095. return;
  1096. }
  1097. over = true;
  1098. });
  1099.  
  1100. canvas.addEventListener("mouseleave", function () {
  1101. if (!self._active) {
  1102. return;
  1103. }
  1104. over = false;
  1105. });
  1106.  
  1107. canvas.addEventListener("wheel", function (e) {
  1108. if (!self._active) {
  1109. return;
  1110. }
  1111. var delta = Math.max(-1, Math.min(1, -e.deltaY * 40));
  1112. if (delta === 0) {
  1113. return;
  1114. }
  1115. e.preventDefault();
  1116. });
  1117.  
  1118. function updateControls(mouse, oldMouse) {
  1119.  
  1120. if (dragAction === DRAG_ACTIONS.none) {
  1121. return;
  1122. }
  1123.  
  1124. switch (dragAction) {
  1125. case DRAG_ACTIONS.xPan:
  1126. // defined by projections on axis
  1127. pan(xLocalAxis, oldMouse, mouse);
  1128. break;
  1129. case DRAG_ACTIONS.yPan:
  1130. pan(yLocalAxis, oldMouse, mouse);
  1131. break;
  1132. case DRAG_ACTIONS.zPan:
  1133. pan(zLocalAxis, oldMouse, mouse);
  1134. break;
  1135. case DRAG_ACTIONS.xRotate:
  1136. rotate(xLocalAxis, oldMouse, mouse);
  1137. break;
  1138. case DRAG_ACTIONS.yRotate:
  1139. rotate(yLocalAxis, oldMouse, mouse);
  1140. break;
  1141. case DRAG_ACTIONS.zRotate:
  1142. rotate(zLocalAxis, oldMouse, mouse);
  1143. break;
  1144. }
  1145. }
  1146.  
  1147. })();
  1148. },
  1149.  
  1150. _destroy: function () {
  1151. if (this._onSceneAABB) {
  1152. this.scene.off(this._onSceneAABB);
  1153. }
  1154. }
  1155. });
  1156. })();