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

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

  1. /**
  2.  
  3. **Curve** is the abstract base class for {{#crossLink "SplineCurve"}}{{/crossLink}},
  4. {{#crossLink "CubicBezierCurve"}}{{/crossLink}}, {{#crossLink "QuadraticBezierCurve"}}{{/crossLink}} and {{#crossLink "Path"}}{{/crossLink}}.
  5.  
  6. ## Examples
  7.  
  8. <ul>
  9. <li>[CubicBezierCurve example](../../examples/#animation_curves_cubicBezier)</li>
  10. <li>[Tweening position along a QuadraticBezierCurve](../../examples/#animation_curves_quadraticBezier)</li>
  11. <li>[Tweening color along a QuadraticBezierCurve](../../examples/#animation_curves_quadraticBezier_color)</li>
  12. <li>[Simple SplineCurve example](../../examples/#animation_curves_spline)</li>
  13. <li>[Moving a PointLight along a SplineCurve](../../examples/#animation_lights_point_world)</li>
  14. <li>[Path example](../../examples/#curves_Path)</li>
  15. </ul>
  16.  
  17. @class Curve
  18. @module xeogl
  19. @submodule curves
  20. @constructor
  21. @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
  22. @param [cfg] {*} Configuration
  23. @param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
  24. @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Curve.
  25. @param [cfg.t=0] Current position on this Curve, in range between 0..1.
  26. @extends Component
  27. */
  28. xeogl.Curve = class xeoglCurve extends xeogl.Component {
  29.  
  30. init(cfg) {
  31. super.init(cfg);
  32. this.t = cfg.t;
  33. }
  34.  
  35. /**
  36. Progress along this Curve.
  37.  
  38. Automatically clamps to range [0..1].
  39.  
  40. Fires a {{#crossLink "Curve/t:event"}}{{/crossLink}} event on change.
  41.  
  42. @property t
  43. @default 0
  44. @type Number
  45. */
  46. set t(value) {
  47. value = value || 0;
  48. this._t = value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
  49. /**
  50. * Fired whenever this Curve's
  51. * {{#crossLink "Curve/t:property"}}{{/crossLink}} property changes.
  52. * @event t
  53. * @param value The property's new value
  54. */
  55. this.fire("t", this._t);
  56. }
  57.  
  58. get t() {
  59. return this._t;
  60. }
  61.  
  62. /**
  63. Tangent on this Curve at position {{#crossLink "Curve/t:property"}}{{/crossLink}}.
  64.  
  65. @property tangent
  66. @type {{Array of Number}}
  67. */
  68. get tangent() {
  69. return this.getTangent(this._t);
  70. }
  71.  
  72. /**
  73. Length of this Curve.
  74. @property length
  75. @type {Number}
  76. */
  77. get length() {
  78. var lengths = this._getLengths();
  79. return lengths[lengths.length - 1];
  80. }
  81.  
  82. /**
  83. * Returns a normalized tangent vector on this Curve at the given position.
  84. * @method getTangent
  85. * @param {Number} t Position to get tangent at.
  86. * @returns {{Array of Number}} Normalized tangent vector
  87. */
  88. getTangent(t) {
  89. var delta = 0.0001;
  90. if (t === undefined) {
  91. t = this._t;
  92. }
  93. var t1 = t - delta;
  94. var t2 = t + delta;
  95. if (t1 < 0) {
  96. t1 = 0;
  97. }
  98. if (t2 > 1) {
  99. t2 = 1;
  100. }
  101. var pt1 = this.getPoint(t1);
  102. var pt2 = this.getPoint(t2);
  103. var vec = xeogl.math.subVec3(pt2, pt1, []);
  104. return xeogl.math.normalizeVec3(vec, []);
  105. }
  106.  
  107. getPointAt(u) {
  108. var t = this.getUToTMapping(u);
  109. return this.getPoint(t);
  110. }
  111.  
  112. /**
  113. * Samples points on this Curve, at the given number of equally-spaced divisions.
  114. * @method getPoints
  115. * @param {Number} divisions The number of divisions.
  116. * @returns {Array of Array} Array of sampled 3D points.
  117. */
  118. getPoints(divisions) {
  119. if (!divisions) {
  120. divisions = 5;
  121. }
  122. var d, pts = [];
  123. for (d = 0; d <= divisions; d++) {
  124. pts.push(this.getPoint(d / divisions));
  125. }
  126. return pts;
  127. }
  128.  
  129. getSpacedPoints(divisions) {
  130. if (!divisions) {
  131. divisions = 5;
  132. }
  133. var d, pts = [];
  134. for (d = 0; d <= divisions; d++) {
  135. pts.push(this.getPointAt(d / divisions));
  136. }
  137. return pts;
  138. }
  139.  
  140. _getLengths(divisions) {
  141. if (!divisions) {
  142. divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions) : 200;
  143. }
  144. if (this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && !this.needsUpdate) {
  145. return this.cacheArcLengths;
  146.  
  147. }
  148. this.needsUpdate = false;
  149. var cache = [];
  150. var current;
  151. var last = this.getPoint(0);
  152. var p;
  153. var sum = 0;
  154. cache.push(0);
  155. for (p = 1; p <= divisions; p++) {
  156. current = this.getPoint(p / divisions);
  157. sum += xeogl.math.lenVec3(xeogl.math.subVec3(current, last, []));
  158. cache.push(sum);
  159. last = current;
  160. }
  161. this.cacheArcLengths = cache;
  162. return cache; // { sums: cache, sum:sum }, Sum is in the last element.
  163. }
  164.  
  165. _updateArcLengths() {
  166. this.needsUpdate = true;
  167. this._getLengths();
  168. }
  169.  
  170. // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
  171.  
  172. getUToTMapping(u, distance) {
  173. var arcLengths = this._getLengths();
  174. var i = 0;
  175. var il = arcLengths.length;
  176. var t;
  177. var targetArcLength; // The targeted u distance value to get
  178. if (distance) {
  179. targetArcLength = distance;
  180. } else {
  181. targetArcLength = u * arcLengths[il - 1];
  182. }
  183. //var time = Date.now();
  184. var low = 0, high = il - 1, comparison;
  185. while (low <= high) {
  186. i = Math.floor(low + ( high - low ) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
  187. comparison = arcLengths[i] - targetArcLength;
  188. if (comparison < 0) {
  189. low = i + 1;
  190. } else if (comparison > 0) {
  191. high = i - 1;
  192. } else {
  193. high = i;
  194. break;
  195. // DONE
  196. }
  197. }
  198. i = high;
  199. if (arcLengths[i] === targetArcLength) {
  200. t = i / ( il - 1 );
  201. return t;
  202. }
  203. var lengthBefore = arcLengths[i];
  204. var lengthAfter = arcLengths[i + 1];
  205. var segmentLength = lengthAfter - lengthBefore;
  206. var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
  207. t = ( i + segmentFraction ) / ( il - 1 );
  208. return t;
  209. }
  210. };
  211.