venn.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. define('echarts/chart/venn', [
  2. 'require',
  3. './base',
  4. 'zrender/shape/Text',
  5. 'zrender/shape/Circle',
  6. 'zrender/shape/Path',
  7. '../config',
  8. '../util/ecData',
  9. 'zrender/tool/util',
  10. '../chart'
  11. ], function (require) {
  12. var ChartBase = require('./base');
  13. var TextShape = require('zrender/shape/Text');
  14. var CircleShape = require('zrender/shape/Circle');
  15. var PathShape = require('zrender/shape/Path');
  16. var ecConfig = require('../config');
  17. ecConfig.venn = {
  18. zlevel: 0,
  19. z: 1,
  20. calculable: false
  21. };
  22. var ecData = require('../util/ecData');
  23. var zrUtil = require('zrender/tool/util');
  24. function Venn(ecTheme, messageCenter, zr, option, myChart) {
  25. ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
  26. this.refresh(option);
  27. }
  28. Venn.prototype = {
  29. type: ecConfig.CHART_TYPE_VENN,
  30. _buildShape: function () {
  31. this.selectedMap = {};
  32. this._symbol = this.option.symbolList;
  33. this._queryTarget;
  34. this._dropBoxList = [];
  35. this._vennDataCounter = 0;
  36. var series = this.series;
  37. var legend = this.component.legend;
  38. for (var i = 0; i < series.length; i++) {
  39. if (series[i].type === ecConfig.CHART_TYPE_VENN) {
  40. series[i] = this.reformOption(series[i]);
  41. var serieName = series[i].name || '';
  42. this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
  43. if (!this.selectedMap[serieName]) {
  44. continue;
  45. }
  46. this._buildVenn(i);
  47. }
  48. }
  49. this.addShapeList();
  50. },
  51. _buildVenn: function (seriesIndex) {
  52. var r0;
  53. var r1;
  54. var serie = this.series[seriesIndex];
  55. var data = serie.data;
  56. if (data[0].value > data[1].value) {
  57. r0 = this.zr.getHeight() / 3;
  58. r1 = r0 * Math.sqrt(data[1].value) / Math.sqrt(data[0].value);
  59. } else {
  60. r1 = this.zr.getHeight() / 3;
  61. r0 = r1 * Math.sqrt(data[0].value) / Math.sqrt(data[1].value);
  62. }
  63. var x0 = this.zr.getWidth() / 2 - r0;
  64. var coincideLengthAnchor = (r0 + r1) / 2 * Math.sqrt(data[2].value) / Math.sqrt((data[0].value + data[1].value) / 2);
  65. var coincideLength = r0 + r1;
  66. if (data[2].value !== 0) {
  67. coincideLength = this._getCoincideLength(data[0].value, data[1].value, data[2].value, r0, r1, coincideLengthAnchor, Math.abs(r0 - r1), r0 + r1);
  68. }
  69. var x1 = x0 + coincideLength;
  70. var y = this.zr.getHeight() / 2;
  71. this._buildItem(seriesIndex, 0, data[0], x0, y, r0);
  72. this._buildItem(seriesIndex, 1, data[1], x1, y, r1);
  73. if (data[2].value !== 0 && data[2].value !== data[0].value && data[2].value !== data[1].value) {
  74. var xLeft = (r0 * r0 - r1 * r1) / (2 * coincideLength) + coincideLength / 2;
  75. var xRight = coincideLength / 2 - (r0 * r0 - r1 * r1) / (2 * coincideLength);
  76. var h = Math.sqrt(r0 * r0 - xLeft * xLeft);
  77. var rightLargeArcFlag = 0;
  78. var leftLargeArcFlag = 0;
  79. if (data[0].value > data[1].value && x1 < x0 + xLeft) {
  80. leftLargeArcFlag = 1;
  81. }
  82. if (data[0].value < data[1].value && x1 < x0 + xRight) {
  83. rightLargeArcFlag = 1;
  84. }
  85. this._buildCoincideItem(seriesIndex, 2, data[2], x0 + xLeft, y - h, y + h, r0, r1, rightLargeArcFlag, leftLargeArcFlag);
  86. }
  87. },
  88. _getCoincideLength: function (value0, value1, value2, r0, r1, coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax) {
  89. var x = (r0 * r0 - r1 * r1) / (2 * coincideLengthAnchor) + coincideLengthAnchor / 2;
  90. var y = coincideLengthAnchor / 2 - (r0 * r0 - r1 * r1) / (2 * coincideLengthAnchor);
  91. var alfa = Math.acos(x / r0);
  92. var beta = Math.acos(y / r1);
  93. var area0 = r0 * r0 * Math.PI;
  94. var area2 = alfa * r0 * r0 - x * r0 * Math.sin(alfa) + beta * r1 * r1 - y * r1 * Math.sin(beta);
  95. var scaleAnchor = area2 / area0;
  96. var scale = value2 / value0;
  97. var approximateValue = Math.abs(scaleAnchor / scale);
  98. if (approximateValue > 0.999 && approximateValue < 1.001) {
  99. return coincideLengthAnchor;
  100. } else if (approximateValue <= 0.999) {
  101. coincideLengthAnchorMax = coincideLengthAnchor;
  102. coincideLengthAnchor = (coincideLengthAnchor + coincideLengthAnchorMin) / 2;
  103. return this._getCoincideLength(value0, value1, value2, r0, r1, coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax);
  104. } else {
  105. coincideLengthAnchorMin = coincideLengthAnchor;
  106. coincideLengthAnchor = (coincideLengthAnchor + coincideLengthAnchorMax) / 2;
  107. return this._getCoincideLength(value0, value1, value2, r0, r1, coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax);
  108. }
  109. },
  110. _buildItem: function (seriesIndex, dataIndex, dataItem, x, y, r) {
  111. var series = this.series;
  112. var serie = series[seriesIndex];
  113. var circle = this.getCircle(seriesIndex, dataIndex, dataItem, x, y, r);
  114. ecData.pack(circle, serie, seriesIndex, dataItem, dataIndex, dataItem.name);
  115. this.shapeList.push(circle);
  116. if (serie.itemStyle.normal.label.show) {
  117. var label = this.getLabel(seriesIndex, dataIndex, dataItem, x, y, r);
  118. ecData.pack(label, serie, seriesIndex, serie.data[dataIndex], dataIndex, serie.data[dataIndex].name);
  119. this.shapeList.push(label);
  120. }
  121. },
  122. _buildCoincideItem: function (seriesIndex, dataIndex, dataItem, x, y0, y1, r0, r1, rightLargeArcFlag, leftLargeArcFlag) {
  123. var series = this.series;
  124. var serie = series[seriesIndex];
  125. var queryTarget = [
  126. dataItem,
  127. serie
  128. ];
  129. var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
  130. var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
  131. var normalColor = normal.color || this.zr.getColor(dataIndex);
  132. var emphasisColor = emphasis.color || this.zr.getColor(dataIndex);
  133. var path = 'M' + x + ',' + y0 + 'A' + r0 + ',' + r0 + ',0,' + rightLargeArcFlag + ',1,' + x + ',' + y1 + 'A' + r1 + ',' + r1 + ',0,' + leftLargeArcFlag + ',1,' + x + ',' + y0;
  134. var style = {
  135. color: normalColor,
  136. path: path
  137. };
  138. var shape = {
  139. zlevel: serie.zlevel,
  140. z: serie.z,
  141. style: style,
  142. highlightStyle: {
  143. color: emphasisColor,
  144. lineWidth: emphasis.borderWidth,
  145. strokeColor: emphasis.borderColor
  146. }
  147. };
  148. shape = new PathShape(shape);
  149. if (shape.buildPathArray) {
  150. shape.style.pathArray = shape.buildPathArray(style.path);
  151. }
  152. ecData.pack(shape, series[seriesIndex], 0, dataItem, dataIndex, dataItem.name);
  153. this.shapeList.push(shape);
  154. },
  155. getCircle: function (seriesIndex, dataIndex, dataItem, x, y, r) {
  156. var serie = this.series[seriesIndex];
  157. var queryTarget = [
  158. dataItem,
  159. serie
  160. ];
  161. var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
  162. var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
  163. var normalColor = normal.color || this.zr.getColor(dataIndex);
  164. var emphasisColor = emphasis.color || this.zr.getColor(dataIndex);
  165. var circle = {
  166. zlevel: serie.zlevel,
  167. z: serie.z,
  168. clickable: true,
  169. style: {
  170. x: x,
  171. y: y,
  172. r: r,
  173. brushType: 'fill',
  174. opacity: 1,
  175. color: normalColor
  176. },
  177. highlightStyle: {
  178. color: emphasisColor,
  179. lineWidth: emphasis.borderWidth,
  180. strokeColor: emphasis.borderColor
  181. }
  182. };
  183. if (this.deepQuery([
  184. dataItem,
  185. serie,
  186. this.option
  187. ], 'calculable')) {
  188. this.setCalculable(circle);
  189. circle.draggable = true;
  190. }
  191. return new CircleShape(circle);
  192. },
  193. getLabel: function (seriesIndex, dataIndex, dataItem, x, y, r) {
  194. var serie = this.series[seriesIndex];
  195. var itemStyle = serie.itemStyle;
  196. var queryTarget = [
  197. dataItem,
  198. serie
  199. ];
  200. var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
  201. var status = 'normal';
  202. var labelControl = itemStyle[status].label;
  203. var textStyle = labelControl.textStyle || {};
  204. var text = this.getLabelText(dataIndex, dataItem, status);
  205. var textFont = this.getFont(textStyle);
  206. var textColor = normal.color || this.zr.getColor(dataIndex);
  207. var textSize = textStyle.fontSize || 12;
  208. var textShape = {
  209. zlevel: serie.zlevel,
  210. z: serie.z,
  211. style: {
  212. x: x,
  213. y: y - r - textSize,
  214. color: textStyle.color || textColor,
  215. text: text,
  216. textFont: textFont,
  217. textAlign: 'center'
  218. }
  219. };
  220. return new TextShape(textShape);
  221. },
  222. getLabelText: function (dataIndex, dataItem, status) {
  223. var series = this.series;
  224. var serie = series[0];
  225. var formatter = this.deepQuery([
  226. dataItem,
  227. serie
  228. ], 'itemStyle.' + status + '.label.formatter');
  229. if (formatter) {
  230. if (typeof formatter == 'function') {
  231. return formatter(serie.name, dataItem.name, dataItem.value);
  232. } else if (typeof formatter == 'string') {
  233. formatter = formatter.replace('{a}', '{a0}').replace('{b}', '{b0}').replace('{c}', '{c0}');
  234. formatter = formatter.replace('{a0}', serie.name).replace('{b0}', dataItem.name).replace('{c0}', dataItem.value);
  235. return formatter;
  236. }
  237. } else {
  238. return dataItem.name;
  239. }
  240. },
  241. refresh: function (newOption) {
  242. if (newOption) {
  243. this.option = newOption;
  244. this.series = newOption.series;
  245. }
  246. this._buildShape();
  247. }
  248. };
  249. zrUtil.inherits(Venn, ChartBase);
  250. require('../chart').define('venn', Venn);
  251. return Venn;
  252. });define('zrender/shape/Path', [
  253. 'require',
  254. './Base',
  255. './util/PathProxy',
  256. '../tool/util'
  257. ], function (require) {
  258. var Base = require('./Base');
  259. var PathProxy = require('./util/PathProxy');
  260. var PathSegment = PathProxy.PathSegment;
  261. var vMag = function (v) {
  262. return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  263. };
  264. var vRatio = function (u, v) {
  265. return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
  266. };
  267. var vAngle = function (u, v) {
  268. return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
  269. };
  270. var Path = function (options) {
  271. Base.call(this, options);
  272. };
  273. Path.prototype = {
  274. type: 'path',
  275. buildPathArray: function (data, x, y) {
  276. if (!data) {
  277. return [];
  278. }
  279. x = x || 0;
  280. y = y || 0;
  281. var cs = data;
  282. var cc = [
  283. 'm',
  284. 'M',
  285. 'l',
  286. 'L',
  287. 'v',
  288. 'V',
  289. 'h',
  290. 'H',
  291. 'z',
  292. 'Z',
  293. 'c',
  294. 'C',
  295. 'q',
  296. 'Q',
  297. 't',
  298. 'T',
  299. 's',
  300. 'S',
  301. 'a',
  302. 'A'
  303. ];
  304. cs = cs.replace(/-/g, ' -');
  305. cs = cs.replace(/ /g, ' ');
  306. cs = cs.replace(/ /g, ',');
  307. cs = cs.replace(/,,/g, ',');
  308. var n;
  309. for (n = 0; n < cc.length; n++) {
  310. cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
  311. }
  312. var arr = cs.split('|');
  313. var ca = [];
  314. var cpx = 0;
  315. var cpy = 0;
  316. for (n = 1; n < arr.length; n++) {
  317. var str = arr[n];
  318. var c = str.charAt(0);
  319. str = str.slice(1);
  320. str = str.replace(new RegExp('e,-', 'g'), 'e-');
  321. var p = str.split(',');
  322. if (p.length > 0 && p[0] === '') {
  323. p.shift();
  324. }
  325. for (var i = 0; i < p.length; i++) {
  326. p[i] = parseFloat(p[i]);
  327. }
  328. while (p.length > 0) {
  329. if (isNaN(p[0])) {
  330. break;
  331. }
  332. var cmd = null;
  333. var points = [];
  334. var ctlPtx;
  335. var ctlPty;
  336. var prevCmd;
  337. var rx;
  338. var ry;
  339. var psi;
  340. var fa;
  341. var fs;
  342. var x1 = cpx;
  343. var y1 = cpy;
  344. switch (c) {
  345. case 'l':
  346. cpx += p.shift();
  347. cpy += p.shift();
  348. cmd = 'L';
  349. points.push(cpx, cpy);
  350. break;
  351. case 'L':
  352. cpx = p.shift();
  353. cpy = p.shift();
  354. points.push(cpx, cpy);
  355. break;
  356. case 'm':
  357. cpx += p.shift();
  358. cpy += p.shift();
  359. cmd = 'M';
  360. points.push(cpx, cpy);
  361. c = 'l';
  362. break;
  363. case 'M':
  364. cpx = p.shift();
  365. cpy = p.shift();
  366. cmd = 'M';
  367. points.push(cpx, cpy);
  368. c = 'L';
  369. break;
  370. case 'h':
  371. cpx += p.shift();
  372. cmd = 'L';
  373. points.push(cpx, cpy);
  374. break;
  375. case 'H':
  376. cpx = p.shift();
  377. cmd = 'L';
  378. points.push(cpx, cpy);
  379. break;
  380. case 'v':
  381. cpy += p.shift();
  382. cmd = 'L';
  383. points.push(cpx, cpy);
  384. break;
  385. case 'V':
  386. cpy = p.shift();
  387. cmd = 'L';
  388. points.push(cpx, cpy);
  389. break;
  390. case 'C':
  391. points.push(p.shift(), p.shift(), p.shift(), p.shift());
  392. cpx = p.shift();
  393. cpy = p.shift();
  394. points.push(cpx, cpy);
  395. break;
  396. case 'c':
  397. points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
  398. cpx += p.shift();
  399. cpy += p.shift();
  400. cmd = 'C';
  401. points.push(cpx, cpy);
  402. break;
  403. case 'S':
  404. ctlPtx = cpx;
  405. ctlPty = cpy;
  406. prevCmd = ca[ca.length - 1];
  407. if (prevCmd.command === 'C') {
  408. ctlPtx = cpx + (cpx - prevCmd.points[2]);
  409. ctlPty = cpy + (cpy - prevCmd.points[3]);
  410. }
  411. points.push(ctlPtx, ctlPty, p.shift(), p.shift());
  412. cpx = p.shift();
  413. cpy = p.shift();
  414. cmd = 'C';
  415. points.push(cpx, cpy);
  416. break;
  417. case 's':
  418. ctlPtx = cpx, ctlPty = cpy;
  419. prevCmd = ca[ca.length - 1];
  420. if (prevCmd.command === 'C') {
  421. ctlPtx = cpx + (cpx - prevCmd.points[2]);
  422. ctlPty = cpy + (cpy - prevCmd.points[3]);
  423. }
  424. points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
  425. cpx += p.shift();
  426. cpy += p.shift();
  427. cmd = 'C';
  428. points.push(cpx, cpy);
  429. break;
  430. case 'Q':
  431. points.push(p.shift(), p.shift());
  432. cpx = p.shift();
  433. cpy = p.shift();
  434. points.push(cpx, cpy);
  435. break;
  436. case 'q':
  437. points.push(cpx + p.shift(), cpy + p.shift());
  438. cpx += p.shift();
  439. cpy += p.shift();
  440. cmd = 'Q';
  441. points.push(cpx, cpy);
  442. break;
  443. case 'T':
  444. ctlPtx = cpx, ctlPty = cpy;
  445. prevCmd = ca[ca.length - 1];
  446. if (prevCmd.command === 'Q') {
  447. ctlPtx = cpx + (cpx - prevCmd.points[0]);
  448. ctlPty = cpy + (cpy - prevCmd.points[1]);
  449. }
  450. cpx = p.shift();
  451. cpy = p.shift();
  452. cmd = 'Q';
  453. points.push(ctlPtx, ctlPty, cpx, cpy);
  454. break;
  455. case 't':
  456. ctlPtx = cpx, ctlPty = cpy;
  457. prevCmd = ca[ca.length - 1];
  458. if (prevCmd.command === 'Q') {
  459. ctlPtx = cpx + (cpx - prevCmd.points[0]);
  460. ctlPty = cpy + (cpy - prevCmd.points[1]);
  461. }
  462. cpx += p.shift();
  463. cpy += p.shift();
  464. cmd = 'Q';
  465. points.push(ctlPtx, ctlPty, cpx, cpy);
  466. break;
  467. case 'A':
  468. rx = p.shift();
  469. ry = p.shift();
  470. psi = p.shift();
  471. fa = p.shift();
  472. fs = p.shift();
  473. x1 = cpx, y1 = cpy;
  474. cpx = p.shift(), cpy = p.shift();
  475. cmd = 'A';
  476. points = this._convertPoint(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
  477. break;
  478. case 'a':
  479. rx = p.shift();
  480. ry = p.shift();
  481. psi = p.shift();
  482. fa = p.shift();
  483. fs = p.shift();
  484. x1 = cpx, y1 = cpy;
  485. cpx += p.shift();
  486. cpy += p.shift();
  487. cmd = 'A';
  488. points = this._convertPoint(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
  489. break;
  490. }
  491. for (var j = 0, l = points.length; j < l; j += 2) {
  492. points[j] += x;
  493. points[j + 1] += y;
  494. }
  495. ca.push(new PathSegment(cmd || c, points));
  496. }
  497. if (c === 'z' || c === 'Z') {
  498. ca.push(new PathSegment('z', []));
  499. }
  500. }
  501. return ca;
  502. },
  503. _convertPoint: function (x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
  504. var psi = psiDeg * (Math.PI / 180);
  505. var xp = Math.cos(psi) * (x1 - x2) / 2 + Math.sin(psi) * (y1 - y2) / 2;
  506. var yp = -1 * Math.sin(psi) * (x1 - x2) / 2 + Math.cos(psi) * (y1 - y2) / 2;
  507. var lambda = xp * xp / (rx * rx) + yp * yp / (ry * ry);
  508. if (lambda > 1) {
  509. rx *= Math.sqrt(lambda);
  510. ry *= Math.sqrt(lambda);
  511. }
  512. var f = Math.sqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) / (rx * rx * (yp * yp) + ry * ry * (xp * xp)));
  513. if (fa === fs) {
  514. f *= -1;
  515. }
  516. if (isNaN(f)) {
  517. f = 0;
  518. }
  519. var cxp = f * rx * yp / ry;
  520. var cyp = f * -ry * xp / rx;
  521. var cx = (x1 + x2) / 2 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
  522. var cy = (y1 + y2) / 2 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
  523. var theta = vAngle([
  524. 1,
  525. 0
  526. ], [
  527. (xp - cxp) / rx,
  528. (yp - cyp) / ry
  529. ]);
  530. var u = [
  531. (xp - cxp) / rx,
  532. (yp - cyp) / ry
  533. ];
  534. var v = [
  535. (-1 * xp - cxp) / rx,
  536. (-1 * yp - cyp) / ry
  537. ];
  538. var dTheta = vAngle(u, v);
  539. if (vRatio(u, v) <= -1) {
  540. dTheta = Math.PI;
  541. }
  542. if (vRatio(u, v) >= 1) {
  543. dTheta = 0;
  544. }
  545. if (fs === 0 && dTheta > 0) {
  546. dTheta = dTheta - 2 * Math.PI;
  547. }
  548. if (fs === 1 && dTheta < 0) {
  549. dTheta = dTheta + 2 * Math.PI;
  550. }
  551. return [
  552. cx,
  553. cy,
  554. rx,
  555. ry,
  556. theta,
  557. dTheta,
  558. psi,
  559. fs
  560. ];
  561. },
  562. buildPath: function (ctx, style) {
  563. var path = style.path;
  564. var x = style.x || 0;
  565. var y = style.y || 0;
  566. style.pathArray = style.pathArray || this.buildPathArray(path, x, y);
  567. var pathArray = style.pathArray;
  568. var pointList = style.pointList = [];
  569. var singlePointList = [];
  570. for (var i = 0, l = pathArray.length; i < l; i++) {
  571. if (pathArray[i].command.toUpperCase() == 'M') {
  572. singlePointList.length > 0 && pointList.push(singlePointList);
  573. singlePointList = [];
  574. }
  575. var p = pathArray[i].points;
  576. for (var j = 0, k = p.length; j < k; j += 2) {
  577. singlePointList.push([
  578. p[j],
  579. p[j + 1]
  580. ]);
  581. }
  582. }
  583. singlePointList.length > 0 && pointList.push(singlePointList);
  584. for (var i = 0, l = pathArray.length; i < l; i++) {
  585. var c = pathArray[i].command;
  586. var p = pathArray[i].points;
  587. switch (c) {
  588. case 'L':
  589. ctx.lineTo(p[0], p[1]);
  590. break;
  591. case 'M':
  592. ctx.moveTo(p[0], p[1]);
  593. break;
  594. case 'C':
  595. ctx.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
  596. break;
  597. case 'Q':
  598. ctx.quadraticCurveTo(p[0], p[1], p[2], p[3]);
  599. break;
  600. case 'A':
  601. var cx = p[0];
  602. var cy = p[1];
  603. var rx = p[2];
  604. var ry = p[3];
  605. var theta = p[4];
  606. var dTheta = p[5];
  607. var psi = p[6];
  608. var fs = p[7];
  609. var r = rx > ry ? rx : ry;
  610. var scaleX = rx > ry ? 1 : rx / ry;
  611. var scaleY = rx > ry ? ry / rx : 1;
  612. ctx.translate(cx, cy);
  613. ctx.rotate(psi);
  614. ctx.scale(scaleX, scaleY);
  615. ctx.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
  616. ctx.scale(1 / scaleX, 1 / scaleY);
  617. ctx.rotate(-psi);
  618. ctx.translate(-cx, -cy);
  619. break;
  620. case 'z':
  621. ctx.closePath();
  622. break;
  623. }
  624. }
  625. return;
  626. },
  627. getRect: function (style) {
  628. if (style.__rect) {
  629. return style.__rect;
  630. }
  631. var lineWidth;
  632. if (style.brushType == 'stroke' || style.brushType == 'fill') {
  633. lineWidth = style.lineWidth || 1;
  634. } else {
  635. lineWidth = 0;
  636. }
  637. var minX = Number.MAX_VALUE;
  638. var maxX = Number.MIN_VALUE;
  639. var minY = Number.MAX_VALUE;
  640. var maxY = Number.MIN_VALUE;
  641. var x = style.x || 0;
  642. var y = style.y || 0;
  643. var pathArray = style.pathArray || this.buildPathArray(style.path);
  644. for (var i = 0; i < pathArray.length; i++) {
  645. var p = pathArray[i].points;
  646. for (var j = 0; j < p.length; j++) {
  647. if (j % 2 === 0) {
  648. if (p[j] + x < minX) {
  649. minX = p[j];
  650. }
  651. if (p[j] + x > maxX) {
  652. maxX = p[j];
  653. }
  654. } else {
  655. if (p[j] + y < minY) {
  656. minY = p[j];
  657. }
  658. if (p[j] + y > maxY) {
  659. maxY = p[j];
  660. }
  661. }
  662. }
  663. }
  664. var rect;
  665. if (minX === Number.MAX_VALUE || maxX === Number.MIN_VALUE || minY === Number.MAX_VALUE || maxY === Number.MIN_VALUE) {
  666. rect = {
  667. x: 0,
  668. y: 0,
  669. width: 0,
  670. height: 0
  671. };
  672. } else {
  673. rect = {
  674. x: Math.round(minX - lineWidth / 2),
  675. y: Math.round(minY - lineWidth / 2),
  676. width: maxX - minX + lineWidth,
  677. height: maxY - minY + lineWidth
  678. };
  679. }
  680. style.__rect = rect;
  681. return rect;
  682. }
  683. };
  684. require('../tool/util').inherits(Path, Base);
  685. return Path;
  686. });