1 | define(["dojo/_base/lang", "./common"], |
---|
2 | function(lang, common){ |
---|
3 | var linear = lang.getObject("dojox.charting.scaler.linear", true); |
---|
4 | |
---|
5 | var deltaLimit = 3, // pixels |
---|
6 | getLabel = common.getNumericLabel; |
---|
7 | |
---|
8 | function findString(/*String*/ val, /*Array*/ text){ |
---|
9 | val = val.toLowerCase(); |
---|
10 | for(var i = text.length - 1; i >= 0; --i){ |
---|
11 | if(val === text[i]){ |
---|
12 | return true; |
---|
13 | } |
---|
14 | } |
---|
15 | return false; |
---|
16 | } |
---|
17 | |
---|
18 | var calcTicks = function(min, max, kwArgs, majorTick, minorTick, microTick, span){ |
---|
19 | kwArgs = lang.delegate(kwArgs); |
---|
20 | if(!majorTick){ |
---|
21 | if(kwArgs.fixUpper == "major"){ kwArgs.fixUpper = "minor"; } |
---|
22 | if(kwArgs.fixLower == "major"){ kwArgs.fixLower = "minor"; } |
---|
23 | } |
---|
24 | if(!minorTick){ |
---|
25 | if(kwArgs.fixUpper == "minor"){ kwArgs.fixUpper = "micro"; } |
---|
26 | if(kwArgs.fixLower == "minor"){ kwArgs.fixLower = "micro"; } |
---|
27 | } |
---|
28 | if(!microTick){ |
---|
29 | if(kwArgs.fixUpper == "micro"){ kwArgs.fixUpper = "none"; } |
---|
30 | if(kwArgs.fixLower == "micro"){ kwArgs.fixLower = "none"; } |
---|
31 | } |
---|
32 | var lowerBound = findString(kwArgs.fixLower, ["major"]) ? |
---|
33 | Math.floor(kwArgs.min / majorTick) * majorTick : |
---|
34 | findString(kwArgs.fixLower, ["minor"]) ? |
---|
35 | Math.floor(kwArgs.min / minorTick) * minorTick : |
---|
36 | findString(kwArgs.fixLower, ["micro"]) ? |
---|
37 | Math.floor(kwArgs.min / microTick) * microTick : kwArgs.min, |
---|
38 | upperBound = findString(kwArgs.fixUpper, ["major"]) ? |
---|
39 | Math.ceil(kwArgs.max / majorTick) * majorTick : |
---|
40 | findString(kwArgs.fixUpper, ["minor"]) ? |
---|
41 | Math.ceil(kwArgs.max / minorTick) * minorTick : |
---|
42 | findString(kwArgs.fixUpper, ["micro"]) ? |
---|
43 | Math.ceil(kwArgs.max / microTick) * microTick : kwArgs.max; |
---|
44 | |
---|
45 | if(kwArgs.useMin){ min = lowerBound; } |
---|
46 | if(kwArgs.useMax){ max = upperBound; } |
---|
47 | |
---|
48 | var majorStart = (!majorTick || kwArgs.useMin && findString(kwArgs.fixLower, ["major"])) ? |
---|
49 | min : Math.ceil(min / majorTick) * majorTick, |
---|
50 | minorStart = (!minorTick || kwArgs.useMin && findString(kwArgs.fixLower, ["major", "minor"])) ? |
---|
51 | min : Math.ceil(min / minorTick) * minorTick, |
---|
52 | microStart = (! microTick || kwArgs.useMin && findString(kwArgs.fixLower, ["major", "minor", "micro"])) ? |
---|
53 | min : Math.ceil(min / microTick) * microTick, |
---|
54 | majorCount = !majorTick ? 0 : (kwArgs.useMax && findString(kwArgs.fixUpper, ["major"]) ? |
---|
55 | Math.round((max - majorStart) / majorTick) : |
---|
56 | Math.floor((max - majorStart) / majorTick)) + 1, |
---|
57 | minorCount = !minorTick ? 0 : (kwArgs.useMax && findString(kwArgs.fixUpper, ["major", "minor"]) ? |
---|
58 | Math.round((max - minorStart) / minorTick) : |
---|
59 | Math.floor((max - minorStart) / minorTick)) + 1, |
---|
60 | microCount = !microTick ? 0 : (kwArgs.useMax && findString(kwArgs.fixUpper, ["major", "minor", "micro"]) ? |
---|
61 | Math.round((max - microStart) / microTick) : |
---|
62 | Math.floor((max - microStart) / microTick)) + 1, |
---|
63 | minorPerMajor = minorTick ? Math.round(majorTick / minorTick) : 0, |
---|
64 | microPerMinor = microTick ? Math.round(minorTick / microTick) : 0, |
---|
65 | majorPrecision = majorTick ? Math.floor(Math.log(majorTick) / Math.LN10) : 0, |
---|
66 | minorPrecision = minorTick ? Math.floor(Math.log(minorTick) / Math.LN10) : 0, |
---|
67 | scale = span / (max - min); |
---|
68 | if(!isFinite(scale)){ scale = 1; } |
---|
69 | |
---|
70 | return { |
---|
71 | bounds: { |
---|
72 | lower: lowerBound, |
---|
73 | upper: upperBound, |
---|
74 | from: min, |
---|
75 | to: max, |
---|
76 | scale: scale, |
---|
77 | span: span |
---|
78 | }, |
---|
79 | major: { |
---|
80 | tick: majorTick, |
---|
81 | start: majorStart, |
---|
82 | count: majorCount, |
---|
83 | prec: majorPrecision |
---|
84 | }, |
---|
85 | minor: { |
---|
86 | tick: minorTick, |
---|
87 | start: minorStart, |
---|
88 | count: minorCount, |
---|
89 | prec: minorPrecision |
---|
90 | }, |
---|
91 | micro: { |
---|
92 | tick: microTick, |
---|
93 | start: microStart, |
---|
94 | count: microCount, |
---|
95 | prec: 0 |
---|
96 | }, |
---|
97 | minorPerMajor: minorPerMajor, |
---|
98 | microPerMinor: microPerMinor, |
---|
99 | scaler: linear |
---|
100 | }; |
---|
101 | }; |
---|
102 | |
---|
103 | return lang.mixin(linear, { |
---|
104 | buildScaler: function(/*Number*/ min, /*Number*/ max, /*Number*/ span, /*Object*/ kwArgs, /*Number?*/ delta, /*Number?*/ minorDelta){ |
---|
105 | var h = {fixUpper: "none", fixLower: "none", natural: false}; |
---|
106 | if(kwArgs){ |
---|
107 | if("fixUpper" in kwArgs){ h.fixUpper = String(kwArgs.fixUpper); } |
---|
108 | if("fixLower" in kwArgs){ h.fixLower = String(kwArgs.fixLower); } |
---|
109 | if("natural" in kwArgs){ h.natural = Boolean(kwArgs.natural); } |
---|
110 | } |
---|
111 | minorDelta = !minorDelta || minorDelta < deltaLimit ? deltaLimit : minorDelta; |
---|
112 | |
---|
113 | // update bounds |
---|
114 | if("min" in kwArgs){ min = kwArgs.min; } |
---|
115 | if("max" in kwArgs){ max = kwArgs.max; } |
---|
116 | if(kwArgs.includeZero){ |
---|
117 | if(min > 0){ min = 0; } |
---|
118 | if(max < 0){ max = 0; } |
---|
119 | } |
---|
120 | h.min = min; |
---|
121 | h.useMin = true; |
---|
122 | h.max = max; |
---|
123 | h.useMax = true; |
---|
124 | |
---|
125 | if("from" in kwArgs){ |
---|
126 | min = kwArgs.from; |
---|
127 | h.useMin = false; |
---|
128 | } |
---|
129 | if("to" in kwArgs){ |
---|
130 | max = kwArgs.to; |
---|
131 | h.useMax = false; |
---|
132 | } |
---|
133 | |
---|
134 | // check for erroneous condition |
---|
135 | if(max <= min){ |
---|
136 | return calcTicks(min, max, h, 0, 0, 0, span); // Object |
---|
137 | } |
---|
138 | if(!delta){ |
---|
139 | delta = max - min; |
---|
140 | } |
---|
141 | var mag = Math.floor(Math.log(delta) / Math.LN10), |
---|
142 | major = kwArgs && ("majorTickStep" in kwArgs) ? kwArgs.majorTickStep : Math.pow(10, mag), |
---|
143 | minor = 0, micro = 0, ticks; |
---|
144 | |
---|
145 | // calculate minor ticks |
---|
146 | if(kwArgs && ("minorTickStep" in kwArgs)){ |
---|
147 | minor = kwArgs.minorTickStep; |
---|
148 | }else{ |
---|
149 | do{ |
---|
150 | minor = major / 10; |
---|
151 | if(!h.natural || minor > 0.9){ |
---|
152 | ticks = calcTicks(min, max, h, major, minor, 0, span); |
---|
153 | if(ticks.bounds.scale * ticks.minor.tick > minorDelta){ break; } |
---|
154 | } |
---|
155 | minor = major / 5; |
---|
156 | if(!h.natural || minor > 0.9){ |
---|
157 | ticks = calcTicks(min, max, h, major, minor, 0, span); |
---|
158 | if(ticks.bounds.scale * ticks.minor.tick > minorDelta){ break; } |
---|
159 | } |
---|
160 | minor = major / 2; |
---|
161 | if(!h.natural || minor > 0.9){ |
---|
162 | ticks = calcTicks(min, max, h, major, minor, 0, span); |
---|
163 | if(ticks.bounds.scale * ticks.minor.tick > minorDelta){ break; } |
---|
164 | } |
---|
165 | return calcTicks(min, max, h, major, 0, 0, span); // Object |
---|
166 | }while(false); |
---|
167 | } |
---|
168 | |
---|
169 | // calculate micro ticks |
---|
170 | if(kwArgs && ("microTickStep" in kwArgs)){ |
---|
171 | micro = kwArgs.microTickStep; |
---|
172 | ticks = calcTicks(min, max, h, major, minor, micro, span); |
---|
173 | }else{ |
---|
174 | do{ |
---|
175 | micro = minor / 10; |
---|
176 | if(!h.natural || micro > 0.9){ |
---|
177 | ticks = calcTicks(min, max, h, major, minor, micro, span); |
---|
178 | if(ticks.bounds.scale * ticks.micro.tick > deltaLimit){ break; } |
---|
179 | } |
---|
180 | micro = minor / 5; |
---|
181 | if(!h.natural || micro > 0.9){ |
---|
182 | ticks = calcTicks(min, max, h, major, minor, micro, span); |
---|
183 | if(ticks.bounds.scale * ticks.micro.tick > deltaLimit){ break; } |
---|
184 | } |
---|
185 | micro = minor / 2; |
---|
186 | if(!h.natural || micro > 0.9){ |
---|
187 | ticks = calcTicks(min, max, h, major, minor, micro, span); |
---|
188 | if(ticks.bounds.scale * ticks.micro.tick > deltaLimit){ break; } |
---|
189 | } |
---|
190 | micro = 0; |
---|
191 | }while(false); |
---|
192 | } |
---|
193 | |
---|
194 | return micro ? ticks : calcTicks(min, max, h, major, minor, 0, span); // Object |
---|
195 | }, |
---|
196 | buildTicks: function(/*Object*/ scaler, /*Object*/ kwArgs){ |
---|
197 | var step, next, tick, |
---|
198 | nextMajor = scaler.major.start, |
---|
199 | nextMinor = scaler.minor.start, |
---|
200 | nextMicro = scaler.micro.start; |
---|
201 | if(kwArgs.microTicks && scaler.micro.tick){ |
---|
202 | step = scaler.micro.tick, next = nextMicro; |
---|
203 | }else if(kwArgs.minorTicks && scaler.minor.tick){ |
---|
204 | step = scaler.minor.tick, next = nextMinor; |
---|
205 | }else if(scaler.major.tick){ |
---|
206 | step = scaler.major.tick, next = nextMajor; |
---|
207 | }else{ |
---|
208 | // no ticks |
---|
209 | return null; |
---|
210 | } |
---|
211 | // make sure that we have finite bounds |
---|
212 | var revScale = 1 / scaler.bounds.scale; |
---|
213 | if(scaler.bounds.to <= scaler.bounds.from || isNaN(revScale) || !isFinite(revScale) || |
---|
214 | step <= 0 || isNaN(step) || !isFinite(step)){ |
---|
215 | // no ticks |
---|
216 | return null; |
---|
217 | } |
---|
218 | // loop over all ticks |
---|
219 | var majorTicks = [], minorTicks = [], microTicks = []; |
---|
220 | while(next <= scaler.bounds.to + revScale){ |
---|
221 | if(Math.abs(nextMajor - next) < step / 2){ |
---|
222 | // major tick |
---|
223 | tick = {value: nextMajor}; |
---|
224 | if(kwArgs.majorLabels){ |
---|
225 | tick.label = getLabel(nextMajor, scaler.major.prec, kwArgs); |
---|
226 | } |
---|
227 | majorTicks.push(tick); |
---|
228 | nextMajor += scaler.major.tick; |
---|
229 | nextMinor += scaler.minor.tick; |
---|
230 | nextMicro += scaler.micro.tick; |
---|
231 | }else if(Math.abs(nextMinor - next) < step / 2){ |
---|
232 | // minor tick |
---|
233 | if(kwArgs.minorTicks){ |
---|
234 | tick = {value: nextMinor}; |
---|
235 | if(kwArgs.minorLabels && (scaler.minMinorStep <= scaler.minor.tick * scaler.bounds.scale)){ |
---|
236 | tick.label = getLabel(nextMinor, scaler.minor.prec, kwArgs); |
---|
237 | } |
---|
238 | minorTicks.push(tick); |
---|
239 | } |
---|
240 | nextMinor += scaler.minor.tick; |
---|
241 | nextMicro += scaler.micro.tick; |
---|
242 | }else{ |
---|
243 | // micro tick |
---|
244 | if(kwArgs.microTicks){ |
---|
245 | microTicks.push({value: nextMicro}); |
---|
246 | } |
---|
247 | nextMicro += scaler.micro.tick; |
---|
248 | } |
---|
249 | next += step; |
---|
250 | } |
---|
251 | return {major: majorTicks, minor: minorTicks, micro: microTicks}; // Object |
---|
252 | }, |
---|
253 | getTransformerFromModel: function(/*Object*/ scaler){ |
---|
254 | var offset = scaler.bounds.from, scale = scaler.bounds.scale; |
---|
255 | return function(x){ return (x - offset) * scale; }; // Function |
---|
256 | }, |
---|
257 | getTransformerFromPlot: function(/*Object*/ scaler){ |
---|
258 | var offset = scaler.bounds.from, scale = scaler.bounds.scale; |
---|
259 | return function(x){ return x / scale + offset; }; // Function |
---|
260 | } |
---|
261 | }); |
---|
262 | }); |
---|