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