/home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/curves/path.js
API Docs for:

File: /home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/curves/path.js

  1. /**
  2. A **Path** is a complex curved path constructed from various {{#crossLink "Curve"}}{{/crossLink}} subtypes.
  3.  
  4. ## Overview
  5.  
  6.  
  7. * A Path can be constructed from these {{#crossLink "Curve"}}{{/crossLink}} subtypes: {{#crossLink "SplineCurve"}}{{/crossLink}},
  8. {{#crossLink "CubicBezierCurve"}}{{/crossLink}} and {{#crossLink "QuadraticBezierCurve"}}{{/crossLink}}.
  9. * You can sample a {{#crossLink "Path/point:property"}}{{/crossLink}} and a {{#crossLink "Curve/tangent:property"}}{{/crossLink}}
  10. vector on a Path for any given value of {{#crossLink "Path/t:property"}}{{/crossLink}} in the range [0..1].
  11. * When you set {{#crossLink "Path/t:property"}}{{/crossLink}} on a Path, its
  12. {{#crossLink "Path/point:property"}}{{/crossLink}} and {{#crossLink "Curve/tangent:property"}}{{/crossLink}} properties
  13. will update accordingly.
  14.  
  15. ## Examples
  16.  
  17. * [CubicBezierCurve example](../../examples/#animation_curves_cubicBezier)
  18. * [Tweening position along a QuadraticBezierCurve](../../examples/#animation_curves_quadraticBezier)
  19. * [Tweening color along a QuadraticBezierCurve](../../examples/#animation_curves_quadraticBezier_color)
  20. * [SplineCurve example](../../examples/#animation_curves_spline)
  21. * [Path example](../../examples/#curves_Path)
  22.  
  23. ## Usage
  24.  
  25. #### Animation along a SplineCurve
  26.  
  27. Create a Path containing a {{#crossLink "CubicBezierCurve"}}{{/crossLink}}, a {{#crossLink "QuadraticBezierCurve"}}{{/crossLink}}
  28. and a {{#crossLink "SplineCurve"}}{{/crossLink}}, subscribe to updates on its {{#crossLink "Path/point:property"}}{{/crossLink}} and
  29. {{#crossLink "Curve/tangent:property"}}{{/crossLink}} properties, then vary its {{#crossLink "Path/t:property"}}{{/crossLink}}
  30. property over time:
  31.  
  32. ````javascript
  33. var path = new xeogl.Path({
  34. curves: [
  35. new xeogl.CubicBezierCurve({
  36. v0: [-10, 0, 0],
  37. v1: [-5, 15, 0],
  38. v2: [20, 15, 0],
  39. v3: [10, 0, 0]
  40. }),
  41. new xeogl.QuadraticBezierCurve({
  42. v0: [10, 0, 0],
  43. v1: [20, 15, 0],
  44. v2: [10, 0, 0]
  45. }),
  46. new xeogl.SplineCurve({
  47. points: [
  48. [10, 0, 0],
  49. [-5, 15, 0],
  50. [20, 15, 0],
  51. [10, 0, 0]
  52. ]
  53. })
  54. ]
  55. });
  56.  
  57. path.on("point", function(point) {
  58. this.log("path.point=" + JSON.stringify(point));
  59. });
  60.  
  61. path.on("tangent", function(tangent) {
  62. this.log("path.tangent=" + JSON.stringify(tangent));
  63. });
  64.  
  65. path.on("t", function(t) {
  66. this.log("path.t=" + t);
  67. });
  68.  
  69. path.scene.on("tick", function(e) {
  70. path.t = (e.time - e.startTime) * 0.01;
  71. });
  72. ````
  73.  
  74. #### Randomly sampling points
  75.  
  76. Use Path's {{#crossLink "Path/getPoint:method"}}{{/crossLink}} and
  77. {{#crossLink "path/getTangent:method"}}{{/crossLink}} methods to sample the point and vector
  78. at a given **t**:
  79.  
  80. ````javascript
  81. path.scene.on("tick", function(e) {
  82.  
  83. var t = (e.time - e.startTime) * 0.01;
  84.  
  85. var point = path.getPoint(t);
  86. var tangent = path.getTangent(t);
  87.  
  88. this.log("t=" + t + ", point=" + JSON.stringify(point) + ", tangent=" + JSON.stringify(tangent));
  89. });
  90. ````
  91.  
  92. #### Sampling multiple points
  93.  
  94. Use Path's {{#crossLink "path/getPoints:method"}}{{/crossLink}} method to sample a list of equidistant points
  95. along it. In the snippet below, we'll build a {{#crossLink "Geometry"}}{{/crossLink}} that renders a line along the
  96. path. Note that we need to flatten the points array for consumption by the {{#crossLink "Geometry"}}{{/crossLink}}.
  97.  
  98. ````javascript
  99. var geometry = new xeogl.Geometry({
  100. positions: xeogl.math.flatten(path.getPoints(50))
  101. });
  102. ````
  103.  
  104. @class Path
  105. @module xeogl
  106. @submodule curves
  107. @constructor
  108. @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
  109. @param [cfg] {*} Fly configuration
  110. @param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
  111. @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Path.
  112. @param [cfg.paths=[]] IDs or instances of {{#crossLink "path"}}{{/crossLink}} subtypes to add to this Path.
  113. @param [cfg.t=0] Current position on this Path, in range between 0..1.
  114. @extends path
  115. */
  116.  
  117. xeogl.Path = class xeoglPath extends xeogl.Curve {
  118.  
  119. init(cfg) {
  120. super.init(cfg);
  121. this._cachedLengths = [];
  122. this._dirty = true;
  123. this._curves = []; // Array of child Curve components
  124. this._t = 0;
  125. this._dirtySubs = []; // Subscriptions to "dirty" events from child Curve components
  126. this._destroyedSubs = []; // Subscriptions to "destroyed" events from child Curve components
  127. this.curves = cfg.curves || []; // Add initial curves
  128. this.t = cfg.t; // Set initial progress
  129. }
  130.  
  131. /**
  132. * Adds a {{#crossLink "Curve"}}{{/crossLink}} to this Path.
  133. *
  134. * Fires a {{#crossLink "Path/curves:event"}}{{/crossLink}} event on change.
  135. *
  136. * @param {Curve} curve The {{#crossLink "Curve"}}{{/crossLink}} to add.
  137. */
  138. addCurve(curve) {
  139. this._curves.push(curve);
  140. this._dirty = true;
  141. /**
  142. * Fired whenever this Path's
  143. * {{#crossLink "Path/curves:property"}}{{/crossLink}} property changes.
  144. * @event curves
  145. * @param value The property's new value
  146. */
  147. this.fire("curves", this._curves);
  148. }
  149.  
  150. /**
  151. The {{#crossLink "Curve"}}Curves{{/crossLink}} in this Path.
  152.  
  153. Fires a {{#crossLink "Path/curves:event"}}{{/crossLink}} event on change.
  154.  
  155. @property curves
  156. @default []
  157. @type {{Array of Spline, Path, QuadraticBezierCurve or CubicBezierCurve}}
  158. */
  159. set curves(value) {
  160.  
  161. value = value || [];
  162.  
  163. var curve;
  164. // Unsubscribe from events on old curves
  165. var i;
  166. var len;
  167. for (i = 0, len = this._curves.length; i < len; i++) {
  168. curve = this._curves[i];
  169. curve.off(this._dirtySubs[i]);
  170. curve.off(this._destroyedSubs[i]);
  171. }
  172.  
  173. this._curves = [];
  174. this._dirtySubs = [];
  175. this._destroyedSubs = [];
  176.  
  177. var self = this;
  178.  
  179. function curveDirty() {
  180. self._dirty = true;
  181. }
  182.  
  183. function curveDestroyed() {
  184. var id = this.id;
  185. for (i = 0, len = self._curves.length; i < len; i++) {
  186. if (self._curves[i].id === id) {
  187. self._curves = self._curves.slice(i, i + 1);
  188. self._dirtySubs = self._dirtySubs.slice(i, i + 1);
  189. self._destroyedSubs = self._destroyedSubs.slice(i, i + 1);
  190. self._dirty = true;
  191. self.fire("curves", self._curves);
  192. return;
  193. }
  194. }
  195. }
  196.  
  197. for (i = 0, len = value.length; i < len; i++) {
  198. curve = value[i];
  199. if (xeogl._isNumeric(curve) || xeogl._isString(curve)) {
  200. // ID given for curve - find the curve component
  201. var id = curve;
  202. curve = this.scene.components[id];
  203. if (!curve) {
  204. this.error("Component not found: " + xeogl._inQuotes(id));
  205. continue;
  206. }
  207. }
  208.  
  209. var type = curve.type;
  210.  
  211. if (type !== "xeogl.SplineCurve" &&
  212. type !== "xeogl.Path" &&
  213. type !== "xeogl.CubicBezierCurve" &&
  214. type !== "xeogl.QuadraticBezierCurve") {
  215.  
  216. this.error("Component " + xeogl._inQuotes(curve.id)
  217. + " is not a xeogl.SplineCurve, xeogl.Path or xeogl.QuadraticBezierCurve");
  218.  
  219. continue;
  220. }
  221.  
  222. this._curves.push(curve);
  223. this._dirtySubs.push(curve.on("dirty", curveDirty));
  224. this._destroyedSubs.push(curve.on("destroyed", curveDestroyed));
  225. }
  226.  
  227. this._dirty = true;
  228.  
  229. this.fire("curves", this._curves);
  230. }
  231.  
  232. get curves() {
  233. return this._curves;
  234. }
  235.  
  236. /**
  237. Current point of progress along this Path.
  238.  
  239. Automatically clamps to range [0..1].
  240.  
  241. Fires a {{#crossLink "Path/t:event"}}{{/crossLink}} event on change.
  242.  
  243. @property t
  244. @default 0
  245. @type Number
  246. */
  247. set t(value) {
  248. value = value || 0;
  249. this._t = value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
  250. /**
  251. * Fired whenever this Path's
  252. * {{#crossLink "Path/t:property"}}{{/crossLink}} property changes.
  253. * @event t
  254. * @param value The property's new value
  255. */
  256. this.fire("t", this._t);
  257. }
  258.  
  259. get t() {
  260. return this._t;
  261. }
  262.  
  263. /**
  264. Point on this Path corresponding to the current value of {{#crossLink "Path/t:property"}}{{/crossLink}}.
  265.  
  266. @property point
  267. @type {{Array of Number}}
  268. */
  269. get point() {
  270. return this.getPoint(this._t);
  271. }
  272.  
  273. /**
  274. Length of this Path, which is the cumulative length of all {{#crossLink "Curve/t:property"}}Curves{{/crossLink}}
  275. currently in {{#crossLink "Path/curves:property"}}{{/crossLink}}.
  276.  
  277. @property length
  278. @type {Number}
  279. */
  280. get length() {
  281. var lens = this._getCurveLengths();
  282. return lens[lens.length - 1];
  283. }
  284.  
  285. /**
  286. * Gets a point on this Path corresponding to the given progress position.
  287. * @param {Number} t Indicates point of progress along this curve, in the range [0..1].
  288. * @returns {{Array of Number}}
  289. */
  290. getPoint(t) {
  291. var d = t * this.length;
  292. var curveLengths = this._getCurveLengths();
  293. var i = 0, diff, curve;
  294. while (i < curveLengths.length) {
  295. if (curveLengths[i] >= d) {
  296. diff = curveLengths[i] - d;
  297. curve = this._curves[i];
  298. var u = 1 - diff / curve.length;
  299. return curve.getPointAt(u);
  300. }
  301. i++;
  302. }
  303. return null;
  304. }
  305.  
  306. _getCurveLengths() {
  307. if (!this._dirty) {
  308. return this._cachedLengths;
  309. }
  310. var lengths = [];
  311. var sums = 0;
  312. var i, il = this._curves.length;
  313. for (i = 0; i < il; i++) {
  314. sums += this._curves[i].length;
  315. lengths.push(sums);
  316.  
  317. }
  318. this._cachedLengths = lengths;
  319. this._dirty = false;
  320. return lengths;
  321. }
  322.  
  323. _getJSON() {
  324. var curveIds = [];
  325. for (var i = 0, len = this._curves.length; i < len; i++) {
  326. curveIds.push(this._curves[i].id);
  327. }
  328. return {
  329. curves: curveIds,
  330. t: this._t
  331. };
  332. }
  333.  
  334. destroy() {
  335. super.destroy();
  336. var i;
  337. var len;
  338. var curve;
  339. for (i = 0, len = this._curves.length; i < len; i++) {
  340. curve = this._curves[i];
  341. curve.off(this._dirtySubs[i]);
  342. curve.off(this._destroyedSubs[i]);
  343. }
  344. }
  345. };
  346.  
  347.