curve.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. var _vector = require("./vector");
  2. var v2Create = _vector.create;
  3. var v2DistSquare = _vector.distSquare;
  4. /**
  5. * 曲线辅助模块
  6. * @module zrender/core/curve
  7. * @author pissang(https://www.github.com/pissang)
  8. */
  9. var mathPow = Math.pow;
  10. var mathSqrt = Math.sqrt;
  11. var EPSILON = 1e-8;
  12. var EPSILON_NUMERIC = 1e-4;
  13. var THREE_SQRT = mathSqrt(3);
  14. var ONE_THIRD = 1 / 3; // 临时变量
  15. var _v0 = v2Create();
  16. var _v1 = v2Create();
  17. var _v2 = v2Create();
  18. function isAroundZero(val) {
  19. return val > -EPSILON && val < EPSILON;
  20. }
  21. function isNotAroundZero(val) {
  22. return val > EPSILON || val < -EPSILON;
  23. }
  24. /**
  25. * 计算三次贝塞尔值
  26. * @memberOf module:zrender/core/curve
  27. * @param {number} p0
  28. * @param {number} p1
  29. * @param {number} p2
  30. * @param {number} p3
  31. * @param {number} t
  32. * @return {number}
  33. */
  34. function cubicAt(p0, p1, p2, p3, t) {
  35. var onet = 1 - t;
  36. return onet * onet * (onet * p0 + 3 * t * p1) + t * t * (t * p3 + 3 * onet * p2);
  37. }
  38. /**
  39. * 计算三次贝塞尔导数值
  40. * @memberOf module:zrender/core/curve
  41. * @param {number} p0
  42. * @param {number} p1
  43. * @param {number} p2
  44. * @param {number} p3
  45. * @param {number} t
  46. * @return {number}
  47. */
  48. function cubicDerivativeAt(p0, p1, p2, p3, t) {
  49. var onet = 1 - t;
  50. return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet + (p3 - p2) * t * t);
  51. }
  52. /**
  53. * 计算三次贝塞尔方程根,使用盛金公式
  54. * @memberOf module:zrender/core/curve
  55. * @param {number} p0
  56. * @param {number} p1
  57. * @param {number} p2
  58. * @param {number} p3
  59. * @param {number} val
  60. * @param {Array.<number>} roots
  61. * @return {number} 有效根数目
  62. */
  63. function cubicRootAt(p0, p1, p2, p3, val, roots) {
  64. // Evaluate roots of cubic functions
  65. var a = p3 + 3 * (p1 - p2) - p0;
  66. var b = 3 * (p2 - p1 * 2 + p0);
  67. var c = 3 * (p1 - p0);
  68. var d = p0 - val;
  69. var A = b * b - 3 * a * c;
  70. var B = b * c - 9 * a * d;
  71. var C = c * c - 3 * b * d;
  72. var n = 0;
  73. if (isAroundZero(A) && isAroundZero(B)) {
  74. if (isAroundZero(b)) {
  75. roots[0] = 0;
  76. } else {
  77. var t1 = -c / b; //t1, t2, t3, b is not zero
  78. if (t1 >= 0 && t1 <= 1) {
  79. roots[n++] = t1;
  80. }
  81. }
  82. } else {
  83. var disc = B * B - 4 * A * C;
  84. if (isAroundZero(disc)) {
  85. var K = B / A;
  86. var t1 = -b / a + K; // t1, a is not zero
  87. var t2 = -K / 2; // t2, t3
  88. if (t1 >= 0 && t1 <= 1) {
  89. roots[n++] = t1;
  90. }
  91. if (t2 >= 0 && t2 <= 1) {
  92. roots[n++] = t2;
  93. }
  94. } else if (disc > 0) {
  95. var discSqrt = mathSqrt(disc);
  96. var Y1 = A * b + 1.5 * a * (-B + discSqrt);
  97. var Y2 = A * b + 1.5 * a * (-B - discSqrt);
  98. if (Y1 < 0) {
  99. Y1 = -mathPow(-Y1, ONE_THIRD);
  100. } else {
  101. Y1 = mathPow(Y1, ONE_THIRD);
  102. }
  103. if (Y2 < 0) {
  104. Y2 = -mathPow(-Y2, ONE_THIRD);
  105. } else {
  106. Y2 = mathPow(Y2, ONE_THIRD);
  107. }
  108. var t1 = (-b - (Y1 + Y2)) / (3 * a);
  109. if (t1 >= 0 && t1 <= 1) {
  110. roots[n++] = t1;
  111. }
  112. } else {
  113. var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));
  114. var theta = Math.acos(T) / 3;
  115. var ASqrt = mathSqrt(A);
  116. var tmp = Math.cos(theta);
  117. var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
  118. var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
  119. var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
  120. if (t1 >= 0 && t1 <= 1) {
  121. roots[n++] = t1;
  122. }
  123. if (t2 >= 0 && t2 <= 1) {
  124. roots[n++] = t2;
  125. }
  126. if (t3 >= 0 && t3 <= 1) {
  127. roots[n++] = t3;
  128. }
  129. }
  130. }
  131. return n;
  132. }
  133. /**
  134. * 计算三次贝塞尔方程极限值的位置
  135. * @memberOf module:zrender/core/curve
  136. * @param {number} p0
  137. * @param {number} p1
  138. * @param {number} p2
  139. * @param {number} p3
  140. * @param {Array.<number>} extrema
  141. * @return {number} 有效数目
  142. */
  143. function cubicExtrema(p0, p1, p2, p3, extrema) {
  144. var b = 6 * p2 - 12 * p1 + 6 * p0;
  145. var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
  146. var c = 3 * p1 - 3 * p0;
  147. var n = 0;
  148. if (isAroundZero(a)) {
  149. if (isNotAroundZero(b)) {
  150. var t1 = -c / b;
  151. if (t1 >= 0 && t1 <= 1) {
  152. extrema[n++] = t1;
  153. }
  154. }
  155. } else {
  156. var disc = b * b - 4 * a * c;
  157. if (isAroundZero(disc)) {
  158. extrema[0] = -b / (2 * a);
  159. } else if (disc > 0) {
  160. var discSqrt = mathSqrt(disc);
  161. var t1 = (-b + discSqrt) / (2 * a);
  162. var t2 = (-b - discSqrt) / (2 * a);
  163. if (t1 >= 0 && t1 <= 1) {
  164. extrema[n++] = t1;
  165. }
  166. if (t2 >= 0 && t2 <= 1) {
  167. extrema[n++] = t2;
  168. }
  169. }
  170. }
  171. return n;
  172. }
  173. /**
  174. * 细分三次贝塞尔曲线
  175. * @memberOf module:zrender/core/curve
  176. * @param {number} p0
  177. * @param {number} p1
  178. * @param {number} p2
  179. * @param {number} p3
  180. * @param {number} t
  181. * @param {Array.<number>} out
  182. */
  183. function cubicSubdivide(p0, p1, p2, p3, t, out) {
  184. var p01 = (p1 - p0) * t + p0;
  185. var p12 = (p2 - p1) * t + p1;
  186. var p23 = (p3 - p2) * t + p2;
  187. var p012 = (p12 - p01) * t + p01;
  188. var p123 = (p23 - p12) * t + p12;
  189. var p0123 = (p123 - p012) * t + p012; // Seg0
  190. out[0] = p0;
  191. out[1] = p01;
  192. out[2] = p012;
  193. out[3] = p0123; // Seg1
  194. out[4] = p0123;
  195. out[5] = p123;
  196. out[6] = p23;
  197. out[7] = p3;
  198. }
  199. /**
  200. * 投射点到三次贝塞尔曲线上,返回投射距离。
  201. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  202. * @param {number} x0
  203. * @param {number} y0
  204. * @param {number} x1
  205. * @param {number} y1
  206. * @param {number} x2
  207. * @param {number} y2
  208. * @param {number} x3
  209. * @param {number} y3
  210. * @param {number} x
  211. * @param {number} y
  212. * @param {Array.<number>} [out] 投射点
  213. * @return {number}
  214. */
  215. function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {
  216. // http://pomax.github.io/bezierinfo/#projections
  217. var t;
  218. var interval = 0.005;
  219. var d = Infinity;
  220. var prev;
  221. var next;
  222. var d1;
  223. var d2;
  224. _v0[0] = x;
  225. _v0[1] = y; // 先粗略估计一下可能的最小距离的 t 值
  226. // PENDING
  227. for (var _t = 0; _t < 1; _t += 0.05) {
  228. _v1[0] = cubicAt(x0, x1, x2, x3, _t);
  229. _v1[1] = cubicAt(y0, y1, y2, y3, _t);
  230. d1 = v2DistSquare(_v0, _v1);
  231. if (d1 < d) {
  232. t = _t;
  233. d = d1;
  234. }
  235. }
  236. d = Infinity; // At most 32 iteration
  237. for (var i = 0; i < 32; i++) {
  238. if (interval < EPSILON_NUMERIC) {
  239. break;
  240. }
  241. prev = t - interval;
  242. next = t + interval; // t - interval
  243. _v1[0] = cubicAt(x0, x1, x2, x3, prev);
  244. _v1[1] = cubicAt(y0, y1, y2, y3, prev);
  245. d1 = v2DistSquare(_v1, _v0);
  246. if (prev >= 0 && d1 < d) {
  247. t = prev;
  248. d = d1;
  249. } else {
  250. // t + interval
  251. _v2[0] = cubicAt(x0, x1, x2, x3, next);
  252. _v2[1] = cubicAt(y0, y1, y2, y3, next);
  253. d2 = v2DistSquare(_v2, _v0);
  254. if (next <= 1 && d2 < d) {
  255. t = next;
  256. d = d2;
  257. } else {
  258. interval *= 0.5;
  259. }
  260. }
  261. } // t
  262. if (out) {
  263. out[0] = cubicAt(x0, x1, x2, x3, t);
  264. out[1] = cubicAt(y0, y1, y2, y3, t);
  265. } // console.log(interval, i);
  266. return mathSqrt(d);
  267. }
  268. /**
  269. * 计算二次方贝塞尔值
  270. * @param {number} p0
  271. * @param {number} p1
  272. * @param {number} p2
  273. * @param {number} t
  274. * @return {number}
  275. */
  276. function quadraticAt(p0, p1, p2, t) {
  277. var onet = 1 - t;
  278. return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
  279. }
  280. /**
  281. * 计算二次方贝塞尔导数值
  282. * @param {number} p0
  283. * @param {number} p1
  284. * @param {number} p2
  285. * @param {number} t
  286. * @return {number}
  287. */
  288. function quadraticDerivativeAt(p0, p1, p2, t) {
  289. return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
  290. }
  291. /**
  292. * 计算二次方贝塞尔方程根
  293. * @param {number} p0
  294. * @param {number} p1
  295. * @param {number} p2
  296. * @param {number} t
  297. * @param {Array.<number>} roots
  298. * @return {number} 有效根数目
  299. */
  300. function quadraticRootAt(p0, p1, p2, val, roots) {
  301. var a = p0 - 2 * p1 + p2;
  302. var b = 2 * (p1 - p0);
  303. var c = p0 - val;
  304. var n = 0;
  305. if (isAroundZero(a)) {
  306. if (isNotAroundZero(b)) {
  307. var t1 = -c / b;
  308. if (t1 >= 0 && t1 <= 1) {
  309. roots[n++] = t1;
  310. }
  311. }
  312. } else {
  313. var disc = b * b - 4 * a * c;
  314. if (isAroundZero(disc)) {
  315. var t1 = -b / (2 * a);
  316. if (t1 >= 0 && t1 <= 1) {
  317. roots[n++] = t1;
  318. }
  319. } else if (disc > 0) {
  320. var discSqrt = mathSqrt(disc);
  321. var t1 = (-b + discSqrt) / (2 * a);
  322. var t2 = (-b - discSqrt) / (2 * a);
  323. if (t1 >= 0 && t1 <= 1) {
  324. roots[n++] = t1;
  325. }
  326. if (t2 >= 0 && t2 <= 1) {
  327. roots[n++] = t2;
  328. }
  329. }
  330. }
  331. return n;
  332. }
  333. /**
  334. * 计算二次贝塞尔方程极限值
  335. * @memberOf module:zrender/core/curve
  336. * @param {number} p0
  337. * @param {number} p1
  338. * @param {number} p2
  339. * @return {number}
  340. */
  341. function quadraticExtremum(p0, p1, p2) {
  342. var divider = p0 + p2 - 2 * p1;
  343. if (divider === 0) {
  344. // p1 is center of p0 and p2
  345. return 0.5;
  346. } else {
  347. return (p0 - p1) / divider;
  348. }
  349. }
  350. /**
  351. * 细分二次贝塞尔曲线
  352. * @memberOf module:zrender/core/curve
  353. * @param {number} p0
  354. * @param {number} p1
  355. * @param {number} p2
  356. * @param {number} t
  357. * @param {Array.<number>} out
  358. */
  359. function quadraticSubdivide(p0, p1, p2, t, out) {
  360. var p01 = (p1 - p0) * t + p0;
  361. var p12 = (p2 - p1) * t + p1;
  362. var p012 = (p12 - p01) * t + p01; // Seg0
  363. out[0] = p0;
  364. out[1] = p01;
  365. out[2] = p012; // Seg1
  366. out[3] = p012;
  367. out[4] = p12;
  368. out[5] = p2;
  369. }
  370. /**
  371. * 投射点到二次贝塞尔曲线上,返回投射距离。
  372. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  373. * @param {number} x0
  374. * @param {number} y0
  375. * @param {number} x1
  376. * @param {number} y1
  377. * @param {number} x2
  378. * @param {number} y2
  379. * @param {number} x
  380. * @param {number} y
  381. * @param {Array.<number>} out 投射点
  382. * @return {number}
  383. */
  384. function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {
  385. // http://pomax.github.io/bezierinfo/#projections
  386. var t;
  387. var interval = 0.005;
  388. var d = Infinity;
  389. _v0[0] = x;
  390. _v0[1] = y; // 先粗略估计一下可能的最小距离的 t 值
  391. // PENDING
  392. for (var _t = 0; _t < 1; _t += 0.05) {
  393. _v1[0] = quadraticAt(x0, x1, x2, _t);
  394. _v1[1] = quadraticAt(y0, y1, y2, _t);
  395. var d1 = v2DistSquare(_v0, _v1);
  396. if (d1 < d) {
  397. t = _t;
  398. d = d1;
  399. }
  400. }
  401. d = Infinity; // At most 32 iteration
  402. for (var i = 0; i < 32; i++) {
  403. if (interval < EPSILON_NUMERIC) {
  404. break;
  405. }
  406. var prev = t - interval;
  407. var next = t + interval; // t - interval
  408. _v1[0] = quadraticAt(x0, x1, x2, prev);
  409. _v1[1] = quadraticAt(y0, y1, y2, prev);
  410. var d1 = v2DistSquare(_v1, _v0);
  411. if (prev >= 0 && d1 < d) {
  412. t = prev;
  413. d = d1;
  414. } else {
  415. // t + interval
  416. _v2[0] = quadraticAt(x0, x1, x2, next);
  417. _v2[1] = quadraticAt(y0, y1, y2, next);
  418. var d2 = v2DistSquare(_v2, _v0);
  419. if (next <= 1 && d2 < d) {
  420. t = next;
  421. d = d2;
  422. } else {
  423. interval *= 0.5;
  424. }
  425. }
  426. } // t
  427. if (out) {
  428. out[0] = quadraticAt(x0, x1, x2, t);
  429. out[1] = quadraticAt(y0, y1, y2, t);
  430. } // console.log(interval, i);
  431. return mathSqrt(d);
  432. }
  433. exports.cubicAt = cubicAt;
  434. exports.cubicDerivativeAt = cubicDerivativeAt;
  435. exports.cubicRootAt = cubicRootAt;
  436. exports.cubicExtrema = cubicExtrema;
  437. exports.cubicSubdivide = cubicSubdivide;
  438. exports.cubicProjectPoint = cubicProjectPoint;
  439. exports.quadraticAt = quadraticAt;
  440. exports.quadraticDerivativeAt = quadraticDerivativeAt;
  441. exports.quadraticRootAt = quadraticRootAt;
  442. exports.quadraticExtremum = quadraticExtremum;
  443. exports.quadraticSubdivide = quadraticSubdivide;
  444. exports.quadraticProjectPoint = quadraticProjectPoint;