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