Coverage Report

Created: 2026-09-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}