Mercurial > hg > svcore
comparison base/ScaleTickIntervals.h @ 1459:3a128665fa6f horizontal-scale
Fixes to logarithmic scale tick intervals. The approach here is not right, though -- and I've left in a failing test or two to remind me of that
author | Chris Cannam |
---|---|
date | Wed, 02 May 2018 14:17:10 +0100 |
parents | 48e9f538e6e9 |
children | 9528c73aa98c |
comparison
equal
deleted
inserted
replaced
1458:0fb5d4e6edeb | 1459:3a128665fa6f |
---|---|
180 | 180 |
181 static Instruction logInstruction(Range r) | 181 static Instruction logInstruction(Range r) |
182 { | 182 { |
183 Display display = Auto; | 183 Display display = Auto; |
184 | 184 |
185 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
186 SVDEBUG << "ScaleTickIntervals::logInstruction: Range is " | |
187 << r.min << " to " << r.max << endl; | |
188 #endif | |
189 | |
185 if (r.n < 1) { | 190 if (r.n < 1) { |
186 return {}; | 191 return {}; |
187 } | 192 } |
188 if (r.max < r.min) { | 193 if (r.max < r.min) { |
189 return logInstruction({ r.max, r.min, r.n }); | 194 return logInstruction({ r.max, r.min, r.n }); |
194 | 199 |
195 double inc = (r.max - r.min) / r.n; | 200 double inc = (r.max - r.min) / r.n; |
196 | 201 |
197 double digInc = log10(inc); | 202 double digInc = log10(inc); |
198 int precInc = int(floor(digInc)); | 203 int precInc = int(floor(digInc)); |
204 | |
199 double roundTo = pow(10.0, precInc); | 205 double roundTo = pow(10.0, precInc); |
200 | 206 |
207 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
208 SVDEBUG << "ScaleTickIntervals::logInstruction: Naive increment is " | |
209 << inc << ", of " << digInc << "-digit length" << endl; | |
210 SVDEBUG << "ScaleTickIntervals::logInstruction: " | |
211 << "So increment is precision " << precInc | |
212 << ", yielding rounding for increment of " | |
213 << roundTo << endl; | |
214 #endif | |
215 | |
201 if (roundTo != 0.0) { | 216 if (roundTo != 0.0) { |
202 inc = round(inc / roundTo) * roundTo; | 217 inc = round(inc / roundTo) * roundTo; |
203 if (inc < roundTo) inc = roundTo; | 218 if (inc < roundTo) inc = roundTo; |
219 | |
220 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
221 SVDEBUG << "ScaleTickIntervals::logInstruction: " | |
222 << "Rounded increment to " << inc << endl; | |
223 #endif | |
204 } | 224 } |
205 | 225 |
206 // if inc is close to giving us powers of two, nudge it | 226 // if inc is close to giving us powers of two, nudge it |
207 if (fabs(inc - 0.301) < 0.01) { | 227 if (fabs(inc - 0.301) < 0.01) { |
208 inc = log10(2.0); | 228 inc = log10(2.0); |
229 | |
230 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
231 SVDEBUG << "ScaleTickIntervals::logInstruction: " | |
232 << "Nudged increment to " << inc << " to get powers of two" | |
233 << endl; | |
234 #endif | |
209 } | 235 } |
210 | 236 |
211 // smallest increment as displayed | 237 // smallest increment as displayed |
212 double minDispInc = | 238 double minDispInc = |
213 LogRange::unmap(r.min + inc) - LogRange::unmap(r.min); | 239 LogRange::unmap(r.min + inc) - LogRange::unmap(r.min); |
214 | 240 |
241 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
242 SVDEBUG << "ScaleTickIntervals::logInstruction: " | |
243 << "Smallest displayed increment is " << minDispInc << endl; | |
244 #endif | |
245 | |
215 int prec = 1; | 246 int prec = 1; |
216 | 247 |
217 if (minDispInc > 0.0) { | 248 if (minDispInc > 0.0) { |
218 prec = int(floor(log10(minDispInc))); | 249 prec = int(ceil(log10(minDispInc))) - 1; |
250 if (prec == 0) prec = 1; | |
219 if (prec < 0) prec = -prec; | 251 if (prec < 0) prec = -prec; |
252 | |
253 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
254 SVDEBUG << "ScaleTickIntervals::logInstruction: " | |
255 << "Precision therefrom is " << prec << endl; | |
256 #endif | |
220 } | 257 } |
221 | 258 |
222 if (r.max >= -2.0 && r.max <= 3.0 && | 259 if (r.max >= -2.0 && r.max <= 3.0 && |
223 r.min >= -3.0 && r.min <= 3.0) { | 260 r.min >= -3.0 && r.min <= 3.0) { |
224 display = Fixed; | 261 display = Fixed; |
225 if (prec == 0) prec = 1; | 262 if (prec == 0) prec = 1; |
263 | |
264 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
265 SVDEBUG << "ScaleTickIntervals::logInstruction: " | |
266 << "Min and max within modest range, adjusted precision to " | |
267 << prec << " and display to Fixed" << endl; | |
268 #endif | |
226 } | 269 } |
227 | 270 |
228 #ifdef DEBUG_SCALE_TICK_INTERVALS | 271 #ifdef DEBUG_SCALE_TICK_INTERVALS |
229 SVDEBUG << "ScaleTickIntervals: calculating logInstruction" << endl | 272 SVDEBUG << "ScaleTickIntervals: calculating logInstruction" << endl |
230 << "ScaleTickIntervals: min = " << r.min << ", max = " << r.max | 273 << "ScaleTickIntervals: min = " << r.min << ", max = " << r.max |
257 Ticks ticks = explode(instruction); | 300 Ticks ticks = explode(instruction); |
258 return ticks; | 301 return ticks; |
259 } | 302 } |
260 | 303 |
261 static Tick makeTick(Display display, int precision, double value) { | 304 static Tick makeTick(Display display, int precision, double value) { |
305 | |
262 if (value == -0.0) { | 306 if (value == -0.0) { |
263 value = 0.0; | 307 value = 0.0; |
264 } | 308 } |
309 | |
265 const int buflen = 40; | 310 const int buflen = 40; |
266 char buffer[buflen]; | 311 char buffer[buflen]; |
267 snprintf(buffer, buflen, | 312 |
268 display == Auto ? "%.*g" : | 313 if (display == Auto) { |
269 display == Fixed ? "%.*f" : | 314 |
270 "%.*e", | 315 int digits = (value != 0.0 ? int(ceil(log10(abs(value)))) : 0); |
271 precision, value); | 316 |
317 // This is not the same logic as %g uses for determining | |
318 // whether to delegate to use scientific or fixed notation | |
319 | |
320 if (digits < -3 || digits > 4) { | |
321 | |
322 display = Auto; // delegate planning to %g | |
323 | |
324 } else { | |
325 | |
326 display = Fixed; | |
327 | |
328 // in %.*f, the * indicates decimal places, not sig figs | |
329 if (precision > digits) { | |
330 precision -= digits; | |
331 } else if (precision == digits) { | |
332 precision = 1; | |
333 } else if (precision + 1 < digits) { | |
334 double r = pow(10, digits - precision - 1); | |
335 value = r * round(value / r); | |
336 precision = 0; | |
337 } else { | |
338 precision = 0; | |
339 } | |
340 } | |
341 } | |
342 | |
343 const char *spec = (display == Auto ? "%.*g" : | |
344 display == Scientific ? "%.*e" : | |
345 "%.*f"); | |
346 | |
347 #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
348 | |
349 snprintf(buffer, buflen, spec, precision, value); | |
350 | |
351 #ifdef DEBUG_SCALE_TICK_INTERVALS | |
352 SVDEBUG << "makeTick: spec = \"" << spec | |
353 << "\", prec = " << precision << ", value = " << value | |
354 << ", label = \"" << buffer << "\"" << endl; | |
355 #endif | |
356 | |
272 return Tick({ value, std::string(buffer) }); | 357 return Tick({ value, std::string(buffer) }); |
273 } | 358 } |
274 | 359 |
275 static Ticks explode(Instruction instruction) { | 360 static Ticks explode(Instruction instruction) { |
276 | 361 |