4be5744f39299a643ec8dcebac69c1d00b6d043d.svn-base 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. if (!dojo._hasResource["dojox.charting.axis2d.Default"]) { // _hasResource
  2. // checks added by
  3. // build. Do not use
  4. // _hasResource
  5. // directly in your
  6. // code.
  7. dojo._hasResource["dojox.charting.axis2d.Default"] = true;
  8. dojo.provide("dojox.charting.axis2d.Default");
  9. dojo.require("dojox.charting.scaler");
  10. dojo.require("dojox.charting.axis2d.common");
  11. dojo.require("dojox.charting.axis2d.Base");
  12. dojo.require("dojo.colors");
  13. dojo.require("dojox.gfx");
  14. dojo.require("dojox.lang.functional");
  15. dojo.require("dojox.lang.utils");
  16. (function() {
  17. var dc = dojox.charting, df = dojox.lang.functional, du = dojox.lang.utils, g = dojox.gfx, labelGap = 4, // in
  18. // pixels
  19. labelFudgeFactor = 0.8; // in percents (to convert font's heigth to
  20. // label width)
  21. var eq = function(/* Number */a, /* Number */b) {
  22. // summary: compare two FP numbers for equality
  23. return Math.abs(a - b) <= 1e-6 * (Math.abs(a) + Math.abs(b)); // Boolean
  24. };
  25. dojo.declare("dojox.charting.axis2d.Default",
  26. dojox.charting.axis2d.Base, {
  27. defaultParams : {
  28. vertical : false, // true for vertical axis
  29. fixUpper : "none", // align the upper on ticks:
  30. // "major", "minor", "micro", "none"
  31. fixLower : "none", // align the lower on ticks:
  32. // "major", "minor", "micro", "none"
  33. natural : false, // all tick marks should be made on
  34. // natural numbers
  35. leftBottom : true, // position of the axis, used with
  36. // "vertical"
  37. includeZero : false, // 0 should be included
  38. fixed : true, // all labels are fixed numbers
  39. majorLabels : true, // draw major labels
  40. minorTicks : true, // draw minor ticks
  41. minorLabels : true, // draw minor labels
  42. microTicks : false, // draw micro ticks
  43. htmlLabels : true
  44. // use HTML to draw labels
  45. },
  46. optionalParams : {
  47. "min" : 0, // minimal value on this axis
  48. "max" : 1, // maximal value on this axis
  49. "majorTickStep" : 4, // major tick step
  50. "minorTickStep" : 2, // minor tick step
  51. "microTickStep" : 1, // micro tick step
  52. "labels" : [], // array of labels for major ticks
  53. // with corresponding numeric values
  54. // ordered by values
  55. // theme components
  56. "stroke" : {}, // stroke for an axis
  57. "majorTick" : {}, // stroke + length for a tick
  58. "minorTick" : {}, // stroke + length for a tick
  59. "font" : "", // font for labels
  60. "fontColor" : "" // color for labels as a string
  61. },
  62. constructor : function(chart, kwArgs) {
  63. this.opt = dojo.clone(this.defaultParams);
  64. du.updateWithObject(this.opt, kwArgs);
  65. du.updateWithPattern(this.opt, kwArgs,
  66. this.optionalParams);
  67. },
  68. dependOnData : function() {
  69. return !("min" in this.opt) || !("max" in this.opt);
  70. },
  71. clear : function() {
  72. delete this.scaler;
  73. this.dirty = true;
  74. return this;
  75. },
  76. initialized : function() {
  77. return "scaler" in this;
  78. },
  79. calculate : function(min, max, span, labels) {
  80. if (this.initialized()) {
  81. return this;
  82. }
  83. this.labels = "labels" in this.opt
  84. ? this.opt.labels
  85. : labels;
  86. if ("min" in this.opt) {
  87. min = this.opt.min;
  88. }
  89. if ("max" in this.opt) {
  90. max = this.opt.max;
  91. }
  92. if (this.opt.includeZero) {
  93. if (min > 0) {
  94. min = 0;
  95. }
  96. if (max < 0) {
  97. max = 0;
  98. }
  99. }
  100. var minMinorStep = 0, ta = this.chart.theme.axis, taFont = "font" in this.opt
  101. ? this.opt.font
  102. : ta.font, size = taFont ? g.normalizedLength(g
  103. .splitFontString(taFont).size) : 0;
  104. if (this.vertical) {
  105. if (size) {
  106. minMinorStep = size + labelGap;
  107. }
  108. } else {
  109. if (size) {
  110. var labelLength = Math.ceil(Math.log(Math.max(
  111. Math.abs(min), Math.abs(max)))
  112. / Math.LN10);
  113. if (min < 0 || max < 0) {
  114. ++labelLength;
  115. }
  116. var precision = Math.floor(Math.log(max - min)
  117. / Math.LN10);
  118. if (precision > 0) {
  119. labelLength += precision;
  120. }
  121. if (this.labels) {
  122. labelLength = df.foldl(df.map(this.labels,
  123. "x.text.length"),
  124. "Math.max(a, b)", labelLength);
  125. }
  126. minMinorStep = Math.floor(size * labelLength
  127. * labelFudgeFactor)
  128. + labelGap;
  129. }
  130. }
  131. var kwArgs = {
  132. fixUpper : this.opt.fixUpper,
  133. fixLower : this.opt.fixLower,
  134. natural : this.opt.natural
  135. };
  136. if ("majorTickStep" in this.opt) {
  137. kwArgs.majorTick = this.opt.majorTickStep;
  138. }
  139. if ("minorTickStep" in this.opt) {
  140. kwArgs.minorTick = this.opt.minorTickStep;
  141. }
  142. if ("microTickStep" in this.opt) {
  143. kwArgs.microTick = this.opt.microTickStep;
  144. }
  145. this.scaler = dojox.charting.scaler(min, max, span,
  146. kwArgs);
  147. this.scaler.minMinorStep = minMinorStep;
  148. return this;
  149. },
  150. getScaler : function() {
  151. return this.scaler;
  152. },
  153. getOffsets : function() {
  154. var offsets = {
  155. l : 0,
  156. r : 0,
  157. t : 0,
  158. b : 0
  159. };
  160. var offset = 0, ta = this.chart.theme.axis, taFont = "font" in this.opt
  161. ? this.opt.font
  162. : ta.font, taMajorTick = "majorTick" in this.opt
  163. ? this.opt.majorTick
  164. : ta.majorTick, taMinorTick = "minorTick" in this.opt
  165. ? this.opt.minorTick
  166. : ta.minorTick, size = taFont
  167. ? g
  168. .normalizedLength(g
  169. .splitFontString(taFont).size)
  170. : 0;
  171. if (this.vertical) {
  172. if (size) {
  173. var s = this.scaler, a = this._getLabel(
  174. s.major.start, s.major.prec).length, b = this
  175. ._getLabel(s.major.start
  176. + s.major.count
  177. * s.major.tick,
  178. s.major.prec).length, c = this
  179. ._getLabel(s.minor.start, s.minor.prec).length, d = this
  180. ._getLabel(s.minor.start
  181. + s.minor.count
  182. * s.minor.tick,
  183. s.minor.prec).length, labelLength = Math
  184. .max(a, b, c, d);
  185. if (this.labels) {
  186. labelLength = df.foldl(df.map(this.labels,
  187. "x.text.length"),
  188. "Math.max(a, b)", labelLength);
  189. }
  190. offset = Math.floor(size * labelLength
  191. * labelFudgeFactor)
  192. + labelGap;
  193. }
  194. offset += labelGap
  195. + Math.max(taMajorTick.length,
  196. taMinorTick.length);
  197. offsets[this.opt.leftBottom ? "l" : "r"] = offset;
  198. offsets.t = offsets.b = size / 2;
  199. } else {
  200. if (size) {
  201. offset = size + labelGap;
  202. }
  203. offset += labelGap
  204. + Math.max(taMajorTick.length,
  205. taMinorTick.length);
  206. offsets[this.opt.leftBottom ? "b" : "t"] = offset;
  207. if (size) {
  208. var s = this.scaler, a = this._getLabel(
  209. s.major.start, s.major.prec).length, b = this
  210. ._getLabel(s.major.start
  211. + s.major.count
  212. * s.major.tick,
  213. s.major.prec).length, c = this
  214. ._getLabel(s.minor.start, s.minor.prec).length, d = this
  215. ._getLabel(s.minor.start
  216. + s.minor.count
  217. * s.minor.tick,
  218. s.minor.prec).length, labelLength = Math
  219. .max(a, b, c, d);
  220. if (this.labels) {
  221. labelLength = df.foldl(df.map(this.labels,
  222. "x.text.length"),
  223. "Math.max(a, b)", labelLength);
  224. }
  225. offsets.l = offsets.r = Math.floor(size
  226. * labelLength * labelFudgeFactor)
  227. / 2;
  228. }
  229. }
  230. return offsets;
  231. },
  232. render : function(dim, offsets) {
  233. if (!this.dirty) {
  234. return this;
  235. }
  236. // prepare variable
  237. var start, stop, axisVector, tickVector, labelOffset, labelAlign, ta = this.chart.theme.axis, taStroke = "stroke" in this.opt
  238. ? this.opt.stroke
  239. : ta.stroke, taMajorTick = "majorTick" in this.opt
  240. ? this.opt.majorTick
  241. : ta.majorTick, taMinorTick = "minorTick" in this.opt
  242. ? this.opt.minorTick
  243. : ta.minorTick, taFont = "font" in this.opt
  244. ? this.opt.font
  245. : ta.font, taFontColor = "fontColor" in this.opt
  246. ? this.opt.fontColor
  247. : ta.fontColor, tickSize = Math.max(
  248. taMajorTick.length, taMinorTick.length), size = taFont
  249. ? g
  250. .normalizedLength(g
  251. .splitFontString(taFont).size)
  252. : 0;
  253. if (this.vertical) {
  254. start = {
  255. y : dim.height - offsets.b
  256. };
  257. stop = {
  258. y : offsets.t
  259. };
  260. axisVector = {
  261. x : 0,
  262. y : -1
  263. };
  264. if (this.opt.leftBottom) {
  265. start.x = stop.x = offsets.l;
  266. tickVector = {
  267. x : -1,
  268. y : 0
  269. };
  270. labelAlign = "end";
  271. } else {
  272. start.x = stop.x = dim.width - offsets.r;
  273. tickVector = {
  274. x : 1,
  275. y : 0
  276. };
  277. labelAlign = "start";
  278. }
  279. labelOffset = {
  280. x : tickVector.x * (tickSize + labelGap),
  281. y : size * 0.4
  282. };
  283. } else {
  284. start = {
  285. x : offsets.l
  286. };
  287. stop = {
  288. x : dim.width - offsets.r
  289. };
  290. axisVector = {
  291. x : 1,
  292. y : 0
  293. };
  294. labelAlign = "middle";
  295. if (this.opt.leftBottom) {
  296. start.y = stop.y = dim.height - offsets.b;
  297. tickVector = {
  298. x : 0,
  299. y : 1
  300. };
  301. labelOffset = {
  302. y : tickSize + labelGap + size
  303. };
  304. } else {
  305. start.y = stop.y = offsets.t;
  306. tickVector = {
  307. x : 0,
  308. y : -1
  309. };
  310. labelOffset = {
  311. y : -tickSize - labelGap
  312. };
  313. }
  314. labelOffset.x = 0;
  315. }
  316. // render shapes
  317. this.cleanGroup();
  318. var s = this.group, c = this.scaler, step, next, nextMajor = c.major.start, nextMinor = c.minor.start, nextMicro = c.micro.start;
  319. s.createLine({
  320. x1 : start.x,
  321. y1 : start.y,
  322. x2 : stop.x,
  323. y2 : stop.y
  324. }).setStroke(taStroke);
  325. if (this.opt.microTicks && c.micro.tick) {
  326. step = c.micro.tick, next = nextMicro;
  327. } else if (this.opt.minorTicks && c.minor.tick) {
  328. step = c.minor.tick, next = nextMinor;
  329. } else if (c.major.tick) {
  330. step = c.major.tick, next = nextMajor;
  331. } else {
  332. // don't draw anything
  333. return this;
  334. }
  335. while (next <= c.bounds.upper + 1 / c.scale) {
  336. var offset = (next - c.bounds.lower) * c.scale, x = start.x
  337. + axisVector.x * offset, y = start.y
  338. + axisVector.y * offset;
  339. if (Math.abs(nextMajor - next) < step / 2) {
  340. // major tick
  341. s.createLine({
  342. x1 : x,
  343. y1 : y,
  344. x2 : x + tickVector.x
  345. * taMajorTick.length,
  346. y2 : y + tickVector.y
  347. * taMajorTick.length
  348. }).setStroke(taMajorTick);
  349. if (this.opt.majorLabels) {
  350. var elem = dc.axis2d.common.createText[this.opt.htmlLabels
  351. ? "html"
  352. : "gfx"](this.chart, s, x
  353. + labelOffset.x, y
  354. + labelOffset.y,
  355. labelAlign, this._getLabel(
  356. nextMajor, c.major.prec),
  357. taFont, taFontColor);
  358. if (this.opt.htmlLabels) {
  359. this.htmlElements.push(elem);
  360. }
  361. }
  362. nextMajor += c.major.tick;
  363. nextMinor += c.minor.tick;
  364. nextMicro += c.micro.tick;
  365. } else if (Math.abs(nextMinor - next) < step / 2) {
  366. // minor tick
  367. if (this.opt.minorTicks) {
  368. s.createLine({
  369. x1 : x,
  370. y1 : y,
  371. x2 : x + tickVector.x
  372. * taMinorTick.length,
  373. y2 : y + tickVector.y
  374. * taMinorTick.length
  375. }).setStroke(taMinorTick);
  376. if (this.opt.minorLabels
  377. && (c.minMinorStep <= c.minor.tick
  378. * c.scale)) {
  379. var elem = dc.axis2d.common.createText[this.opt.htmlLabels
  380. ? "html"
  381. : "gfx"](this.chart, s, x
  382. + labelOffset.x, y
  383. + labelOffset.y,
  384. labelAlign, this
  385. ._getLabel(nextMinor,
  386. c.minor.prec),
  387. taFont, taFontColor);
  388. if (this.opt.htmlLabels) {
  389. this.htmlElements.push(elem);
  390. }
  391. }
  392. }
  393. nextMinor += c.minor.tick;
  394. nextMicro += c.micro.tick;
  395. } else {
  396. // micro tick
  397. if (this.opt.microTicks) {
  398. s.createLine({
  399. x1 : x,
  400. y1 : y,
  401. // use minor ticks for now
  402. x2 : x + tickVector.x
  403. * taMinorTick.length,
  404. y2 : y + tickVector.y
  405. * taMinorTick.length
  406. }).setStroke(taMinorTick);
  407. }
  408. nextMicro += c.micro.tick;
  409. }
  410. next += step;
  411. }
  412. this.dirty = false;
  413. return this;
  414. },
  415. // utilities
  416. _getLabel : function(number, precision) {
  417. if (this.opt.labels) {
  418. // classic binary search
  419. var l = this.opt.labels, lo = 0, hi = l.length;
  420. while (lo < hi) {
  421. var mid = Math.floor((lo + hi) / 2), val = l[mid].value;
  422. if (val < number) {
  423. lo = mid + 1;
  424. } else {
  425. hi = mid;
  426. }
  427. }
  428. // lets take into account FP errors
  429. if (lo < l.length && eq(l[lo].value, number)) {
  430. return l[lo].text;
  431. }
  432. --lo;
  433. if (lo < l.length && eq(l[lo].value, number)) {
  434. return l[lo].text;
  435. }
  436. lo += 2;
  437. if (lo < l.length && eq(l[lo].value, number)) {
  438. return l[lo].text;
  439. }
  440. // otherwise we will produce a number
  441. }
  442. return this.opt.fixed ? number.toFixed(precision < 0
  443. ? -precision
  444. : 0) : number.toString();
  445. }
  446. });
  447. })();
  448. }