Axis.html 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-chart-axis-Axis'>/**
  19. </span> * @class Ext.chart.axis.Axis
  20. *
  21. * Defines axis for charts. The axis position, type, style can be configured.
  22. * The axes are defined in an axes array of configuration objects where the type,
  23. * field, grid and other configuration options can be set. To know more about how
  24. * to create a Chart please check the Chart class documentation. Here's an example for the axes part:
  25. * An example of axis for a series (in this case for an area chart that has multiple layers of yFields) could be:
  26. *
  27. * axes: [{
  28. * type: 'Numeric',
  29. * position: 'left',
  30. * fields: ['data1', 'data2', 'data3'],
  31. * title: 'Number of Hits',
  32. * grid: {
  33. * odd: {
  34. * opacity: 1,
  35. * fill: '#ddd',
  36. * stroke: '#bbb',
  37. * 'stroke-width': 1
  38. * }
  39. * },
  40. * minimum: 0
  41. * }, {
  42. * type: 'Category',
  43. * position: 'bottom',
  44. * fields: ['name'],
  45. * title: 'Month of the Year',
  46. * grid: true,
  47. * label: {
  48. * rotate: {
  49. * degrees: 315
  50. * }
  51. * }
  52. * }]
  53. *
  54. * In this case we use a `Numeric` axis for displaying the values of the Area series and a `Category` axis for displaying the names of
  55. * the store elements. The numeric axis is placed on the left of the screen, while the category axis is placed at the bottom of the chart.
  56. * Both the category and numeric axes have `grid` set, which means that horizontal and vertical lines will cover the chart background. In the
  57. * category axis the labels will be rotated so they can fit the space better.
  58. */
  59. Ext.define('Ext.chart.axis.Axis', {
  60. /* Begin Definitions */
  61. extend: 'Ext.chart.axis.Abstract',
  62. alternateClassName: 'Ext.chart.Axis',
  63. requires: ['Ext.draw.Draw'],
  64. /* End Definitions */
  65. <span id='Ext-chart-axis-Axis-cfg-grid'> /**
  66. </span> * @cfg {Boolean/Object} grid
  67. * The grid configuration enables you to set a background grid for an axis.
  68. * If set to *true* on a vertical axis, vertical lines will be drawn.
  69. * If set to *true* on a horizontal axis, horizontal lines will be drawn.
  70. * If both are set, a proper grid with horizontal and vertical lines will be drawn.
  71. *
  72. * You can set specific options for the grid configuration for odd and/or even lines/rows.
  73. * Since the rows being drawn are rectangle sprites, you can set to an odd or even property
  74. * all styles that apply to {@link Ext.draw.Sprite}. For more information on all the style
  75. * properties you can set please take a look at {@link Ext.draw.Sprite}. Some useful style properties are `opacity`, `fill`, `stroke`, `stroke-width`, etc.
  76. *
  77. * The possible values for a grid option are then *true*, *false*, or an object with `{ odd, even }` properties
  78. * where each property contains a sprite style descriptor object that is defined in {@link Ext.draw.Sprite}.
  79. *
  80. * For example:
  81. *
  82. * axes: [{
  83. * type: 'Numeric',
  84. * position: 'left',
  85. * fields: ['data1', 'data2', 'data3'],
  86. * title: 'Number of Hits',
  87. * grid: {
  88. * odd: {
  89. * opacity: 1,
  90. * fill: '#ddd',
  91. * stroke: '#bbb',
  92. * 'stroke-width': 1
  93. * }
  94. * }
  95. * }, {
  96. * type: 'Category',
  97. * position: 'bottom',
  98. * fields: ['name'],
  99. * title: 'Month of the Year',
  100. * grid: true
  101. * }]
  102. *
  103. */
  104. <span id='Ext-chart-axis-Axis-cfg-majorTickSteps'> /**
  105. </span> * @cfg {Number} majorTickSteps
  106. * If `minimum` and `maximum` are specified it forces the number of major ticks to the specified value.
  107. * If a number of major ticks is forced, it wont search for pretty numbers at the ticks.
  108. */
  109. <span id='Ext-chart-axis-Axis-cfg-minorTickSteps'> /**
  110. </span> * @cfg {Number} minorTickSteps
  111. * The number of small ticks between two major ticks. Default is zero.
  112. */
  113. <span id='Ext-chart-axis-Axis-cfg-title'> /**
  114. </span> * @cfg {String} title
  115. * The title for the Axis
  116. */
  117. // @private force min/max values from store
  118. forceMinMax: false,
  119. <span id='Ext-chart-axis-Axis-cfg-dashSize'> /**
  120. </span> * @cfg {Number} dashSize
  121. * The size of the dash marker. Default's 3.
  122. */
  123. dashSize: 3,
  124. <span id='Ext-chart-axis-Axis-cfg-position'> /**
  125. </span> * @cfg {String} position
  126. * Where to set the axis. Available options are `left`, `bottom`, `right`, `top`. Default's `bottom`.
  127. */
  128. position: 'bottom',
  129. // @private
  130. skipFirst: false,
  131. <span id='Ext-chart-axis-Axis-cfg-length'> /**
  132. </span> * @cfg {Number} length
  133. * Offset axis position. Default's 0.
  134. */
  135. length: 0,
  136. <span id='Ext-chart-axis-Axis-cfg-width'> /**
  137. </span> * @cfg {Number} width
  138. * Offset axis width. Default's 0.
  139. */
  140. width: 0,
  141. <span id='Ext-chart-axis-Axis-cfg-adjustEnd'> /**
  142. </span> * @cfg {Boolean} adjustEnd
  143. * Whether to adjust the label at the end of the axis.
  144. */
  145. adjustEnd: true,
  146. majorTickSteps: false,
  147. // @private
  148. applyData: Ext.emptyFn,
  149. getRange: function () {
  150. var me = this,
  151. chart = me.chart,
  152. store = chart.getChartStore(),
  153. data = store.data.items,
  154. series = chart.series.items,
  155. position = me.position,
  156. boundedAxes,
  157. seriesClasses = Ext.chart.series,
  158. aggregations = [],
  159. min = Infinity, max = -Infinity,
  160. vertical = me.position === 'left' || me.position === 'right',
  161. i, ln, ln2, j, k, dataLength = data.length, aggregates,
  162. countedFields = {},
  163. allFields = {},
  164. excludable = true,
  165. fields, fieldMap, record, field, value;
  166. fields = me.fields;
  167. for (j = 0, ln = fields.length; j &lt; ln; j++) {
  168. allFields[fields[j]] = true;
  169. }
  170. for (i = 0, ln = series.length; i &lt; ln; i++) {
  171. if (series[i].seriesIsHidden) {
  172. continue;
  173. }
  174. if (!series[i].getAxesForXAndYFields) {
  175. continue;
  176. }
  177. boundedAxes = series[i].getAxesForXAndYFields();
  178. if (boundedAxes.xAxis &amp;&amp; boundedAxes.xAxis !== position &amp;&amp; boundedAxes.yAxis &amp;&amp; boundedAxes.yAxis !== position) {
  179. // If the series explicitly exclude current Axis, then exit.
  180. continue;
  181. }
  182. if (seriesClasses.Bar &amp;&amp; series[i] instanceof seriesClasses.Bar &amp;&amp; !series[i].column) {
  183. // If this is a horizontal bar series, then flip xField and yField.
  184. fields = vertical ? Ext.Array.from(series[i].xField) : Ext.Array.from(series[i].yField);
  185. } else {
  186. fields = vertical ? Ext.Array.from(series[i].yField) : Ext.Array.from(series[i].xField);
  187. }
  188. if (me.fields.length) {
  189. for (j = 0, ln2 = fields.length; j &lt; ln2; j++) {
  190. if (allFields[fields[j]]) {
  191. break;
  192. }
  193. }
  194. if (j == ln2) {
  195. // Not matching fields, skipping this series.
  196. continue;
  197. }
  198. }
  199. if (aggregates = series[i].stacked) {
  200. // If this is a bar/column series, then it will be aggregated if it is of the same direction of the axis.
  201. if (seriesClasses.Bar &amp;&amp; series[i] instanceof seriesClasses.Bar) {
  202. if (series[i].column != vertical) {
  203. aggregates = false;
  204. excludable = false;
  205. }
  206. }
  207. // Otherwise it is stacked vertically
  208. else if (!vertical) {
  209. aggregates = false;
  210. excludable = false;
  211. }
  212. }
  213. if (aggregates) {
  214. fieldMap = {};
  215. for (j = 0; j &lt; fields.length; j++) {
  216. if (excludable &amp;&amp; series[i].__excludes &amp;&amp; series[i].__excludes[j]) {
  217. continue;
  218. }
  219. if (!allFields[fields[j]]) {
  220. Ext.Logger.warn('Field `' + fields[j] + '` is not included in the ' + position + ' axis config.');
  221. }
  222. allFields[fields[j]] = fieldMap[fields[j]] = true;
  223. }
  224. aggregations.push({
  225. fields: fieldMap,
  226. value: 0
  227. });
  228. } else {
  229. if (!fields || fields.length == 0) {
  230. fields = me.fields;
  231. }
  232. for (j = 0; j &lt; fields.length; j++) {
  233. if (excludable &amp;&amp; series[i].__excludes &amp;&amp; series[i].__excludes[j]) {
  234. continue;
  235. }
  236. allFields[fields[j]] = countedFields[fields[j]] = true;
  237. }
  238. }
  239. }
  240. for (i = 0; i &lt; dataLength; i++) {
  241. record = data[i];
  242. for (k = 0; k &lt; aggregations.length; k++) {
  243. aggregations[k].value = 0;
  244. }
  245. for (field in allFields) {
  246. value = record.get(field);
  247. if (isNaN(value)) {
  248. continue;
  249. }
  250. if (value === undefined) {
  251. value = 0;
  252. }
  253. if (countedFields[field]) {
  254. if (min &gt; value) {
  255. min = value;
  256. }
  257. if (max &lt; value) {
  258. max = value;
  259. }
  260. }
  261. for (k = 0; k &lt; aggregations.length; k++) {
  262. if (aggregations[k].fields[field]) {
  263. aggregations[k].value += value;
  264. // If any aggregation is actually hit, then the min value should be at most 0.
  265. if (min &gt; 0) {
  266. min = 0;
  267. }
  268. if (max &lt; aggregations[k].value) {
  269. max = aggregations[k].value;
  270. }
  271. }
  272. }
  273. }
  274. }
  275. if (!isFinite(max)) {
  276. max = me.prevMax || 0;
  277. }
  278. if (!isFinite(min)) {
  279. min = me.prevMin || 0;
  280. }
  281. //normalize min max for snapEnds.
  282. if (min != max &amp;&amp; (max != Math.floor(max))) {
  283. max = Math.floor(max) + 1;
  284. }
  285. if (!isNaN(me.minimum)) {
  286. min = me.minimum;
  287. }
  288. if (!isNaN(me.maximum)) {
  289. max = me.maximum;
  290. }
  291. if (min &gt;= max) {
  292. // snapEnds will return NaN if max &gt;= min;
  293. max = min + 1;
  294. }
  295. return {min: min, max: max};
  296. },
  297. // @private creates a structure with start, end and step points.
  298. calcEnds: function () {
  299. var me = this,
  300. range = me.getRange(),
  301. min = range.min,
  302. max = range.max,
  303. steps, prettyNumbers, out, changedRange;
  304. steps = (Ext.isNumber(me.majorTickSteps) ? me.majorTickSteps + 1 : me.steps);
  305. prettyNumbers = !(Ext.isNumber(me.maximum) &amp;&amp; Ext.isNumber(me.minimum) &amp;&amp; Ext.isNumber(me.majorTickSteps) &amp;&amp; me.majorTickSteps &gt; 0);
  306. out = Ext.draw.Draw.snapEnds(min, max, steps, prettyNumbers);
  307. if (Ext.isNumber(me.maximum)) {
  308. out.to = me.maximum;
  309. changedRange = true;
  310. }
  311. if (Ext.isNumber(me.minimum)) {
  312. out.from = me.minimum;
  313. changedRange = true;
  314. }
  315. if (me.adjustMaximumByMajorUnit) {
  316. out.to = Math.ceil(out.to / out.step) * out.step;
  317. changedRange = true;
  318. }
  319. if (me.adjustMinimumByMajorUnit) {
  320. out.from = Math.floor(out.from / out.step) * out.step;
  321. changedRange = true;
  322. }
  323. if (changedRange) {
  324. out.steps = Math.ceil((out.to - out.from) / out.step);
  325. }
  326. me.prevMin = (min == max ? 0 : min);
  327. me.prevMax = max;
  328. return out;
  329. },
  330. <span id='Ext-chart-axis-Axis-method-drawAxis'> /**
  331. </span> * Renders the axis into the screen and updates its position.
  332. */
  333. drawAxis: function (init) {
  334. var me = this,
  335. i,
  336. x = me.x,
  337. y = me.y,
  338. gutterX = me.chart.maxGutter[0],
  339. gutterY = me.chart.maxGutter[1],
  340. dashSize = me.dashSize,
  341. subDashesX = me.minorTickSteps || 0,
  342. subDashesY = me.minorTickSteps || 0,
  343. length = me.length,
  344. position = me.position,
  345. inflections = [],
  346. calcLabels = false,
  347. stepCalcs = me.applyData(),
  348. step = stepCalcs.step,
  349. steps = stepCalcs.steps,
  350. from = stepCalcs.from,
  351. to = stepCalcs.to,
  352. trueLength,
  353. currentX,
  354. currentY,
  355. path,
  356. dashesX,
  357. dashesY,
  358. delta;
  359. //If no steps are specified
  360. //then don't draw the axis. This generally happens
  361. //when an empty store.
  362. if (me.hidden || isNaN(step) || (from &gt; to)) {
  363. return;
  364. }
  365. me.from = stepCalcs.from;
  366. me.to = stepCalcs.to;
  367. if (position == 'left' || position == 'right') {
  368. currentX = Math.floor(x) + 0.5;
  369. path = [&quot;M&quot;, currentX, y, &quot;l&quot;, 0, -length];
  370. trueLength = length - (gutterY * 2);
  371. }
  372. else {
  373. currentY = Math.floor(y) + 0.5;
  374. path = [&quot;M&quot;, x, currentY, &quot;l&quot;, length, 0];
  375. trueLength = length - (gutterX * 2);
  376. }
  377. // Supports the case that we have only 1 record.
  378. delta = steps &amp;&amp; trueLength / steps;
  379. dashesX = Math.max(subDashesX + 1, 0);
  380. dashesY = Math.max(subDashesY + 1, 0);
  381. if (me.type == 'Numeric' || me.type == 'Time') {
  382. calcLabels = true;
  383. me.labels = [stepCalcs.from];
  384. }
  385. if (position == 'right' || position == 'left') {
  386. currentY = y - gutterY;
  387. currentX = x - ((position == 'left') * dashSize * 2);
  388. while (currentY &gt;= y - gutterY - trueLength) {
  389. path.push(&quot;M&quot;, currentX, Math.floor(currentY) + 0.5, &quot;l&quot;, dashSize * 2 + 1, 0);
  390. if (currentY != y - gutterY) {
  391. for (i = 1; i &lt; dashesY; i++) {
  392. path.push(&quot;M&quot;, currentX + dashSize, Math.floor(currentY + delta * i / dashesY) + 0.5, &quot;l&quot;, dashSize + 1, 0);
  393. }
  394. }
  395. inflections.push([ Math.floor(x), Math.floor(currentY) ]);
  396. currentY -= delta;
  397. if (calcLabels) {
  398. me.labels.push(me.labels[me.labels.length - 1] + step);
  399. }
  400. if (delta === 0) {
  401. break;
  402. }
  403. }
  404. if (Math.round(currentY + delta - (y - gutterY - trueLength))) {
  405. path.push(&quot;M&quot;, currentX, Math.floor(y - length + gutterY) + 0.5, &quot;l&quot;, dashSize * 2 + 1, 0);
  406. for (i = 1; i &lt; dashesY; i++) {
  407. path.push(&quot;M&quot;, currentX + dashSize, Math.floor(y - length + gutterY + delta * i / dashesY) + 0.5, &quot;l&quot;, dashSize + 1, 0);
  408. }
  409. inflections.push([ Math.floor(x), Math.floor(currentY) ]);
  410. if (calcLabels) {
  411. me.labels.push(me.labels[me.labels.length - 1] + step);
  412. }
  413. }
  414. } else {
  415. currentX = x + gutterX;
  416. currentY = y - ((position == 'top') * dashSize * 2);
  417. while (currentX &lt;= x + gutterX + trueLength) {
  418. path.push(&quot;M&quot;, Math.floor(currentX) + 0.5, currentY, &quot;l&quot;, 0, dashSize * 2 + 1);
  419. if (currentX != x + gutterX) {
  420. for (i = 1; i &lt; dashesX; i++) {
  421. path.push(&quot;M&quot;, Math.floor(currentX - delta * i / dashesX) + 0.5, currentY, &quot;l&quot;, 0, dashSize + 1);
  422. }
  423. }
  424. inflections.push([ Math.floor(currentX), Math.floor(y) ]);
  425. currentX += delta;
  426. if (calcLabels) {
  427. me.labels.push(me.labels[me.labels.length - 1] + step);
  428. }
  429. if (delta === 0) {
  430. break;
  431. }
  432. }
  433. if (Math.round(currentX - delta - (x + gutterX + trueLength))) {
  434. path.push(&quot;M&quot;, Math.floor(x + length - gutterX) + 0.5, currentY, &quot;l&quot;, 0, dashSize * 2 + 1);
  435. for (i = 1; i &lt; dashesX; i++) {
  436. path.push(&quot;M&quot;, Math.floor(x + length - gutterX - delta * i / dashesX) + 0.5, currentY, &quot;l&quot;, 0, dashSize + 1);
  437. }
  438. inflections.push([ Math.floor(currentX), Math.floor(y) ]);
  439. if (calcLabels) {
  440. me.labels.push(me.labels[me.labels.length - 1] + step);
  441. }
  442. }
  443. }
  444. // the label on index &quot;inflections.length-1&quot; is the last label that gets rendered
  445. if (calcLabels) {
  446. me.labels[inflections.length - 1] = +(me.labels[inflections.length - 1]).toFixed(10);
  447. }
  448. if (!me.axis) {
  449. me.axis = me.chart.surface.add(Ext.apply({
  450. type: 'path',
  451. path: path
  452. }, me.axisStyle));
  453. }
  454. me.axis.setAttributes({
  455. path: path
  456. }, true);
  457. me.inflections = inflections;
  458. if (!init &amp;&amp; me.grid) {
  459. me.drawGrid();
  460. }
  461. me.axisBBox = me.axis.getBBox();
  462. me.drawLabel();
  463. },
  464. <span id='Ext-chart-axis-Axis-method-drawGrid'> /**
  465. </span> * Renders an horizontal and/or vertical grid into the Surface.
  466. */
  467. drawGrid: function () {
  468. var me = this,
  469. surface = me.chart.surface,
  470. grid = me.grid,
  471. odd = grid.odd,
  472. even = grid.even,
  473. inflections = me.inflections,
  474. ln = inflections.length - ((odd || even) ? 0 : 1),
  475. position = me.position,
  476. gutter = me.chart.maxGutter,
  477. width = me.width - 2,
  478. point, prevPoint,
  479. i = 1,
  480. path = [], styles, lineWidth, dlineWidth,
  481. oddPath = [], evenPath = [];
  482. if ((gutter[1] !== 0 &amp;&amp; (position == 'left' || position == 'right')) ||
  483. (gutter[0] !== 0 &amp;&amp; (position == 'top' || position == 'bottom'))) {
  484. i = 0;
  485. ln++;
  486. }
  487. for (; i &lt; ln; i++) {
  488. point = inflections[i];
  489. prevPoint = inflections[i - 1];
  490. if (odd || even) {
  491. path = (i % 2) ? oddPath : evenPath;
  492. styles = ((i % 2) ? odd : even) || {};
  493. lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;
  494. dlineWidth = 2 * lineWidth;
  495. if (position == 'left') {
  496. path.push(&quot;M&quot;, prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth,
  497. &quot;L&quot;, prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,
  498. &quot;L&quot;, point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,
  499. &quot;L&quot;, point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, &quot;Z&quot;);
  500. }
  501. else if (position == 'right') {
  502. path.push(&quot;M&quot;, prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth,
  503. &quot;L&quot;, prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,
  504. &quot;L&quot;, point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,
  505. &quot;L&quot;, point[0] - lineWidth, point[1] + 0.5 + lineWidth, &quot;Z&quot;);
  506. }
  507. else if (position == 'top') {
  508. path.push(&quot;M&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth,
  509. &quot;L&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,
  510. &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,
  511. &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, &quot;Z&quot;);
  512. }
  513. else {
  514. path.push(&quot;M&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth,
  515. &quot;L&quot;, prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,
  516. &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,
  517. &quot;L&quot;, point[0] + 0.5 - lineWidth, point[1] - lineWidth, &quot;Z&quot;);
  518. }
  519. } else {
  520. if (position == 'left') {
  521. path = path.concat([&quot;M&quot;, point[0] + 0.5, point[1] + 0.5, &quot;l&quot;, width, 0]);
  522. }
  523. else if (position == 'right') {
  524. path = path.concat([&quot;M&quot;, point[0] - 0.5, point[1] + 0.5, &quot;l&quot;, -width, 0]);
  525. }
  526. else if (position == 'top') {
  527. path = path.concat([&quot;M&quot;, point[0] + 0.5, point[1] + 0.5, &quot;l&quot;, 0, width]);
  528. }
  529. else {
  530. path = path.concat([&quot;M&quot;, point[0] + 0.5, point[1] - 0.5, &quot;l&quot;, 0, -width]);
  531. }
  532. }
  533. }
  534. if (odd || even) {
  535. if (oddPath.length) {
  536. if (!me.gridOdd &amp;&amp; oddPath.length) {
  537. me.gridOdd = surface.add({
  538. type: 'path',
  539. path: oddPath
  540. });
  541. }
  542. me.gridOdd.setAttributes(Ext.apply({
  543. path: oddPath,
  544. hidden: false
  545. }, odd || {}), true);
  546. }
  547. if (evenPath.length) {
  548. if (!me.gridEven) {
  549. me.gridEven = surface.add({
  550. type: 'path',
  551. path: evenPath
  552. });
  553. }
  554. me.gridEven.setAttributes(Ext.apply({
  555. path: evenPath,
  556. hidden: false
  557. }, even || {}), true);
  558. }
  559. }
  560. else {
  561. if (path.length) {
  562. if (!me.gridLines) {
  563. me.gridLines = me.chart.surface.add({
  564. type: 'path',
  565. path: path,
  566. &quot;stroke-width&quot;: me.lineWidth || 1,
  567. stroke: me.gridColor || '#ccc'
  568. });
  569. }
  570. me.gridLines.setAttributes({
  571. hidden: false,
  572. path: path
  573. }, true);
  574. }
  575. else if (me.gridLines) {
  576. me.gridLines.hide(true);
  577. }
  578. }
  579. },
  580. // @private
  581. getOrCreateLabel: function (i, text) {
  582. var me = this,
  583. labelGroup = me.labelGroup,
  584. textLabel = labelGroup.getAt(i),
  585. surface = me.chart.surface;
  586. if (textLabel) {
  587. if (text != textLabel.attr.text) {
  588. textLabel.setAttributes(Ext.apply({
  589. text: text
  590. }, me.label), true);
  591. textLabel._bbox = textLabel.getBBox();
  592. }
  593. }
  594. else {
  595. textLabel = surface.add(Ext.apply({
  596. group: labelGroup,
  597. type: 'text',
  598. x: 0,
  599. y: 0,
  600. text: text
  601. }, me.label));
  602. surface.renderItem(textLabel);
  603. textLabel._bbox = textLabel.getBBox();
  604. }
  605. //get untransformed bounding box
  606. if (me.label.rotation) {
  607. textLabel.setAttributes({
  608. rotation: {
  609. degrees: 0
  610. }
  611. }, true);
  612. textLabel._ubbox = textLabel.getBBox();
  613. textLabel.setAttributes(me.label, true);
  614. } else {
  615. textLabel._ubbox = textLabel._bbox;
  616. }
  617. return textLabel;
  618. },
  619. rect2pointArray: function (sprite) {
  620. var surface = this.chart.surface,
  621. rect = surface.getBBox(sprite, true),
  622. p1 = [rect.x, rect.y],
  623. p1p = p1.slice(),
  624. p2 = [rect.x + rect.width, rect.y],
  625. p2p = p2.slice(),
  626. p3 = [rect.x + rect.width, rect.y + rect.height],
  627. p3p = p3.slice(),
  628. p4 = [rect.x, rect.y + rect.height],
  629. p4p = p4.slice(),
  630. matrix = sprite.matrix;
  631. //transform the points
  632. p1[0] = matrix.x.apply(matrix, p1p);
  633. p1[1] = matrix.y.apply(matrix, p1p);
  634. p2[0] = matrix.x.apply(matrix, p2p);
  635. p2[1] = matrix.y.apply(matrix, p2p);
  636. p3[0] = matrix.x.apply(matrix, p3p);
  637. p3[1] = matrix.y.apply(matrix, p3p);
  638. p4[0] = matrix.x.apply(matrix, p4p);
  639. p4[1] = matrix.y.apply(matrix, p4p);
  640. return [p1, p2, p3, p4];
  641. },
  642. intersect: function (l1, l2) {
  643. var r1 = this.rect2pointArray(l1),
  644. r2 = this.rect2pointArray(l2);
  645. return !!Ext.draw.Draw.intersect(r1, r2).length;
  646. },
  647. drawHorizontalLabels: function () {
  648. var me = this,
  649. labelConf = me.label,
  650. floor = Math.floor,
  651. max = Math.max,
  652. axes = me.chart.axes,
  653. insetPadding = me.chart.insetPadding,
  654. position = me.position,
  655. inflections = me.inflections,
  656. ln = inflections.length,
  657. labels = me.labels,
  658. maxHeight = 0,
  659. ratio,
  660. bbox, point, prevLabel, prevLabelId,
  661. adjustEnd = me.adjustEnd,
  662. hasLeft = axes.findIndex('position', 'left') != -1,
  663. hasRight = axes.findIndex('position', 'right') != -1,
  664. textLabel, text,
  665. last, x, y, i, firstLabel;
  666. last = ln - 1;
  667. //get a reference to the first text label dimensions
  668. point = inflections[0];
  669. firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));
  670. ratio = Math.floor(Math.abs(Math.sin(labelConf.rotate &amp;&amp; (labelConf.rotate.degrees * Math.PI / 180) || 0)));
  671. for (i = 0; i &lt; ln; i++) {
  672. point = inflections[i];
  673. text = me.label.renderer(labels[i]);
  674. textLabel = me.getOrCreateLabel(i, text);
  675. bbox = textLabel._bbox;
  676. maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);
  677. x = floor(point[0] - (ratio ? bbox.height : bbox.width) / 2);
  678. if (adjustEnd &amp;&amp; me.chart.maxGutter[0] == 0) {
  679. if (i == 0 &amp;&amp; !hasLeft) {
  680. x = point[0];
  681. }
  682. else if (i == last &amp;&amp; !hasRight) {
  683. x = Math.min(x, point[0] - bbox.width + insetPadding);
  684. }
  685. }
  686. if (position == 'top') {
  687. y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);
  688. }
  689. else {
  690. y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);
  691. }
  692. textLabel.setAttributes({
  693. hidden: false,
  694. x: x,
  695. y: y
  696. }, true);
  697. // Skip label if there isn't available minimum space
  698. if (i != 0 &amp;&amp; (me.intersect(textLabel, prevLabel)
  699. || me.intersect(textLabel, firstLabel))) {
  700. if (i === last &amp;&amp; prevLabelId !== 0) {
  701. prevLabel.hide(true);
  702. } else {
  703. textLabel.hide(true);
  704. continue;
  705. }
  706. }
  707. prevLabel = textLabel;
  708. prevLabelId = i;
  709. }
  710. return maxHeight;
  711. },
  712. drawVerticalLabels: function () {
  713. var me = this,
  714. inflections = me.inflections,
  715. position = me.position,
  716. ln = inflections.length,
  717. chart = me.chart,
  718. insetPadding = chart.insetPadding,
  719. labels = me.labels,
  720. maxWidth = 0,
  721. max = Math.max,
  722. floor = Math.floor,
  723. ceil = Math.ceil,
  724. axes = me.chart.axes,
  725. gutterY = me.chart.maxGutter[1],
  726. bbox, point, prevLabel, prevLabelId,
  727. hasTop = axes.findIndex('position', 'top') != -1,
  728. hasBottom = axes.findIndex('position', 'bottom') != -1,
  729. adjustEnd = me.adjustEnd,
  730. textLabel, text,
  731. last = ln - 1, x, y, i;
  732. for (i = 0; i &lt; ln; i++) {
  733. point = inflections[i];
  734. text = me.label.renderer(labels[i]);
  735. textLabel = me.getOrCreateLabel(i, text);
  736. bbox = textLabel._bbox;
  737. maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);
  738. y = point[1];
  739. if (adjustEnd &amp;&amp; gutterY &lt; bbox.height / 2) {
  740. if (i == last &amp;&amp; !hasTop) {
  741. y = Math.max(y, me.y - me.length + ceil(bbox.height / 2) - insetPadding);
  742. }
  743. else if (i == 0 &amp;&amp; !hasBottom) {
  744. y = me.y + gutterY - floor(bbox.height / 2);
  745. }
  746. }
  747. if (position == 'left') {
  748. x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;
  749. }
  750. else {
  751. x = point[0] + me.dashSize + me.label.padding + 2;
  752. }
  753. textLabel.setAttributes(Ext.apply({
  754. hidden: false,
  755. x: x,
  756. y: y
  757. }, me.label), true);
  758. // Skip label if there isn't available minimum space
  759. if (i != 0 &amp;&amp; me.intersect(textLabel, prevLabel)) {
  760. if (i === last &amp;&amp; prevLabelId !== 0) {
  761. prevLabel.hide(true);
  762. } else {
  763. textLabel.hide(true);
  764. continue;
  765. }
  766. }
  767. prevLabel = textLabel;
  768. prevLabelId = i;
  769. }
  770. return maxWidth;
  771. },
  772. <span id='Ext-chart-axis-Axis-method-drawLabel'> /**
  773. </span> * Renders the labels in the axes.
  774. */
  775. drawLabel: function () {
  776. var me = this,
  777. position = me.position,
  778. labelGroup = me.labelGroup,
  779. inflections = me.inflections,
  780. maxWidth = 0,
  781. maxHeight = 0,
  782. ln, i;
  783. if (position == 'left' || position == 'right') {
  784. maxWidth = me.drawVerticalLabels();
  785. } else {
  786. maxHeight = me.drawHorizontalLabels();
  787. }
  788. // Hide unused bars
  789. ln = labelGroup.getCount();
  790. i = inflections.length;
  791. for (; i &lt; ln; i++) {
  792. labelGroup.getAt(i).hide(true);
  793. }
  794. me.bbox = {};
  795. Ext.apply(me.bbox, me.axisBBox);
  796. me.bbox.height = maxHeight;
  797. me.bbox.width = maxWidth;
  798. if (Ext.isString(me.title)) {
  799. me.drawTitle(maxWidth, maxHeight);
  800. }
  801. },
  802. <span id='Ext-chart-axis-Axis-method-setTitle'> /**
  803. </span> * Updates the {@link #title} of this axis.
  804. * @param {String} title
  805. */
  806. setTitle: function (title) {
  807. this.title = title;
  808. this.drawLabel();
  809. },
  810. // @private draws the title for the axis.
  811. drawTitle: function (maxWidth, maxHeight) {
  812. var me = this,
  813. position = me.position,
  814. surface = me.chart.surface,
  815. displaySprite = me.displaySprite,
  816. title = me.title,
  817. rotate = (position == 'left' || position == 'right'),
  818. x = me.x,
  819. y = me.y,
  820. base, bbox, pad;
  821. if (displaySprite) {
  822. displaySprite.setAttributes({text: title}, true);
  823. } else {
  824. base = {
  825. type: 'text',
  826. x: 0,
  827. y: 0,
  828. text: title
  829. };
  830. displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));
  831. surface.renderItem(displaySprite);
  832. }
  833. bbox = displaySprite.getBBox();
  834. pad = me.dashSize + me.label.padding;
  835. if (rotate) {
  836. y -= ((me.length / 2) - (bbox.height / 2));
  837. if (position == 'left') {
  838. x -= (maxWidth + pad + (bbox.width / 2));
  839. }
  840. else {
  841. x += (maxWidth + pad + bbox.width - (bbox.width / 2));
  842. }
  843. me.bbox.width += bbox.width + 10;
  844. }
  845. else {
  846. x += (me.length / 2) - (bbox.width * 0.5);
  847. if (position == 'top') {
  848. y -= (maxHeight + pad + (bbox.height * 0.3));
  849. }
  850. else {
  851. y += (maxHeight + pad + (bbox.height * 0.8));
  852. }
  853. me.bbox.height += bbox.height + 10;
  854. }
  855. displaySprite.setAttributes({
  856. translate: {
  857. x: x,
  858. y: y
  859. }
  860. }, true);
  861. }
  862. });
  863. </pre>
  864. </body>
  865. </html>