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"
30 #define _(msgid) gettext (msgid)
32 static const double standard_tick[] = {1, 2, 5, 10};
35 Find a set {LOWER, INTERVAL, N_TICKS} such that:
38 LOWER + INTERVAL > LOWDBL,
40 LOWER + N_TICKS * INTERVAL < HIGHDBL
41 LOWER + (N_TICKS + 1) * INTERVAL >= HIGHDBL
45 and X is an element of {1, 2, 5}
51 |....+....+....+. .+....|
56 chart_get_scale (double high, double low,
57 double *lower, double *interval,
61 double fitness = DBL_MAX;
67 if ((high - low) < 10 * DBL_MIN) {
74 logrange = floor(log10(high-low));
76 /* Find the most pleasing interval */
77 for (i = 1; i < 4; ++i)
79 double cinterval = standard_tick[i] * pow(10.0,logrange-1);
80 double clower = floor(low/cinterval) * cinterval;
81 int cnticks = ceil((high - clower) / cinterval)-1;
82 double cfitness = fabs(7.5 - cnticks);
84 if (cfitness < fitness) {
87 *interval = cinterval;
94 Generate a format string which can be passed to printf like functions,
95 which will produce a string in scientific notation representing a real
96 number. N_DECIMALS is the number of decimal places EXPONENT is the
97 value of the exponent.
100 gen_pango_markup_scientific_format_string (int n_decimals, int exponent)
102 /* TRANSLATORS: This is a format string which, when presented to
103 printf like functions, will create a pango markup string to
104 display real number in scientific notation.
106 In its untranslated form, it will display similar to "1.23 x 10^4". You
107 can leave it untranslated if this is how scientific notation is usually
108 presented in your language.
110 Some locales (such as German) prefer the centered dot rather than the
111 multiplication sign between the mantissa an exponent. In which
112 case, you can change "#215;" to "#8901;" or other unicode code
113 point as appropriate.
115 The . in this string does not and should not be changed, since
116 that is taken care of by the stdc library.
118 For information on Pango markup, see
119 http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
121 For tables of unicode code points, see http://unicode.org/charts
123 return xasprintf(_("%%.%dlf×10<sup>%d</sup>"), n_decimals, exponent);
127 * Compute the optimum format string and the scaling
128 * for the tick drawing on a chart axis
129 * Input: lower: the lowest tick
130 * interval:the interval between the ticks
131 * nticks: the number of tick intervals (bins) on the axis
132 * Return: fs: format string for printf to print the tick value
133 * scale: scaling factor for the tick value
134 * The format string has to be freed after usage.
135 * An example format string and scalefactor:
136 * Non Scientific: "%.3lf", scale=1.00
137 * Scientific: "%.2lfe3", scale = 0.001
139 * fs = chart_get_ticks_format(-0.7,0.1,8,&scale);
140 * printf(fs,value*scale);
144 chart_get_ticks_format (const double lower, const double interval,
145 const unsigned int nticks, double *scale)
147 double logmax = log10(fmax(fabs(lower + (nticks+1)*interval),fabs(lower)));
148 double logintv = log10(interval);
150 char *format_string = NULL;
153 if (logmax > 0.0 && logintv < 0.0)
155 nrdecs = MIN(6,(int)(ceil(fabs(logintv))));
158 format_string = xasprintf("%%.%dlf",nrdecs);
160 format_string = xasprintf("%%lg");
162 else if (logmax > 0.0) /*logintv is > 0*/
164 if (logintv < 5.0 && logmax < 10.0)
166 logshift = 0; /* No scientific format */
168 format_string = xstrdup("%.0lf");
172 logshift = (int)logmax;
173 /* Possible intervals are 0.2Ex, 0.5Ex, 1.0Ex */
174 /* log10(0.2E9) = 8.30, log10(0.5E9) = 8.69, log10(1.0E9) = 9 */
175 /* 0.2 and 0.5 need one decimal more. For stability subtract 0.1 */
176 nrdecs = MIN(8,(int)(ceil(logshift-logintv-0.1)));
177 format_string = gen_pango_markup_scientific_format_string (nrdecs, logshift);
180 else /* logmax and logintv are < 0 */
184 logshift = 0; /* No scientific format */
185 nrdecs = MIN(8,(int)(ceil(-logintv)));
186 format_string = xasprintf("%%.%dlf",nrdecs);
190 logshift = (int)logmax-1;
191 nrdecs = MIN(8,(int)(ceil(logshift-logintv-0.1)));
192 format_string = gen_pango_markup_scientific_format_string (nrdecs, logshift);
195 *scale = pow(10.0,-(double)logshift);
196 return format_string;