aaaaaa123456789

JS version of multi-line time step function

Apr 15th, 2021 (edited)
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // points: array of {x, y}
  2. // position: relative position within the line, between 0 and 1
  3. // speeding_distance: distance required to go from no speed to max and vice-versa
  4. function compute_point (points, position, speeding_distance) {
  5.   "use strict";
  6.   let count = points.length;
  7.   if (!count) return {x: 0, y: 0};
  8.   if (!-- count) return points[0];
  9.   if (position <= 0) return points[0];
  10.   if (position >= 1) return points[count];
  11.   if (speeding_distance < 0.01) speeding_distance = 0.01;
  12.   let acceleration = 4 / speeding_distance;
  13.   let p, segment_length = [];
  14.   for (p = 0; p < count; p ++)
  15.     segment_length[p] = Math.sqrt((points[p + 1].x - points[p].x) * (points[p + 1].x - points[p].x) +
  16.                                   (points[p + 1].y - points[p].y) * (points[p + 1].y - points[p].y));
  17.   let corner_speed = [0];
  18.   corner_speed[count] = 0;
  19.   // speed at each corner: 1 - cos(angle). Easy to calculate using a dot product, without any actual trigonometry.
  20.   for (p = 1; p < count; p ++)
  21.     corner_speed[p] = 1 - ((points[p + 1].x - points[p].x) * (points[p - 1].x - points[p].x) +
  22.                            (points[p + 1].y - points[p].y) * (points[p - 1].y - points[p].y)) /
  23.                           (segment_length[p] * segment_length[p - 1]);
  24.   let segment_time = [], segment_type = [], total_time = 0;
  25.   let initial, final, peak, time, length; // work with local variables for simplicity
  26.   for (p = 0; p < count; p ++) {
  27.     initial = corner_speed[p];
  28.     final = corner_speed[p + 1];
  29.     length = segment_length[p];
  30.     peak = Math.sqrt((initial * initial + final * final) / 2 + length * acceleration);
  31.     if (peak >= 2) {
  32.       // speed capped at 2; the peak value is just a calculation aid now
  33.       segment_type[p] = 0;
  34.       time = (peak * peak - 2 * (initial + final) + 4) / (acceleration * 2);
  35.     } else if ((peak >= initial) && (peak >= final)) {
  36.       segment_type[p] = 1;
  37.       time = (peak * 2 - initial - final) / acceleration;
  38.     } else if (initial <= final) {
  39.       // not enough distance to reach the final speed, so we lower it
  40.       segment_type[p] = 2;
  41.       final = Math.sqrt(initial * initial + 2 * acceleration * length);
  42.       corner_speed[p + 1] = final;
  43.       time = length * 2 / (initial + final);
  44.     } else {
  45.       // not enough distance to stop this time, but we must stop faster or we'll overrun the segment
  46.       segment_type[p] = 3;
  47.       time = length * 2 / (initial + final); // same as above
  48.     }
  49.     segment_time[p] = time;
  50.     total_time += time;
  51.   }
  52.   position *= total_time;
  53.   for (p = 0; (p < count) && (segment_time[p] <= position); position -= segment_time[p ++]);
  54.   let type = 4;
  55.   if (p < count) {
  56.     initial = corner_speed[p];
  57.     final = corner_speed[p + 1];
  58.     time = segment_time[p];
  59.     length = segment_length[p];
  60.     type = segment_type[p];
  61.     position /= time;
  62.   }
  63.   let first_split, second_split;
  64.   switch (type) {
  65.     case 0:
  66.       // two splits: when peak speed is reached and when slowdown begins
  67.       first_split = (2 - initial) / acceleration;
  68.       second_split = time - (2 - final) / acceleration;
  69.       break;
  70.     case 1:
  71.       // only one split
  72.       first_split = ((final - initial) / acceleration + time) / 2;
  73.       second_split = first_split;
  74.       break;
  75.     case 2:
  76.       // no splits (the split would come after the end point); assign an arbitrary large value
  77.       first_split = time * 16;
  78.       second_split = first_split;
  79.       break;
  80.     case 3:
  81.       // uses a different formula, so compute the position directly (and avoid recomputing later)
  82.       position *= (2 * initial + position * (final - initial)) / (initial + final);
  83.       break;
  84.     default:
  85.       // only possible if position >= 1 due to rounding errors; just stay at the line's end point
  86.       return points[count];
  87.   }
  88.   if (type < 3)
  89.     if (position <= (first_split / time))
  90.       position *= time * (initial + time * position * acceleration / 2) / length;
  91.     else if (position >= (second_split / time))
  92.       position = 1 + (position - 1) * time * (final - time * acceleration * (position - 1) / 2) / length;
  93.     else
  94.       position = (2 * (time * position - first_split) + (4 - initial * initial) / (2 * acceleration)) / length;
  95.   return {
  96.     x: Math.round(points[p].x * (1 - position) + points[p + 1].x * position),
  97.     y: Math.round(points[p].y * (1 - position) + points[p + 1].y * position)
  98.   };
  99. }
Add Comment
Please, Sign In to add comment