sys-file-encoding: Clean up .c file on distclean.
[pspp.git] / src / math / chart-geometry.c
blob7029ddd73714a1f453c85943d4048a7387fce438
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/>. */
17 #include <config.h>
18 #include <math.h>
19 #include <float.h>
20 #include <assert.h>
22 #include "chart-geometry.h"
23 #include <stdlib.h>
25 #include "gl/xalloc.h"
26 #include "gl/minmax.h"
27 #include "gl/xvasprintf.h"
29 #include "gettext.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:
37 LOWER <= LOWDBL,
38 LOWER + INTERVAL > LOWDBL,
40 LOWER + N_TICKS * INTERVAL < HIGHDBL
41 LOWER + (N_TICKS + 1) * INTERVAL >= HIGHDBL
43 INTERVAL = X * 10^N
44 where: N is integer
45 and X is an element of {1, 2, 5}
47 In other words:
49 INTERVAL
50 > <
51 |....+....+....+. .+....|
52 LOWER 1 2 3 N_TICKS
53 ^LOWDBL ^HIGHDBL
55 void
56 chart_get_scale (double high, double low,
57 double *lower, double *interval,
58 int *n_ticks)
60 int i;
61 double fitness = DBL_MAX;
62 double logrange;
63 *n_ticks = 0;
65 assert (high >= low);
67 if ((high - low) < 10 * DBL_MIN) {
68 *n_ticks = 0;
69 *lower = low;
70 *interval = 0.0;
71 return;
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) {
85 fitness = cfitness;
86 *lower = clower;
87 *interval = cinterval;
88 *n_ticks = cnticks;
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.
99 static inline char *
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&#215;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
138 * Usage example:
139 * fs = chart_get_ticks_format(-0.7,0.1,8,&scale);
140 * printf(fs,value*scale);
141 * free(fs);
143 char *
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);
149 int logshift = 0;
150 char *format_string = NULL;
151 int nrdecs = 0;
153 if (logmax > 0.0 && logintv < 0.0)
155 nrdecs = MIN(6,(int)(ceil(fabs(logintv))));
156 logshift = 0;
157 if (logmax < 12.0)
158 format_string = xasprintf("%%.%dlf",nrdecs);
159 else
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 */
167 nrdecs = 0;
168 format_string = xstrdup("%.0lf");
170 else
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 */
182 if (logmax > -3.0)
184 logshift = 0; /* No scientific format */
185 nrdecs = MIN(8,(int)(ceil(-logintv)));
186 format_string = xasprintf("%%.%dlf",nrdecs);
188 else
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;