25b4e9bad33f39a5199a21606e0f315997b6fb2220d214afb848fa2fa3379ae65a47f01ffd4e30aca0be0490b8b6aa46f723717acc28d5c770ae27a419499a 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.bezierCurveToPolyline = bezierCurveToPolyline;
  7. exports.getBezierCurveLength = getBezierCurveLength;
  8. exports["default"] = void 0;
  9. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  10. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
  11. var sqrt = Math.sqrt,
  12. pow = Math.pow,
  13. ceil = Math.ceil,
  14. abs = Math.abs; // Initialize the number of points per curve
  15. var defaultSegmentPointsNum = 50;
  16. /**
  17. * @example data structure of bezierCurve
  18. * bezierCurve = [
  19. * // Starting point of the curve
  20. * [10, 10],
  21. * // BezierCurve segment data (controlPoint1, controlPoint2, endPoint)
  22. * [
  23. * [20, 20], [40, 20], [50, 10]
  24. * ],
  25. * ...
  26. * ]
  27. */
  28. /**
  29. * @description Abstract the curve as a polyline consisting of N points
  30. * @param {Array} bezierCurve bezierCurve data
  31. * @param {Number} precision calculation accuracy. Recommended for 1-20. Default = 5
  32. * @return {Object} Calculation results and related data
  33. * @return {Array} Option.segmentPoints Point data that constitutes a polyline after calculation
  34. * @return {Number} Option.cycles Number of iterations
  35. * @return {Number} Option.rounds The number of recursions for the last iteration
  36. */
  37. function abstractBezierCurveToPolyline(bezierCurve) {
  38. var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
  39. var segmentsNum = bezierCurve.length - 1;
  40. var startPoint = bezierCurve[0];
  41. var endPoint = bezierCurve[segmentsNum][2];
  42. var segments = bezierCurve.slice(1);
  43. var getSegmentTPointFuns = segments.map(function (seg, i) {
  44. var beginPoint = i === 0 ? startPoint : segments[i - 1][2];
  45. return createGetBezierCurveTPointFun.apply(void 0, [beginPoint].concat((0, _toConsumableArray2["default"])(seg)));
  46. }); // Initialize the curve to a polyline
  47. var segmentPointsNum = new Array(segmentsNum).fill(defaultSegmentPointsNum);
  48. var segmentPoints = getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum); // Calculate uniformly distributed points by iteratively
  49. var result = calcUniformPointsByIteration(segmentPoints, getSegmentTPointFuns, segments, precision);
  50. result.segmentPoints.push(endPoint);
  51. return result;
  52. }
  53. /**
  54. * @description Generate a method for obtaining corresponding point by t according to curve data
  55. * @param {Array} beginPoint BezierCurve begin point. [x, y]
  56. * @param {Array} controlPoint1 BezierCurve controlPoint1. [x, y]
  57. * @param {Array} controlPoint2 BezierCurve controlPoint2. [x, y]
  58. * @param {Array} endPoint BezierCurve end point. [x, y]
  59. * @return {Function} Expected function
  60. */
  61. function createGetBezierCurveTPointFun(beginPoint, controlPoint1, controlPoint2, endPoint) {
  62. return function (t) {
  63. var tSubed1 = 1 - t;
  64. var tSubed1Pow3 = pow(tSubed1, 3);
  65. var tSubed1Pow2 = pow(tSubed1, 2);
  66. var tPow3 = pow(t, 3);
  67. var tPow2 = pow(t, 2);
  68. return [beginPoint[0] * tSubed1Pow3 + 3 * controlPoint1[0] * t * tSubed1Pow2 + 3 * controlPoint2[0] * tPow2 * tSubed1 + endPoint[0] * tPow3, beginPoint[1] * tSubed1Pow3 + 3 * controlPoint1[1] * t * tSubed1Pow2 + 3 * controlPoint2[1] * tPow2 * tSubed1 + endPoint[1] * tPow3];
  69. };
  70. }
  71. /**
  72. * @description Get the distance between two points
  73. * @param {Array} point1 BezierCurve begin point. [x, y]
  74. * @param {Array} point2 BezierCurve controlPoint1. [x, y]
  75. * @return {Number} Expected distance
  76. */
  77. function getTwoPointDistance(_ref, _ref2) {
  78. var _ref3 = (0, _slicedToArray2["default"])(_ref, 2),
  79. ax = _ref3[0],
  80. ay = _ref3[1];
  81. var _ref4 = (0, _slicedToArray2["default"])(_ref2, 2),
  82. bx = _ref4[0],
  83. by = _ref4[1];
  84. return sqrt(pow(ax - bx, 2) + pow(ay - by, 2));
  85. }
  86. /**
  87. * @description Get the sum of the array of numbers
  88. * @param {Array} nums An array of numbers
  89. * @return {Number} Expected sum
  90. */
  91. function getNumsSum(nums) {
  92. return nums.reduce(function (sum, num) {
  93. return sum + num;
  94. }, 0);
  95. }
  96. /**
  97. * @description Get the distance of multiple sets of points
  98. * @param {Array} segmentPoints Multiple sets of point data
  99. * @return {Array} Distance of multiple sets of point data
  100. */
  101. function getSegmentPointsDistance(segmentPoints) {
  102. return segmentPoints.map(function (points, i) {
  103. return new Array(points.length - 1).fill(0).map(function (temp, j) {
  104. return getTwoPointDistance(points[j], points[j + 1]);
  105. });
  106. });
  107. }
  108. /**
  109. * @description Get the distance of multiple sets of points
  110. * @param {Array} segmentPoints Multiple sets of point data
  111. * @return {Array} Distance of multiple sets of point data
  112. */
  113. function getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum) {
  114. return getSegmentTPointFuns.map(function (getSegmentTPointFun, i) {
  115. var tGap = 1 / segmentPointsNum[i];
  116. return new Array(segmentPointsNum[i]).fill('').map(function (foo, j) {
  117. return getSegmentTPointFun(j * tGap);
  118. });
  119. });
  120. }
  121. /**
  122. * @description Get the sum of deviations between line segment and the average length
  123. * @param {Array} segmentPointsDistance Segment length of polyline
  124. * @param {Number} avgLength Average length of the line segment
  125. * @return {Number} Deviations
  126. */
  127. function getAllDeviations(segmentPointsDistance, avgLength) {
  128. return segmentPointsDistance.map(function (seg) {
  129. return seg.map(function (s) {
  130. return abs(s - avgLength);
  131. });
  132. }).map(function (seg) {
  133. return getNumsSum(seg);
  134. }).reduce(function (total, v) {
  135. return total + v;
  136. }, 0);
  137. }
  138. /**
  139. * @description Calculate uniformly distributed points by iteratively
  140. * @param {Array} segmentPoints Multiple setd of points that make up a polyline
  141. * @param {Array} getSegmentTPointFuns Functions of get a point on the curve with t
  142. * @param {Array} segments BezierCurve data
  143. * @param {Number} precision Calculation accuracy
  144. * @return {Object} Calculation results and related data
  145. * @return {Array} Option.segmentPoints Point data that constitutes a polyline after calculation
  146. * @return {Number} Option.cycles Number of iterations
  147. * @return {Number} Option.rounds The number of recursions for the last iteration
  148. */
  149. function calcUniformPointsByIteration(segmentPoints, getSegmentTPointFuns, segments, precision) {
  150. // The number of loops for the current iteration
  151. var rounds = 4; // Number of iterations
  152. var cycles = 1;
  153. var _loop = function _loop() {
  154. // Recalculate the number of points per curve based on the last iteration data
  155. var totalPointsNum = segmentPoints.reduce(function (total, seg) {
  156. return total + seg.length;
  157. }, 0); // Add last points of segment to calc exact segment length
  158. segmentPoints.forEach(function (seg, i) {
  159. return seg.push(segments[i][2]);
  160. });
  161. var segmentPointsDistance = getSegmentPointsDistance(segmentPoints);
  162. var lineSegmentNum = segmentPointsDistance.reduce(function (total, seg) {
  163. return total + seg.length;
  164. }, 0);
  165. var segmentlength = segmentPointsDistance.map(function (seg) {
  166. return getNumsSum(seg);
  167. });
  168. var totalLength = getNumsSum(segmentlength);
  169. var avgLength = totalLength / lineSegmentNum; // Check if precision is reached
  170. var allDeviations = getAllDeviations(segmentPointsDistance, avgLength);
  171. if (allDeviations <= precision) return "break";
  172. totalPointsNum = ceil(avgLength / precision * totalPointsNum * 1.1);
  173. var segmentPointsNum = segmentlength.map(function (length) {
  174. return ceil(length / totalLength * totalPointsNum);
  175. }); // Calculate the points after redistribution
  176. segmentPoints = getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum);
  177. totalPointsNum = segmentPoints.reduce(function (total, seg) {
  178. return total + seg.length;
  179. }, 0);
  180. var segmentPointsForLength = JSON.parse(JSON.stringify(segmentPoints));
  181. segmentPointsForLength.forEach(function (seg, i) {
  182. return seg.push(segments[i][2]);
  183. });
  184. segmentPointsDistance = getSegmentPointsDistance(segmentPointsForLength);
  185. lineSegmentNum = segmentPointsDistance.reduce(function (total, seg) {
  186. return total + seg.length;
  187. }, 0);
  188. segmentlength = segmentPointsDistance.map(function (seg) {
  189. return getNumsSum(seg);
  190. });
  191. totalLength = getNumsSum(segmentlength);
  192. avgLength = totalLength / lineSegmentNum;
  193. var stepSize = 1 / totalPointsNum / 10; // Recursively for each segment of the polyline
  194. getSegmentTPointFuns.forEach(function (getSegmentTPointFun, i) {
  195. var currentSegmentPointsNum = segmentPointsNum[i];
  196. var t = new Array(currentSegmentPointsNum).fill('').map(function (foo, j) {
  197. return j / segmentPointsNum[i];
  198. }); // Repeated recursive offset
  199. for (var r = 0; r < rounds; r++) {
  200. var distance = getSegmentPointsDistance([segmentPoints[i]])[0];
  201. var deviations = distance.map(function (d) {
  202. return d - avgLength;
  203. });
  204. var offset = 0;
  205. for (var j = 0; j < currentSegmentPointsNum; j++) {
  206. if (j === 0) return;
  207. offset += deviations[j - 1];
  208. t[j] -= stepSize * offset;
  209. if (t[j] > 1) t[j] = 1;
  210. if (t[j] < 0) t[j] = 0;
  211. segmentPoints[i][j] = getSegmentTPointFun(t[j]);
  212. }
  213. }
  214. });
  215. rounds *= 4;
  216. cycles++;
  217. };
  218. do {
  219. var _ret = _loop();
  220. if (_ret === "break") break;
  221. } while (rounds <= 1025);
  222. segmentPoints = segmentPoints.reduce(function (all, seg) {
  223. return all.concat(seg);
  224. }, []);
  225. return {
  226. segmentPoints: segmentPoints,
  227. cycles: cycles,
  228. rounds: rounds
  229. };
  230. }
  231. /**
  232. * @description Get the polyline corresponding to the Bezier curve
  233. * @param {Array} bezierCurve BezierCurve data
  234. * @param {Number} precision Calculation accuracy. Recommended for 1-20. Default = 5
  235. * @return {Array|Boolean} Point data that constitutes a polyline after calculation (Invalid input will return false)
  236. */
  237. function bezierCurveToPolyline(bezierCurve) {
  238. var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
  239. if (!bezierCurve) {
  240. console.error('bezierCurveToPolyline: Missing parameters!');
  241. return false;
  242. }
  243. if (!(bezierCurve instanceof Array)) {
  244. console.error('bezierCurveToPolyline: Parameter bezierCurve must be an array!');
  245. return false;
  246. }
  247. if (typeof precision !== 'number') {
  248. console.error('bezierCurveToPolyline: Parameter precision must be a number!');
  249. return false;
  250. }
  251. var _abstractBezierCurveT = abstractBezierCurveToPolyline(bezierCurve, precision),
  252. segmentPoints = _abstractBezierCurveT.segmentPoints;
  253. return segmentPoints;
  254. }
  255. /**
  256. * @description Get the bezier curve length
  257. * @param {Array} bezierCurve bezierCurve data
  258. * @param {Number} precision calculation accuracy. Recommended for 5-10. Default = 5
  259. * @return {Number|Boolean} BezierCurve length (Invalid input will return false)
  260. */
  261. function getBezierCurveLength(bezierCurve) {
  262. var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
  263. if (!bezierCurve) {
  264. console.error('getBezierCurveLength: Missing parameters!');
  265. return false;
  266. }
  267. if (!(bezierCurve instanceof Array)) {
  268. console.error('getBezierCurveLength: Parameter bezierCurve must be an array!');
  269. return false;
  270. }
  271. if (typeof precision !== 'number') {
  272. console.error('getBezierCurveLength: Parameter precision must be a number!');
  273. return false;
  274. }
  275. var _abstractBezierCurveT2 = abstractBezierCurveToPolyline(bezierCurve, precision),
  276. segmentPoints = _abstractBezierCurveT2.segmentPoints; // Calculate the total length of the points that make up the polyline
  277. var pointsDistance = getSegmentPointsDistance([segmentPoints])[0];
  278. var length = getNumsSum(pointsDistance);
  279. return length;
  280. }
  281. var _default = bezierCurveToPolyline;
  282. exports["default"] = _default;