1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2004, 2015 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #include "chart-geometry.h"
25 #include "gl/xalloc.h"
26 #include "gl/minmax.h"
27 #include "gl/xvasprintf.h"
29 static const double standard_tick[] = {1, 2, 5, 10};
32 Find a set {LOWER, INTERVAL, N_TICKS} such that:
35 LOWER + INTERVAL > LOWDBL,
37 LOWER + N_TICKS * INTERVAL < HIGHDBL
38 LOWER + (N_TICKS + 1) * INTERVAL >= HIGHDBL
42 and X is an element of {1, 2, 5}
48 |....+....+....+. .+....|
53 chart_get_scale (double high, double low,
54 double *lower, double *interval,
58 double fitness = DBL_MAX;
64 if ((high - low) < 10 * DBL_MIN) {
71 logrange = floor(log10(high-low));
73 /* Find the most pleasing interval */
74 for (i = 1; i < 4; ++i)
76 double cinterval = standard_tick[i] * pow(10.0,logrange-1);
77 double clower = floor(low/cinterval) * cinterval;
78 int cnticks = ceil((high - clower) / cinterval)-1;
79 double cfitness = fabs(7.5 - cnticks);
81 if (cfitness < fitness) {
84 *interval = cinterval;
91 * Compute the optimum format string and the scaling
92 * for the tick drawing on a chart axis
93 * Input: lower: the lowest tick
94 * interval:the interval between the ticks
95 * nticks: the number of tick intervals (bins) on the axis
96 * Return: fs: format string for printf to print the tick value
97 * scale: scaling factor for the tick value
98 * The format string has to be freed after usage.
99 * An example format string and scalefactor:
100 * Non Scientific: "%.3lf", scale=1.00
101 * Scientific: "%.2lfe3", scale = 0.001
103 * fs = chart_get_ticks_format(-0.7,0.1,8,&scale);
104 * printf(fs,value*scale);
108 chart_get_ticks_format (const double lower, const double interval,
109 const unsigned int nticks, double *scale)
111 double logmax = log10(fmax(fabs(lower + (nticks+1)*interval),fabs(lower)));
112 double logintv = log10(interval);
114 char *format_string = NULL;
117 if (logmax > 0.0 && logintv < 0.0)
119 nrdecs = MIN(6,(int)(ceil(fabs(logintv))));
122 format_string = xasprintf("%%.%dlf",nrdecs);
124 format_string = xasprintf("%%lg");
126 else if (logmax > 0.0) /*logintv is > 0*/
128 if (logintv < 5.0 && logmax < 10.0)
130 logshift = 0; /* No scientific format */
132 format_string = xstrdup("%.0lf");
136 logshift = (int)logmax;
137 /* Possible intervals are 0.2Ex, 0.5Ex, 1.0Ex */
138 /* log10(0.2E9) = 8.30, log10(0.5E9) = 8.69, log10(1.0E9) = 9 */
139 /* 0.2 and 0.5 need one decimal more. For stability subtract 0.1 */
140 nrdecs = MIN(8,(int)(ceil(logshift-logintv-0.1)));
141 format_string = xasprintf("%%.%dlf⋅10<sup>%d</sup>",nrdecs,logshift);
144 else /* logmax and logintv are < 0 */
148 logshift = 0; /* No scientific format */
149 nrdecs = MIN(8,(int)(ceil(-logintv)));
150 format_string = xasprintf("%%.%dlf",nrdecs);
154 logshift = (int)logmax-1;
155 nrdecs = MIN(8,(int)(ceil(logshift-logintv-0.1)));
156 format_string = xasprintf("%%.%dlf⋅10<sup>%d</sup>",nrdecs,logshift);
159 *scale = pow(10.0,-(double)logshift);
160 return format_string;