Invisible.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.charting.axis2d.Invisible"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.charting.axis2d.Invisible"] = true;
  8. dojo.provide("dojox.charting.axis2d.Invisible");
  9. dojo.require("dojox.charting.scaler.linear");
  10. dojo.require("dojox.charting.axis2d.common");
  11. dojo.require("dojox.charting.axis2d.Base");
  12. dojo.require("dojo.string");
  13. dojo.require("dojox.gfx");
  14. dojo.require("dojox.lang.functional");
  15. dojo.require("dojox.lang.utils");
  16. (function(){
  17. var dc = dojox.charting,
  18. df = dojox.lang.functional,
  19. du = dojox.lang.utils,
  20. g = dojox.gfx,
  21. lin = dc.scaler.linear,
  22. merge = du.merge,
  23. labelGap = 4, // in pixels
  24. centerAnchorLimit = 45; // in degrees
  25. dojo.declare("dojox.charting.axis2d.Invisible", dojox.charting.axis2d.Base, {
  26. // summary:
  27. // The default axis object used in dojox.charting. See dojox.charting.Chart2D.addAxis for details.
  28. //
  29. // defaultParams: Object
  30. // The default parameters used to define any axis.
  31. // optionalParams: Object
  32. // Any optional parameters needed to define an axis.
  33. /*
  34. // TODO: the documentation tools need these to be pre-defined in order to pick them up
  35. // correctly, but the code here is partially predicated on whether or not the properties
  36. // actually exist. For now, we will leave these undocumented but in the code for later. -- TRT
  37. // opt: Object
  38. // The actual options used to define this axis, created at initialization.
  39. // scalar: Object
  40. // The calculated helper object to tell charts how to draw an axis and any data.
  41. // ticks: Object
  42. // The calculated tick object that helps a chart draw the scaling on an axis.
  43. // dirty: Boolean
  44. // The state of the axis (whether it needs to be redrawn or not)
  45. // scale: Number
  46. // The current scale of the axis.
  47. // offset: Number
  48. // The current offset of the axis.
  49. opt: null,
  50. scalar: null,
  51. ticks: null,
  52. dirty: true,
  53. scale: 1,
  54. offset: 0,
  55. */
  56. defaultParams: {
  57. vertical: false, // true for vertical axis
  58. fixUpper: "none", // align the upper on ticks: "major", "minor", "micro", "none"
  59. fixLower: "none", // align the lower on ticks: "major", "minor", "micro", "none"
  60. natural: false, // all tick marks should be made on natural numbers
  61. leftBottom: true, // position of the axis, used with "vertical"
  62. includeZero: false, // 0 should be included
  63. fixed: true, // all labels are fixed numbers
  64. majorLabels: true, // draw major labels
  65. minorTicks: true, // draw minor ticks
  66. minorLabels: true, // draw minor labels
  67. microTicks: false, // draw micro ticks
  68. rotation: 0 // label rotation angle in degrees
  69. },
  70. optionalParams: {
  71. min: 0, // minimal value on this axis
  72. max: 1, // maximal value on this axis
  73. from: 0, // visible from this value
  74. to: 1, // visible to this value
  75. majorTickStep: 4, // major tick step
  76. minorTickStep: 2, // minor tick step
  77. microTickStep: 1, // micro tick step
  78. labels: [], // array of labels for major ticks
  79. // with corresponding numeric values
  80. // ordered by values
  81. labelFunc: null, // function to compute label values
  82. maxLabelSize: 0, // size in px. For use with labelFunc
  83. maxLabelCharCount: 0, // size in word count.
  84. trailingSymbol: null
  85. // TODO: add support for minRange!
  86. // minRange: 1, // smallest distance from min allowed on the axis
  87. },
  88. constructor: function(chart, kwArgs){
  89. // summary:
  90. // The constructor for an axis.
  91. // chart: dojox.charting.Chart2D
  92. // The chart the axis belongs to.
  93. // kwArgs: dojox.charting.axis2d.__AxisCtorArgs?
  94. // Any optional keyword arguments to be used to define this axis.
  95. this.opt = dojo.clone(this.defaultParams);
  96. du.updateWithObject(this.opt, kwArgs);
  97. du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
  98. },
  99. dependOnData: function(){
  100. // summary:
  101. // Find out whether or not the axis options depend on the data in the axis.
  102. return !("min" in this.opt) || !("max" in this.opt); // Boolean
  103. },
  104. clear: function(){
  105. // summary:
  106. // Clear out all calculated properties on this axis;
  107. // returns: dojox.charting.axis2d.Default
  108. // The reference to the axis for functional chaining.
  109. delete this.scaler;
  110. delete this.ticks;
  111. this.dirty = true;
  112. return this; // dojox.charting.axis2d.Default
  113. },
  114. initialized: function(){
  115. // summary:
  116. // Finds out if this axis has been initialized or not.
  117. // returns: Boolean
  118. // Whether a scaler has been calculated and if the axis is not dirty.
  119. return "scaler" in this && !(this.dirty && this.dependOnData());
  120. },
  121. setWindow: function(scale, offset){
  122. // summary:
  123. // Set the drawing "window" for the axis.
  124. // scale: Number
  125. // The new scale for the axis.
  126. // offset: Number
  127. // The new offset for the axis.
  128. // returns: dojox.charting.axis2d.Default
  129. // The reference to the axis for functional chaining.
  130. this.scale = scale;
  131. this.offset = offset;
  132. return this.clear(); // dojox.charting.axis2d.Default
  133. },
  134. getWindowScale: function(){
  135. // summary:
  136. // Get the current windowing scale of the axis.
  137. return "scale" in this ? this.scale : 1; // Number
  138. },
  139. getWindowOffset: function(){
  140. // summary:
  141. // Get the current windowing offset for the axis.
  142. return "offset" in this ? this.offset : 0; // Number
  143. },
  144. _groupLabelWidth: function(labels, font, wcLimit){
  145. if(!labels.length){
  146. return 0;
  147. }
  148. if(dojo.isObject(labels[0])){
  149. labels = df.map(labels, function(label){ return label.text; });
  150. }
  151. if (wcLimit) {
  152. labels = df.map(labels, function(label){
  153. return dojo.trim(label).length == 0 ? "" : label.substring(0, wcLimit) + this.trailingSymbol;
  154. }, this);
  155. }
  156. var s = labels.join("<br>");
  157. return dojox.gfx._base._getTextBox(s, {font: font}).w || 0;
  158. },
  159. calculate: function(min, max, span, labels){
  160. // summary:
  161. // Perform all calculations needed to render this axis.
  162. // min: Number
  163. // The smallest value represented on this axis.
  164. // max: Number
  165. // The largest value represented on this axis.
  166. // span: Number
  167. // The span in pixels over which axis calculations are made.
  168. // labels: String[]
  169. // Optional list of labels.
  170. // returns: dojox.charting.axis2d.Default
  171. // The reference to the axis for functional chaining.
  172. if(this.initialized()){
  173. return this;
  174. }
  175. var o = this.opt;
  176. this.labels = "labels" in o ? o.labels : labels;
  177. this.scaler = lin.buildScaler(min, max, span, o);
  178. var tsb = this.scaler.bounds;
  179. if("scale" in this){
  180. // calculate new range
  181. o.from = tsb.lower + this.offset;
  182. o.to = (tsb.upper - tsb.lower) / this.scale + o.from;
  183. // make sure that bounds are correct
  184. if( !isFinite(o.from) ||
  185. isNaN(o.from) ||
  186. !isFinite(o.to) ||
  187. isNaN(o.to) ||
  188. o.to - o.from >= tsb.upper - tsb.lower
  189. ){
  190. // any error --- remove from/to bounds
  191. delete o.from;
  192. delete o.to;
  193. delete this.scale;
  194. delete this.offset;
  195. }else{
  196. // shift the window, if we are out of bounds
  197. if(o.from < tsb.lower){
  198. o.to += tsb.lower - o.from;
  199. o.from = tsb.lower;
  200. }else if(o.to > tsb.upper){
  201. o.from += tsb.upper - o.to;
  202. o.to = tsb.upper;
  203. }
  204. // update the offset
  205. this.offset = o.from - tsb.lower;
  206. }
  207. // re-calculate the scaler
  208. this.scaler = lin.buildScaler(min, max, span, o);
  209. tsb = this.scaler.bounds;
  210. // cleanup
  211. if(this.scale == 1 && this.offset == 0){
  212. delete this.scale;
  213. delete this.offset;
  214. }
  215. }
  216. var ta = this.chart.theme.axis, labelWidth = 0, rotation = o.rotation % 360,
  217. // TODO: we use one font --- of major tick, we need to use major and minor fonts
  218. taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font),
  219. size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0,
  220. cosr = Math.abs(Math.cos(rotation * Math.PI / 180)),
  221. sinr = Math.abs(Math.sin(rotation * Math.PI / 180));
  222. if(rotation < 0){
  223. rotation += 360;
  224. }
  225. if(size){
  226. if(this.vertical ? rotation != 0 && rotation != 180 : rotation != 90 && rotation != 270){
  227. // we need width of all labels
  228. if(this.labels){
  229. labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount);
  230. }else{
  231. var labelLength = Math.ceil(
  232. Math.log(
  233. Math.max(
  234. Math.abs(tsb.from),
  235. Math.abs(tsb.to)
  236. )
  237. ) / Math.LN10
  238. ),
  239. t = [];
  240. if(tsb.from < 0 || tsb.to < 0){
  241. t.push("-");
  242. }
  243. t.push(dojo.string.rep("9", labelLength));
  244. var precision = Math.floor(
  245. Math.log( tsb.to - tsb.from ) / Math.LN10
  246. );
  247. if(precision > 0){
  248. t.push(".");
  249. t.push(dojo.string.rep("9", precision));
  250. }
  251. labelWidth = dojox.gfx._base._getTextBox(
  252. t.join(""),
  253. { font: taFont }
  254. ).w;
  255. }
  256. labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth;
  257. }else{
  258. labelWidth = size;
  259. }
  260. switch(rotation){
  261. case 0:
  262. case 90:
  263. case 180:
  264. case 270:
  265. // trivial cases: use labelWidth
  266. break;
  267. default:
  268. // rotated labels
  269. var gap1 = Math.sqrt(labelWidth * labelWidth + size * size), // short labels
  270. gap2 = this.vertical ? size * cosr + labelWidth * sinr : labelWidth * cosr + size * sinr; // slanted labels
  271. labelWidth = Math.min(gap1, gap2);
  272. break;
  273. }
  274. }
  275. this.scaler.minMinorStep = labelWidth + labelGap;
  276. this.ticks = lin.buildTicks(this.scaler, o);
  277. return this; // dojox.charting.axis2d.Default
  278. },
  279. getScaler: function(){
  280. // summary:
  281. // Get the pre-calculated scaler object.
  282. return this.scaler; // Object
  283. },
  284. getTicks: function(){
  285. // summary:
  286. // Get the pre-calculated ticks object.
  287. return this.ticks; // Object
  288. }
  289. });
  290. })();
  291. }