/src/r-source/src/main/plot.c
Line | Count | Source |
1 | | /* |
2 | | * R : A Computer Language for Statistical Data Analysis |
3 | | * Copyright (C) 1997--2021 The R Core Team |
4 | | * Copyright (C) 2002--2009 The R Foundation |
5 | | * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, a copy is available at |
19 | | * https://www.R-project.org/Licenses/ |
20 | | */ |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | # include <config.h> |
24 | | #endif |
25 | | |
26 | | #include <Defn.h> // Rexp10 (et al) |
27 | | #include <float.h> /* for DBL_MAX */ |
28 | | #include <Graphics.h> |
29 | | #include <Print.h> |
30 | | #include <Rmath.h> // for imax2 |
31 | | |
32 | | /* used in graphics and grid */ |
33 | | SEXP CreateAtVector(double axp[], const double usr[], int nint, Rboolean logflag) |
34 | 0 | { |
35 | | /* Create an 'at = ...' vector for axis(.) |
36 | | * i.e., the vector of tick mark locations, |
37 | | * when none has been specified (= default). |
38 | | * |
39 | | * axp[0:2] = (x1, x2, nInt), where x1..x2 are the extreme tick marks |
40 | | * {unless in log case, where nInt \in {1,2,3 ; -1,-2,....} |
41 | | * and the `nint' argument is used *instead*.} |
42 | | * |
43 | | * only if(logflag && axp[2] >= 0) |
44 | | * usr[0:1] is used, additionally |
45 | | * |
46 | | * The resulting REAL vector must have length >= 1, ideally >= 2 |
47 | | */ |
48 | 0 | SEXP at = R_NilValue;/* -Wall*/ |
49 | 0 | double dn, rng, small; |
50 | 0 | int i, n; |
51 | | // "arbitrary" threshold: |delta_tick| / SMALL is "barely visible" in plot |
52 | 0 | #define SMALL_F 100. |
53 | 0 | if (!logflag || axp[2] < 0) { /* --- linear axis --- Only use axp[] arg. */ |
54 | 0 | n = (int)(fabs(axp[2]) + 0.25);/* >= 0 */ |
55 | 0 | dn = imax2(1, n); |
56 | 0 | rng = axp[1] - axp[0]; |
57 | 0 | at = allocVector(REALSXP, n + 1); |
58 | 0 | double a_i; |
59 | 0 | if(!R_FINITE(rng)) { // need to carefully work around overflow |
60 | 0 | double at_ = axp[0]/dn; // 2021-07: "/dn" avoids overflow |
61 | 0 | rng = axp[1]/dn - at_; |
62 | 0 | small = fabs(rng)/SMALL_F; |
63 | | #ifdef DEBUG_axis |
64 | | REprintf("CreateAtVector(axp=(%g,%g, %g), log=F, diff(*)=Inf: at_=%g, rng=%g, small=%g\n", |
65 | | axp[0],axp[1], axp[2], at_, rng, small); |
66 | | #endif |
67 | 0 | int n2 = n/2; // integer division |
68 | 0 | for (i = 0; i <= n2; i++) { // from the left |
69 | 0 | a_i = axp[0] + i * rng; |
70 | | // REprintf(" at[i=%2d]=%g\n", i+1, a_i); |
71 | 0 | REAL(at)[i] = (fabs(a_i) < small) ? 0. : a_i; |
72 | 0 | } |
73 | 0 | for (int i2 = 0; i2 < n-n2; i2++) { // from the right |
74 | 0 | i = n-i2; // for(i in n:k) where k = n-(n-n2-1) = n2+1 |
75 | 0 | a_i = axp[1] - i2 * rng; |
76 | | // REprintf(" at[i=%2d]=%g\n", i+1, a_i); |
77 | 0 | REAL(at)[i] = (fabs(a_i) < small) ? 0. : a_i; |
78 | 0 | } |
79 | 0 | } |
80 | 0 | else { // rng is finite (normal case): |
81 | 0 | small = fabs(rng)/SMALL_F/dn; |
82 | 0 | for (i = 0; i <= n; i++) { |
83 | 0 | a_i = axp[0] + (i / dn) * rng; |
84 | 0 | REAL(at)[i] = (fabs(a_i) < small) ? 0. : a_i; |
85 | 0 | } |
86 | 0 | } |
87 | 0 | } |
88 | 0 | else { /* ------ log axis ----- */ |
89 | 0 | bool reversed = false; |
90 | 0 | double |
91 | 0 | umin = usr[0], |
92 | 0 | umax = usr[1]; |
93 | 0 | n = (int)(axp[2] + 0.5); |
94 | | /* {xy}axp[2] for 'log': GLpretty() [./graphics.c] sets |
95 | | n < 0: very small scale ==> linear axis, above, or |
96 | | n = 1,2,3. see switch() below */ |
97 | | #ifdef DEBUG_axis |
98 | | REprintf("CreateAtVector(axp=(%g,%g,%g), usr=(%g,%g), _log_):", |
99 | | axp[0],axp[1],axp[2], usr[0],usr[1]); |
100 | | #endif |
101 | 0 | if (umin > umax) { |
102 | 0 | reversed = (axp[0] > axp[1]); |
103 | 0 | if (reversed) { |
104 | | /* have *reversed* log axis -- whereas |
105 | | * the switch(n) { .. } below assumes *increasing* values |
106 | | * --> reverse axis direction here, and reverse back at end */ |
107 | 0 | umin = usr[1]; |
108 | 0 | umax = usr[0]; |
109 | 0 | dn = axp[0]; axp[0] = axp[1]; axp[1] = dn; |
110 | 0 | } |
111 | 0 | else { |
112 | | /* can the following still happen... ? */ |
113 | 0 | warning("CreateAtVector \"log\"(from axis()): " |
114 | 0 | "usr[0] = %g > %g = usr[1] !", umin, umax); |
115 | 0 | } |
116 | 0 | } |
117 | | /* allow a fuzz (iff we don't under-/over-flow) since we will do things like 0.2*dn >= umin */ |
118 | 0 | dn = 1 - 1e-12; if(fabs(umin*dn) > 0. ) umin *= dn; |
119 | 0 | dn = 1 + 1e-12; if(fabs(umax*dn) <= DBL_MAX) umax *= dn; |
120 | |
|
121 | 0 | dn = axp[0]; |
122 | 0 | if (dn < DBL_MIN) {/* was 1e-300; now seems too cautious */ |
123 | 0 | if (dn <= 0) /* real trouble (once for Solaris) later on */ |
124 | 0 | error("CreateAtVector [log-axis()]: axp[0] = %g < 0!", dn); |
125 | 0 | else |
126 | 0 | warning("CreateAtVector [log-axis()]: small axp[0] = %g", dn); |
127 | 0 | } |
128 | | |
129 | | /* You get the 3 cases below by |
130 | | * for (y in 1e-5*c(1,2,8)) plot(y, log = "y") |
131 | | */ |
132 | 0 | switch(n) { |
133 | 0 | case 1: /* large range: 1 * 10^k */ |
134 | 0 | { |
135 | 0 | i = (int)(floor(log10(axp[1])) - ceil(log10(axp[0])) + 0.25); |
136 | | // want nint intervals, i.e. typically nint+1 breaks : |
137 | 0 | int ne = i / nint; |
138 | | /* for nint breaks, i.e. typically nint-1 intervals, would be |
139 | | * ne = i / imax2(1, nint - 1); *PLUS* replace s/nint/nint-1/ below !! */ |
140 | | #ifdef DEBUG_axis |
141 | | REprintf(" .. case 1: umin,umax= %g,%g;\n (nint=%d, ne=%d); ", |
142 | | umin, umax, nint, ne); |
143 | | if (ne < 1) { |
144 | | REprintf("ne = %d <= 0 !!\n\t axp[0:1]=(%g,%g) ==> i = %d, nint = %d; ", |
145 | | ne, axp[0],axp[1], i, nint); |
146 | | } |
147 | | #endif |
148 | 0 | double l10_max = log10(umax), |
149 | 0 | d0 = l10_max - log10(dn); |
150 | | #ifdef DEBUG_axis |
151 | | REprintf("exponent diff d0=%g\n", d0); |
152 | | #endif |
153 | 0 | if(ne < 1) ne = 1; |
154 | 0 | else // if ne is too large, i.e, the "final tick" is beyond umax, reduce it : |
155 | 0 | while(ne > 1 && nint*ne > d0) { |
156 | 0 | ne--; |
157 | | #ifdef DEBUG_axis |
158 | | REprintf(" last > umax ==> ne--: ne=%d\n", ne); |
159 | | #endif |
160 | 0 | } |
161 | 0 | int k = 1 + ne / 308; // >= 1, typically == 1. |
162 | 0 | if(k > 1) {// i.e. ne > 308: 10^ne overflows; must split the multiplication |
163 | 0 | ne = k*(ne/k); // <= ne_{previous} |
164 | | #ifdef DEBUG_axis |
165 | | REprintf(" original ne > 308: split in k=%d parts; new ne=%d\n", k,ne); |
166 | | #endif |
167 | 0 | } |
168 | | /* Now, still in exponent-10 range: nint*ne <= d0 = l10_max - log10(dn) |
169 | | * If difference (=: d1) is "large", say > 3, increase the first at[] =: d0 |
170 | | */ |
171 | 0 | double d1 = d0 - nint*ne; // >= 0 |
172 | | #ifdef DEBUG_axis |
173 | | REprintf("expo.diff d0 - nint*ne =: d1=%g\n", d1); |
174 | | #endif |
175 | 0 | d0 = dn; |
176 | |
|
177 | 0 | #define Large_D1 5 |
178 | | // === was '3' all up into R 4.1.0 |
179 | 0 | if(d1 > Large_D1) { |
180 | 0 | d0 = dn * Rexp10(floor(d1/2)); |
181 | | #ifdef DEBUG_axis |
182 | | REprintf("large d1 => d0 := dn * 10 ^ fl(d1/2) = dn * 10^%d = %g\n", |
183 | | (int)floor(d1/2), d0); |
184 | | #endif |
185 | 0 | } |
186 | 0 | rng = Rexp10((double)ne/k); // = 10^(ne/k) >= 10 |
187 | 0 | n=0; |
188 | 0 | dn=d0; |
189 | 0 | while(dn < umax) { |
190 | 0 | for(int j=0; j < k; j++) |
191 | 0 | dn *= rng; |
192 | 0 | n++; |
193 | 0 | } |
194 | | #ifdef DEBUG_axis |
195 | | REprintf(" rng:=10^(ne/(k=%d)) = %g => n=%d, final dn=%g\n", k, rng, n, dn); |
196 | | #endif |
197 | 0 | if (!n) |
198 | 0 | error("log - axis(), 'at' creation, _LARGE_ range: " |
199 | 0 | "invalid {xy}axp or par; nint=%d\n" |
200 | 0 | " axp[0:1]=(%g,%g), usr[0:1]=(%g,%g); i=%d, ni=%d", |
201 | 0 | nint, axp[0],axp[1], umin,umax, i,ne); |
202 | 0 | at = allocVector(REALSXP, n); |
203 | 0 | dn=d0; |
204 | 0 | for(int i=0; i < n; i++) { |
205 | 0 | REAL(at)[i] = dn; |
206 | 0 | for(int j=0; j < k; j++) |
207 | 0 | dn *= rng; |
208 | 0 | } |
209 | 0 | break; |
210 | 0 | } |
211 | 0 | case 2: /* medium range: 1, 5 * 10^k */ |
212 | 0 | n = 0; |
213 | 0 | if (0.5 * dn >= umin) n++; |
214 | | #ifdef DEBUG_axis |
215 | | REprintf(" .. case 2: (dn, umin,umax, n) = (%g, %g,%g, %d)\n", |
216 | | dn, umin, umax, n); |
217 | | #endif |
218 | 0 | for (;;) { |
219 | 0 | if (dn > umax) break; |
220 | 0 | n++; |
221 | 0 | if (5 * dn > umax) break; |
222 | 0 | n++; |
223 | 0 | dn *= 10; |
224 | 0 | } |
225 | 0 | if (!n) |
226 | 0 | error("log - axis(), 'at' creation, _MEDIUM_ range: " |
227 | 0 | "invalid {xy}axp or par;\n" |
228 | 0 | " axp[0]= %g, usr[0:1]=(%g,%g)", |
229 | 0 | axp[0], umin,umax); |
230 | |
|
231 | 0 | at = allocVector(REALSXP, n); |
232 | 0 | dn = axp[0]; |
233 | 0 | n = 0; |
234 | 0 | if (0.5 * dn >= umin) REAL(at)[n++] = 0.5 * dn; |
235 | 0 | for (;;) { |
236 | 0 | if (dn > umax) break; |
237 | 0 | REAL(at)[n++] = dn; |
238 | 0 | if (5 * dn > umax) break; |
239 | 0 | REAL(at)[n++] = 5 * dn; |
240 | 0 | dn *= 10; |
241 | 0 | } |
242 | 0 | break; |
243 | | |
244 | 0 | case 3: /* small range: 1,2,5,10 * 10^k */ |
245 | 0 | n = 0; |
246 | 0 | if (0.2 * dn >= umin) n++; |
247 | 0 | if (0.5 * dn >= umin) n++; |
248 | 0 | for (;;) { |
249 | 0 | if (dn > umax) break; |
250 | 0 | n++; |
251 | 0 | if (2 * dn > umax) break; |
252 | 0 | n++; |
253 | 0 | if (5 * dn > umax) break; |
254 | 0 | n++; |
255 | 0 | dn *= 10; |
256 | 0 | } |
257 | | #ifdef DEBUG_axis |
258 | | REprintf(" .. case 3: (umin,umax)-usr[*] = (%g, %g); n=%d, dn=%g\n", |
259 | | umin-usr[reversed? 1: 0], |
260 | | umax-usr[reversed? 0: 1], n, dn); |
261 | | #endif |
262 | 0 | if (!n) |
263 | 0 | error("log - axis(), 'at' creation, _SMALL_ range: " |
264 | 0 | "invalid {xy}axp or par;\n" |
265 | 0 | " axp[0]= %g, usr[0:1]=(%g,%g)", |
266 | 0 | axp[0], umin,umax); |
267 | 0 | at = allocVector(REALSXP, n); |
268 | 0 | dn = axp[0]; |
269 | 0 | n = 0; |
270 | 0 | if (0.2 * dn >= umin) REAL(at)[n++] = 0.2 * dn; |
271 | 0 | if (0.5 * dn >= umin) REAL(at)[n++] = 0.5 * dn; |
272 | 0 | for (;;) { |
273 | 0 | if (dn > umax) break; |
274 | 0 | REAL(at)[n++] = dn; |
275 | 0 | if (2 * dn > umax) break; |
276 | 0 | REAL(at)[n++] = 2 * dn; |
277 | 0 | if (5 * dn > umax) break; |
278 | 0 | REAL(at)[n++] = 5 * dn; |
279 | 0 | dn *= 10; |
280 | 0 | } |
281 | 0 | break; |
282 | 0 | default: |
283 | 0 | error("log - axis(), 'at' creation: INVALID {xy}axp[3] = %g", |
284 | 0 | axp[2]); |
285 | 0 | } |
286 | | |
287 | 0 | if (reversed) {/* reverse back again - last assignment was at[n++]= . */ |
288 | 0 | for (i = 0; i < n/2; i++) { /* swap( at[i], at[n-i-1] ) : */ |
289 | 0 | dn = REAL(at)[i]; |
290 | 0 | REAL(at)[i] = REAL(at)[n-i-1]; |
291 | 0 | REAL(at)[n-i-1] = dn; |
292 | 0 | } |
293 | 0 | } |
294 | 0 | } /* linear / log */ |
295 | 0 | return at; |
296 | 0 | } |