1
0

scale.logarithmic.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. 'use strict';
  2. module.exports = function(Chart) {
  3. var helpers = Chart.helpers;
  4. var defaultConfig = {
  5. position: 'left',
  6. // label settings
  7. ticks: {
  8. callback: Chart.Ticks.formatters.logarithmic
  9. }
  10. };
  11. var LogarithmicScale = Chart.Scale.extend({
  12. determineDataLimits: function() {
  13. var me = this;
  14. var opts = me.options;
  15. var tickOpts = opts.ticks;
  16. var chart = me.chart;
  17. var data = chart.data;
  18. var datasets = data.datasets;
  19. var getValueOrDefault = helpers.getValueOrDefault;
  20. var isHorizontal = me.isHorizontal();
  21. function IDMatches(meta) {
  22. return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
  23. }
  24. // Calculate Range
  25. me.min = null;
  26. me.max = null;
  27. me.minNotZero = null;
  28. if (opts.stacked) {
  29. var valuesPerType = {};
  30. helpers.each(datasets, function(dataset, datasetIndex) {
  31. var meta = chart.getDatasetMeta(datasetIndex);
  32. if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
  33. if (valuesPerType[meta.type] === undefined) {
  34. valuesPerType[meta.type] = [];
  35. }
  36. helpers.each(dataset.data, function(rawValue, index) {
  37. var values = valuesPerType[meta.type];
  38. var value = +me.getRightValue(rawValue);
  39. if (isNaN(value) || meta.data[index].hidden) {
  40. return;
  41. }
  42. values[index] = values[index] || 0;
  43. if (opts.relativePoints) {
  44. values[index] = 100;
  45. } else {
  46. // Don't need to split positive and negative since the log scale can't handle a 0 crossing
  47. values[index] += value;
  48. }
  49. });
  50. }
  51. });
  52. helpers.each(valuesPerType, function(valuesForType) {
  53. var minVal = helpers.min(valuesForType);
  54. var maxVal = helpers.max(valuesForType);
  55. me.min = me.min === null ? minVal : Math.min(me.min, minVal);
  56. me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
  57. });
  58. } else {
  59. helpers.each(datasets, function(dataset, datasetIndex) {
  60. var meta = chart.getDatasetMeta(datasetIndex);
  61. if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
  62. helpers.each(dataset.data, function(rawValue, index) {
  63. var value = +me.getRightValue(rawValue);
  64. if (isNaN(value) || meta.data[index].hidden) {
  65. return;
  66. }
  67. if (me.min === null) {
  68. me.min = value;
  69. } else if (value < me.min) {
  70. me.min = value;
  71. }
  72. if (me.max === null) {
  73. me.max = value;
  74. } else if (value > me.max) {
  75. me.max = value;
  76. }
  77. if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
  78. me.minNotZero = value;
  79. }
  80. });
  81. }
  82. });
  83. }
  84. me.min = getValueOrDefault(tickOpts.min, me.min);
  85. me.max = getValueOrDefault(tickOpts.max, me.max);
  86. if (me.min === me.max) {
  87. if (me.min !== 0 && me.min !== null) {
  88. me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
  89. me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
  90. } else {
  91. me.min = 1;
  92. me.max = 10;
  93. }
  94. }
  95. },
  96. buildTicks: function() {
  97. var me = this;
  98. var opts = me.options;
  99. var tickOpts = opts.ticks;
  100. var generationOptions = {
  101. min: tickOpts.min,
  102. max: tickOpts.max
  103. };
  104. var ticks = me.ticks = Chart.Ticks.generators.logarithmic(generationOptions, me);
  105. if (!me.isHorizontal()) {
  106. // We are in a vertical orientation. The top value is the highest. So reverse the array
  107. ticks.reverse();
  108. }
  109. // At this point, we need to update our max and min given the tick values since we have expanded the
  110. // range of the scale
  111. me.max = helpers.max(ticks);
  112. me.min = helpers.min(ticks);
  113. if (tickOpts.reverse) {
  114. ticks.reverse();
  115. me.start = me.max;
  116. me.end = me.min;
  117. } else {
  118. me.start = me.min;
  119. me.end = me.max;
  120. }
  121. },
  122. convertTicksToLabels: function() {
  123. this.tickValues = this.ticks.slice();
  124. Chart.Scale.prototype.convertTicksToLabels.call(this);
  125. },
  126. // Get the correct tooltip label
  127. getLabelForIndex: function(index, datasetIndex) {
  128. return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
  129. },
  130. getPixelForTick: function(index) {
  131. return this.getPixelForValue(this.tickValues[index]);
  132. },
  133. getPixelForValue: function(value) {
  134. var me = this;
  135. var innerDimension;
  136. var pixel;
  137. var start = me.start;
  138. var newVal = +me.getRightValue(value);
  139. var range;
  140. var opts = me.options;
  141. var tickOpts = opts.ticks;
  142. if (me.isHorizontal()) {
  143. range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
  144. if (newVal === 0) {
  145. pixel = me.left;
  146. } else {
  147. innerDimension = me.width;
  148. pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
  149. }
  150. } else {
  151. // Bottom - top since pixels increase downward on a screen
  152. innerDimension = me.height;
  153. if (start === 0 && !tickOpts.reverse) {
  154. range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
  155. if (newVal === start) {
  156. pixel = me.bottom;
  157. } else if (newVal === me.minNotZero) {
  158. pixel = me.bottom - innerDimension * 0.02;
  159. } else {
  160. pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
  161. }
  162. } else if (me.end === 0 && tickOpts.reverse) {
  163. range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
  164. if (newVal === me.end) {
  165. pixel = me.top;
  166. } else if (newVal === me.minNotZero) {
  167. pixel = me.top + innerDimension * 0.02;
  168. } else {
  169. pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
  170. }
  171. } else {
  172. range = helpers.log10(me.end) - helpers.log10(start);
  173. innerDimension = me.height;
  174. pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
  175. }
  176. }
  177. return pixel;
  178. },
  179. getValueForPixel: function(pixel) {
  180. var me = this;
  181. var range = helpers.log10(me.end) - helpers.log10(me.start);
  182. var value, innerDimension;
  183. if (me.isHorizontal()) {
  184. innerDimension = me.width;
  185. value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
  186. } else { // todo: if start === 0
  187. innerDimension = me.height;
  188. value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
  189. }
  190. return value;
  191. }
  192. });
  193. Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
  194. };