aframe-route.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. if (typeof AFRAME === 'undefined') {
  2. throw new Error('Component attempted to register before AFRAME was available.');
  3. }
  4. /**
  5. * Curve component for A-Frame to deal with spline curves
  6. */
  7. var zAxis = new THREE.Vector3(0, 0, 1);
  8. var degToRad = THREE.Math.degToRad;
  9. AFRAME.registerComponent('curve-point', {
  10. //dependencies: ['position'],
  11. schema: {},
  12. init: function () {
  13. this.el.addEventListener("componentchanged", this.changeHandler.bind(this));
  14. this.el.emit("curve-point-change");
  15. },
  16. changeHandler: function (event) {
  17. if (event.detail.name == "position") {
  18. this.el.emit("curve-point-change");
  19. }
  20. }
  21. });
  22. AFRAME.registerComponent('curve', {
  23. //dependencies: ['curve-point'],
  24. schema: {
  25. type: {
  26. type: 'string',
  27. default: 'CatmullRom',
  28. oneOf: ['CatmullRom', 'CubicBezier', 'QuadraticBezier', 'Line']
  29. },
  30. closed: {
  31. type: 'boolean',
  32. default: false
  33. }
  34. },
  35. init: function () {
  36. this.pathPoints = null;
  37. this.curve = null;
  38. this.el.addEventListener("curve-point-change", this.update.bind(this));
  39. this.el.addEventListener("gps-entity-place-update-position", this.update.bind(this));
  40. },
  41. update: function (oldData) {
  42. this.points = Array.from(this.el.querySelectorAll("a-curve-point, [curve-point]"));
  43. console.log(this.points);
  44. if (this.points.length <= 1) {
  45. console.warn("At least 2 curve-points needed to draw a curve");
  46. this.curve = null;
  47. } else {
  48. // Get Array of Positions from Curve-Points
  49. var pointsArray = this.points.map(function (point) {
  50. if (point.x !== undefined && point.y !== undefined && point.z !== undefined) {
  51. return point;
  52. }
  53. return point.object3D.getWorldPosition();
  54. });
  55. // Update the Curve if either the Curve-Points or other Properties changed
  56. if (!AFRAME.utils.deepEqual(pointsArray, this.pathPoints) || (oldData !== 'CustomEvent' && !AFRAME.utils.deepEqual(this.data, oldData))) {
  57. this.curve = null;
  58. this.pathPoints = pointsArray;
  59. // Create Curve
  60. switch (this.data.type) {
  61. case 'CubicBezier':
  62. if (this.pathPoints.length != 4) {
  63. throw new Error('The Three constructor of type CubicBezierCurve3 requires 4 points');
  64. }
  65. this.curve = new THREE.CubicBezierCurve3(this.pathPoints[0], this.pathPoints[1], this.pathPoints[2], this.pathPoints[3]);
  66. break;
  67. case 'QuadraticBezier':
  68. if (this.pathPoints.length != 3) {
  69. throw new Error('The Three constructor of type QuadraticBezierCurve3 requires 3 points');
  70. }
  71. this.curve = new THREE.QuadraticBezierCurve3(this.pathPoints[0], this.pathPoints[1], this.pathPoints[2]);
  72. break;
  73. case 'Line':
  74. if (this.pathPoints.length != 2) {
  75. throw new Error('The Three constructor of type LineCurve3 requires 2 points');
  76. }
  77. this.curve = new THREE.LineCurve3(this.pathPoints[0], this.pathPoints[1]);
  78. break;
  79. case 'CatmullRom':
  80. this.curve = new THREE.CatmullRomCurve3(this.pathPoints);
  81. break;
  82. case 'Spline':
  83. this.curve = new THREE.SplineCurve3(this.pathPoints);
  84. break;
  85. default:
  86. throw new Error('No Three constructor of type (case sensitive): ' + this.data.type + 'Curve3');
  87. }
  88. this.curve.closed = this.data.closed;
  89. this.el.emit('curve-updated');
  90. }
  91. }
  92. },
  93. remove: function () {
  94. this.el.removeEventListener("curve-point-change", this.update.bind(this));
  95. },
  96. closestPointInLocalSpace: function closestPoint(point, resolution, testPoint, currentRes) {
  97. if (!this.curve) throw Error('Curve not instantiated yet.');
  98. resolution = resolution || 0.1 / this.curve.getLength();
  99. currentRes = currentRes || 0.5;
  100. testPoint = testPoint || 0.5;
  101. currentRes /= 2;
  102. var aTest = testPoint + currentRes;
  103. var bTest = testPoint - currentRes;
  104. var a = this.curve.getPointAt(aTest);
  105. var b = this.curve.getPointAt(bTest);
  106. var aDistance = a.distanceTo(point);
  107. var bDistance = b.distanceTo(point);
  108. var aSmaller = aDistance < bDistance;
  109. if (currentRes < resolution) {
  110. var tangent = this.curve.getTangentAt(aSmaller ? aTest : bTest);
  111. if (currentRes < resolution) return {
  112. result: aSmaller ? aTest : bTest,
  113. location: aSmaller ? a : b,
  114. distance: aSmaller ? aDistance : bDistance,
  115. normal: normalFromTangent(tangent),
  116. tangent: tangent
  117. };
  118. }
  119. if (aDistance < bDistance) {
  120. return this.closestPointInLocalSpace(point, resolution, aTest, currentRes);
  121. } else {
  122. return this.closestPointInLocalSpace(point, resolution, bTest, currentRes);
  123. }
  124. }
  125. });
  126. var tempQuaternion = new THREE.Quaternion();
  127. function normalFromTangent(tangent) {
  128. var lineEnd = new THREE.Vector3(0, 1, 0);
  129. tempQuaternion.setFromUnitVectors(zAxis, tangent);
  130. lineEnd.applyQuaternion(tempQuaternion);
  131. return lineEnd;
  132. }
  133. AFRAME.registerShader('line', {
  134. schema: {
  135. color: {default: '#ff0000'},
  136. },
  137. init: function (data) {
  138. this.material = new THREE.LineBasicMaterial(data);
  139. },
  140. update: function (data) {
  141. this.material = new THREE.LineBasicMaterial(data);
  142. },
  143. });
  144. AFRAME.registerComponent('draw-curve', {
  145. //dependencies: ['curve', 'material'],
  146. schema: {
  147. curve: {type: 'selector'}
  148. },
  149. init: function () {
  150. console.log(this.data);
  151. this.data.curve.addEventListener('curve-updated', this.update.bind(this));
  152. },
  153. update: function () {
  154. if (this.data.curve) {
  155. this.curve = this.data.curve.components.curve;
  156. }
  157. if (this.curve && this.curve.curve) {
  158. var lineGeometry = new THREE.BufferGeometry().setFromPoints(this.curve.curve.getPoints(this.curve.curve.getPoints().length * 10));
  159. var mesh = this.el.getOrCreateObject3D('mesh', THREE.Line);
  160. var lineMaterial = mesh.material ? mesh.material : new THREE.LineBasicMaterial({
  161. color: "#ff0000"
  162. });
  163. this.el.setObject3D('mesh', new THREE.Line(lineGeometry, lineMaterial));
  164. }
  165. },
  166. remove: function () {
  167. this.data.curve.removeEventListener('curve-updated', this.update.bind(this));
  168. this.el.getObject3D('mesh').geometry = new THREE.Geometry();
  169. }
  170. });
  171. AFRAME.registerComponent('clone-along-curve', {
  172. //dependencies: ['curve'],
  173. schema: {
  174. curve: {type: 'selector'},
  175. spacing: {default: 1},
  176. rotation: {
  177. type: 'vec3',
  178. default: '0 0 0'
  179. },
  180. scale: {
  181. type: 'vec3',
  182. default: '1 1 1'
  183. }
  184. },
  185. init: function () {
  186. this.el.addEventListener('model-loaded', this.update.bind(this));
  187. this.data.curve.addEventListener('curve-updated', this.update.bind(this));
  188. },
  189. update: function () {
  190. this.remove();
  191. if (this.data.curve) {
  192. this.curve = this.data.curve.components.curve;
  193. }
  194. if (!this.el.getObject3D('clones') && this.curve && this.curve.curve) {
  195. var mesh = this.el.getObject3D('mesh');
  196. var length = this.curve.curve.getLength();
  197. var start = 0;
  198. var counter = start;
  199. var cloneMesh = this.el.getOrCreateObject3D('clones', THREE.Group);
  200. var parent = new THREE.Object3D();
  201. mesh.scale.set(this.data.scale.x, this.data.scale.y, this.data.scale.z);
  202. mesh.rotation.set(degToRad(this.data.rotation.x), degToRad(this.data.rotation.y), degToRad(this.data.rotation.z));
  203. mesh.rotation.order = 'YXZ';
  204. parent.add(mesh);
  205. while (counter <= length) {
  206. var child = parent.clone(true);
  207. child.position.copy(this.curve.curve.getPointAt(counter / length));
  208. tangent = this.curve.curve.getTangentAt(counter / length).normalize();
  209. child.quaternion.setFromUnitVectors(zAxis, tangent);
  210. cloneMesh.add(child);
  211. counter += this.data.spacing;
  212. }
  213. }
  214. },
  215. remove: function () {
  216. this.curve = null;
  217. if (this.el.getObject3D('clones')) {
  218. this.el.removeObject3D('clones');
  219. }
  220. }
  221. });
  222. AFRAME.registerPrimitive('a-draw-curve', {
  223. defaultComponents: {
  224. 'draw-curve': {},
  225. },
  226. mappings: {
  227. curveref: 'draw-curve.curve',
  228. }
  229. });
  230. AFRAME.registerPrimitive('a-curve-point', {
  231. defaultComponents: {
  232. 'curve-point': {},
  233. },
  234. mappings: {}
  235. });
  236. AFRAME.registerPrimitive('a-curve', {
  237. defaultComponents: {
  238. 'curve': {}
  239. },
  240. mappings: {
  241. type: 'curve.type',
  242. }
  243. });