1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
|
/* This file contains miscellaneous subroutines for GNU graph. Currently,
it contains only array_bounds(), which is called if the user fails to
specify at least one of the bounds xmin,xmax,ymin,ymax.
array_bounds() returns the unspecified bounds via pointers. I.e., it
finishes the job of specifying a bounding box for the data points that
will be plotted. The box may later be expanded so that its bounds are
multiples of the tick spacing (see plotter.c).
array_bounds() is called in graph.c, just before a graph is begun. */
#include "sys-defines.h"
#include "extern.h"
/* bit fields for return value from Cohen-Sutherland clipper */
enum { ACCEPTED = 0x1, CLIPPED_FIRST = 0x2, CLIPPED_SECOND = 0x4 };
/* for internal clipper use */
enum { TOP = 0x1, BOTTOM = 0x2, RIGHT = 0x4, LEFT = 0x8 };
/* forward references */
static int clip_line ____P((double *x0_p, double *y0_p, double *x1_p, double *y1_p, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y));
static int compute_relevant_points ____P ((double xx, double yy, double oldxx, double oldyy, int clip_mode, double user_min_x, double user_min_y, double user_max_x, double user_max_y, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y, double xxr[2], double yyr[2]));
static int compute_outcode ____P ((double x, double y, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y));
void
#ifdef _HAVE_PROTOS
array_bounds (const Point *p, int length,
bool transpose_axes, int clip_mode,
double *min_x, double *min_y, double *max_x, double *max_y,
bool spec_min_x, bool spec_min_y,
bool spec_max_x, bool spec_max_y)
#else
array_bounds (p, length,
transpose_axes, clip_mode,
min_x, min_y, max_x, max_y,
spec_min_x, spec_min_y, spec_max_x, spec_max_y)
const Point *p;
int length;
bool transpose_axes;
int clip_mode;
double *min_x, *min_y, *max_x, *max_y;
bool spec_min_x, spec_min_y, spec_max_x, spec_max_y;
#endif
{
/* keep compilers happy */
double user_min_x = 0.0, user_min_y = 0.0;
double user_max_x = 0.0, user_max_y = 0.0;
double local_min_x = 0.0, local_min_y = 0.0;
double local_max_x = 0.0, local_max_y = 0.0;
double xx, yy, oldxx, oldyy;
bool point_seen = false;
int i;
if (length == 0)
/* adopt a convention */
{
if (!spec_min_x)
*min_x = 0.0;
if (!spec_min_y)
*min_y = 0.0;
if (!spec_max_x)
*max_x = *min_x;
if (!spec_max_y)
*max_y = *min_y;
return;
}
if (spec_min_x)
user_min_x = *min_x;
else /* won't use user_min_x */
local_min_x = DBL_MAX;
if (spec_max_x)
user_max_x = *max_x;
else /* won't use user_max_x */
local_max_x = -(DBL_MAX);
/* special case: user specified both bounds, but min > max (reversed axis) */
if (spec_min_x && spec_max_x && user_min_x > user_max_x)
{
double tmp;
tmp = user_min_x;
user_min_x = user_max_x;
user_max_x = tmp;
}
if (spec_min_y)
user_min_y = *min_y;
else
local_min_y = DBL_MAX; /* won't use user_min_y */
if (spec_max_y)
user_max_y = *max_y;
else /* won't use user_max_y */
local_max_y = -(DBL_MAX);
/* special case: user specified both bounds, but min > max (reversed axis) */
if (spec_min_y && spec_max_y && user_min_y > user_max_y)
{
double tmp;
tmp = user_min_y;
user_min_y = user_max_y;
user_max_y = tmp;
}
/* loop through points in array; examine each line segment */
oldxx = oldyy = 0.0; /* previous point */
for (i = 0; i < length; i++)
{
double xxr[2], yyr[2]; /* storage for `relevant points' */
int n, j;
int effective_clip_mode;
/* get new point */
xx = (transpose_axes ? p[i].y : p[i].x);
yy = (transpose_axes ? p[i].x : p[i].y);
/* determine clipping mode (see compute_relevant_points() below) */
if (i == 0 || p[i].pendown == false
|| (p[i].linemode <= 0 && p[i].fill_fraction < 0.0))
/* no polyline or filling, each point is isolated */
effective_clip_mode = 0;
else if (p[i].fill_fraction >= 0.0)
effective_clip_mode = 2;
else
effective_clip_mode = clip_mode;
n = compute_relevant_points (xx, yy, oldxx, oldyy,
effective_clip_mode,
user_min_x, user_min_y,
user_max_x, user_max_y,
spec_min_x, spec_min_y,
spec_max_x, spec_max_y,
xxr, yyr);
/* loop through relevant points, updating bounding box */
for (j = 0; j < n; j++)
{
point_seen = true;
if (!spec_min_x)
local_min_x = DMIN(local_min_x, xxr[j]);
if (!spec_min_y)
local_min_y = DMIN(local_min_y, yyr[j]);
if (!spec_max_x)
local_max_x = DMAX(local_max_x, xxr[j]);
if (!spec_max_y)
local_max_y = DMAX(local_max_y, yyr[j]);
}
oldxx = xx;
oldyy = yy;
}
if (!point_seen)
/* a convention */
local_min_x = local_min_y = local_max_x = local_max_y = 0.0;
/* pass back bounds that user didn't specify */
if (!spec_min_x)
*min_x = local_min_x;
if (!spec_min_y)
*min_y = local_min_y;
if (!spec_max_x)
*max_x = local_max_x;
if (!spec_max_y)
*max_y = local_max_y;
return;
}
/* For a new data point (xx,yy), compute the `relevant points', i.e. the
ones that should be used in updating the bounding box. There may be 0,
1, or 2 of them. The number of relevant points is returned, and the
relevant points themselves are returned via pointers.
The relevant points are computed from the line segment extending from
(oldxx,oldyy) to (xx,yy), via an algorithm parametrized by a
gnuplot-style clip mode (0, 1, or 2).
If clip mode=0 then the simplest algorithm is used: (xx,yy) is a
relevant point iff it satisfies the user-specified bound(s), and there
are no other relevant points, i.e., (oldxx,oldyy) is ignored.
If clip mode=1 then if the line segment from (oldxx, oldyy) from (xx,yy)
has at least one endpoint that satisfies the user-specified bounds, it
generates two relevant points: the endpoints of the line segment,
clipped to the bounds. If on the other hand neither endpoint of the
line segment from (oldxx,oldyy) to (xx,yy) satisfies the user-specified
bounds, no relevant points are generated even if the line segment
contains points that satisfy the bounds.
If clip mode=2 then the line segment, if it intersects the bounding box,
is clipped on both ends, and both resulting endpoints are relevant. */
static int
#ifdef _HAVE_PROTOS
compute_relevant_points (double xx, double yy,
double oldxx, double oldyy,
int clip_mode,
double user_min_x, double user_min_y,
double user_max_x, double user_max_y,
bool spec_min_x, bool spec_min_y,
bool spec_max_x, bool spec_max_y,
double xxr[2], double yyr[2])
#else
compute_relevant_points (xx, yy, oldxx, oldyy,
clip_mode,
user_min_x, user_min_y, user_max_x, user_max_y,
spec_min_x, spec_min_y, spec_max_x, spec_max_y,
xxr, yyr)
double xx, yy, oldxx, oldyy;
int clip_mode;
double user_min_x, user_min_y, user_max_x, user_max_y;
bool spec_min_x, spec_min_y, spec_max_x, spec_max_y;
double xxr[2], yyr[2];
#endif
{
int clipval;
switch (clip_mode)
{
case 0:
if ((!spec_min_x || xx >= user_min_x)
&& (!spec_max_x || xx <= user_max_x)
&& (!spec_min_y || yy >= user_min_y)
&& (!spec_max_y || yy <= user_max_y))
{
xxr[0] = xx;
yyr[0] = yy;
return 1;
}
else
return 0;
break;
case 1:
default:
clipval = clip_line (&oldxx, &oldyy, &xx, &yy, user_min_x, user_max_x, user_min_y, user_max_y, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
if ((clipval & ACCEPTED)
&& !((clipval & CLIPPED_FIRST) && (clipval & CLIPPED_SECOND)))
{
xxr[0] = oldxx;
yyr[0] = oldyy;
xxr[1] = xx;
yyr[1] = yy;
return 2;
}
else
return 0;
break;
case 2:
clipval = clip_line (&oldxx, &oldyy, &xx, &yy, user_min_x, user_max_x, user_min_y, user_max_y, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
if (clipval & ACCEPTED)
{
xxr[0] = oldxx;
yyr[0] = oldyy;
xxr[1] = xx;
yyr[1] = yy;
return 2;
}
else
return 0;
break;
}
}
/* clip_line() takes two points, the endpoints of a line segment in the
* device frame (expressed in terms of floating-point device coordinates),
* and destructively passes back two points: the endpoints of the line
* segment clipped by Cohen-Sutherland to the rectangular clipping area.
* The return value contains bitfields ACCEPTED, CLIPPED_FIRST, and
* CLIPPED_SECOND.
*
* This is a modified C-S clipper: the flags spec_{min,max}_{x,y} indicate
* whether or not clipping is to be performed on each edge.
*/
static int
#ifdef _HAVE_PROTOS
clip_line (double *x0_p, double *y0_p, double *x1_p, double *y1_p, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y)
#else
clip_line (x0_p, y0_p, x1_p, y1_p, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y)
double *x0_p, *y0_p, *x1_p, *y1_p;
double x_min_clip, x_max_clip, y_min_clip, y_max_clip;
bool spec_min_x, spec_min_y, spec_max_x, spec_max_y;
#endif
{
double x0 = *x0_p;
double y0 = *y0_p;
double x1 = *x1_p;
double y1 = *y1_p;
int outcode0, outcode1;
bool accepted;
int clipval = 0;
outcode0 = compute_outcode (x0, y0, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
outcode1 = compute_outcode (x1, y1, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
for ( ; ; )
{
if (!(outcode0 | outcode1)) /* accept */
{
accepted = true;
break;
}
else if (outcode0 & outcode1) /* reject */
{
accepted = false;
break;
}
else
{
/* at least one endpoint is outside; choose one that is */
int outcode_out = (outcode0 ? outcode0 : outcode1);
double x, y; /* intersection with clip edge */
if (outcode_out & RIGHT)
{
x = x_max_clip;
y = y0 + (y1 - y0) * (x_max_clip - x0) / (x1 - x0);
}
else if (outcode_out & LEFT)
{
x = x_min_clip;
y = y0 + (y1 - y0) * (x_min_clip - x0) / (x1 - x0);
}
else if (outcode_out & TOP)
{
x = x0 + (x1 - x0) * (y_max_clip - y0) / (y1 - y0);
y = y_max_clip;
}
else /* BOTTOM bit must be set */
{
x = x0 + (x1 - x0) * (y_min_clip - y0) / (y1 - y0);
y = y_min_clip;
}
if (outcode_out == outcode0)
{
x0 = x;
y0 = y;
outcode0 = compute_outcode (x0, y0, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
}
else
{
x1 = x;
y1 = y;
outcode1 = compute_outcode (x1, y1, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
}
}
}
if (accepted)
{
clipval |= ACCEPTED;
if ((x0 != *x0_p) || (y0 != *y0_p))
clipval |= CLIPPED_FIRST;
if ((x1 != *x1_p) || (y1 != *y1_p))
clipval |= CLIPPED_SECOND;
*x0_p = x0;
*y0_p = y0;
*x1_p = x1;
*y1_p = y1;
}
return clipval;
}
static int
#ifdef _HAVE_PROTOS
compute_outcode (double x, double y, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y)
#else
compute_outcode (x, y, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y)
double x, y, x_min_clip, x_max_clip, y_min_clip, y_max_clip;
bool spec_min_x, spec_min_y, spec_max_x, spec_max_y;
#endif
{
int code = 0;
if (spec_max_x && x > x_max_clip)
code |= RIGHT;
else if (spec_min_x && x < x_min_clip)
code |= LEFT;
if (spec_max_y && y > y_max_clip)
code |= TOP;
else if (spec_min_y && y < y_min_clip)
code |= BOTTOM;
return code;
}
|