blob: d2cf5d8ba5ae7d071ad923328d2f72b985b29ad1 [file] [log] [blame]
/*
* Source code from Xfig 3.2.4 modified to work with arrays of doubles
* instead linked lists of F_points and to remove some globals(!)
* See copyright etc below.
*
* #included from engine.c
* That manages the R_alloc stack.
*/
/*
* From w_drawprim.h
*/
#define MAXNUMPTS 25000
/*
* From u_draw.c
*/
/*
* FIG : Facility for Interactive Generation of figures
* Copyright (c) 1985-1988 by Supoj Sutanthavibul
* Parts Copyright (c) 1989-2002 by Brian V. Smith
* Parts Copyright (c) 1991 by Paul King
* Parts Copyright (c) 1992 by James Tough
* Parts Copyright (c) 1998 by Georg Stemmer
* Parts Copyright (c) 1995 by C. Blanc and C. Schlick
*
* Any party obtaining a copy of these files is granted, free of charge, a
* full and unrestricted irrevocable, world-wide, paid up, royalty-free,
* nonexclusive right and license to deal in this software and
* documentation files (the "Software"), including without limitation the
* rights to use, copy, modify, merge, publish and/or distribute copies of
* the Software, and to permit persons who receive copies from any such
* party to do so, with the only requirement being that this copyright
* notice remain intact.
*
*/
/************** POLYGON/CURVE DRAWING FACILITIES ****************/
static int npoints;
static int max_points;
static double *xpoints;
static double *ypoints;
/************* Code begins here *************/
/* R_allocs or mallocs global arrays */
static Rboolean
add_point(double x, double y, pGEDevDesc dd)
{
if (npoints >= max_points) {
int tmp_n;
double *tmp_px;
double *tmp_py;
tmp_n = max_points + 200;
/* too many points, return false */
if (tmp_n > MAXNUMPTS) {
error(_("add_point - reached MAXNUMPTS (%d)"),tmp_n);
}
if (max_points == 0) {
tmp_px = (double *) R_alloc(tmp_n, sizeof(double));
tmp_py = (double *) R_alloc(tmp_n, sizeof(double));
} else {
tmp_px = (double *) S_realloc((char *) xpoints,
tmp_n, max_points,
sizeof(double));
tmp_py = (double *) S_realloc((char *) ypoints,
tmp_n, max_points,
sizeof(double));
}
if (tmp_px == NULL || tmp_py == NULL) {
error(_("insufficient memory to allocate point array"));
}
xpoints = tmp_px;
ypoints = tmp_py;
max_points = tmp_n;
}
/* ignore identical points */
if (npoints > 0 && xpoints[npoints-1] == x && ypoints[npoints-1] == y)
return TRUE;
/*
* Convert back from 1200ppi to DEVICE coordinates
*/
xpoints[npoints] = toDeviceX(x / 1200, GE_INCHES, dd);
ypoints[npoints] = toDeviceY(y / 1200, GE_INCHES, dd);
npoints = npoints + 1;
return TRUE;
}
/*
* From u_draw_spline.c
*/
/*
* FIG : Facility for Interactive Generation of figures
* Copyright (c) 1985-1988 by Supoj Sutanthavibul
* Parts Copyright (c) 1989-2002 by Brian V. Smith
* Parts Copyright (c) 1991 by Paul King
*
* Any party obtaining a copy of these files is granted, free of charge, a
* full and unrestricted irrevocable, world-wide, paid up, royalty-free,
* nonexclusive right and license to deal in this software and
* documentation files (the "Software"), including without limitation the
* rights to use, copy, modify, merge, publish and/or distribute copies of
* the Software, and to permit persons who receive copies from any such
* party to do so, with the only requirement being that this copyright
* notice remain intact.
*
*/
/* THIS FILE IS #included FROM u_draw.c and u_geom.c */
/********************* CURVES FOR SPLINES *****************************
The following spline drawing routines are from
"X-splines : A Spline Model Designed for the End User"
by Carole BLANC and Christophe SCHLICK,
in Proceedings of SIGGRAPH ' 95
***********************************************************************/
#define HIGH_PRECISION 0.5
#define LOW_PRECISION 1.0
#define ZOOM_PRECISION 5.0
#define ARROW_START 4
#define MAX_SPLINE_STEP 0.2
/***********************************************************************/
#define Q(s) (-(s))
#define EQN_NUMERATOR(dim) \
(A_blend[0]*dim[0]+A_blend[1]*dim[1]+A_blend[2]*dim[2]+A_blend[3]*dim[3])
static double
f_blend(double numerator, double denominator)
{
double p = 2 * denominator * denominator;
double u = numerator / denominator;
double u2 = u * u;
return (u * u2 * (10 - p + (2*p - 15)*u + (6 - p)*u2));
}
static double
g_blend(double u, double q) /* p equals 2 */
{
return(u*(q + u*(2*q + u*(8 - 12*q + u*(14*q - 11 + u*(4 - 5*q))))));
}
static double
h_blend(double u, double q)
{
double u2=u*u;
return (u * (q + u * (2 * q + u2 * (-2*q - u*q))));
}
static void
negative_s1_influence(double t, double s1, double *A0, double *A2)
{
*A0 = h_blend(-t, Q(s1));
*A2 = g_blend(t, Q(s1));
}
static void
negative_s2_influence(double t, double s2, double *A1, double *A3)
{
*A1 = g_blend(1-t, Q(s2));
*A3 = h_blend(t-1, Q(s2));
}
static void
positive_s1_influence(double k, double t, double s1, double *A0, double *A2)
{
double Tk;
Tk = k+1+s1;
*A0 = (t+k+1<Tk) ? f_blend(t+k+1-Tk, k-Tk) : 0.0;
Tk = k+1-s1;
*A2 = f_blend(t+k+1-Tk, k+2-Tk);
}
static void
positive_s2_influence(double k, double t, double s2, double *A1, double *A3)
{
double Tk;
Tk = k+2+s2;
*A1 = f_blend(t+k+1-Tk, k+1-Tk);
Tk = k+2-s2;
*A3 = (t+k+1>Tk) ? f_blend(t+k+1-Tk, k+3-Tk) : 0.0;
}
static void
point_adding(double *A_blend, double *px, double *py,
pGEDevDesc dd)
{
double weights_sum;
weights_sum = A_blend[0] + A_blend[1] + A_blend[2] + A_blend[3];
add_point(EQN_NUMERATOR(px) / (weights_sum),
EQN_NUMERATOR(py) / (weights_sum),
dd);
}
static void
point_computing(double *A_blend,
double *px, double *py,
double *x, double *y)
{
double weights_sum;
weights_sum = A_blend[0] + A_blend[1] + A_blend[2] + A_blend[3];
*x = EQN_NUMERATOR(px) / (weights_sum);
*y = EQN_NUMERATOR(py) / (weights_sum);
}
static double
step_computing(int k,
double *px, double *py,
double s1, double s2,
double precision,
pGEDevDesc dd)
{
double A_blend[4];
double xstart, ystart, xend, yend, xmid, ymid, xlength, ylength;
double start_to_end_dist, devWidth, devHeight, devDiag, number_of_steps;
double step, angle_cos, scal_prod, xv1, xv2, yv1, yv2, sides_length_prod;
/* This function computes the step used to draw the segment (p1, p2)
(xv1, yv1) : coordinates of the vector from middle to origin
(xv2, yv2) : coordinates of the vector from middle to extremity */
if ((s1 == 0) && (s2 == 0))
return(1.0); /* only one step in case of linear segment */
/* compute coordinates of the origin */
if (s1>0) {
if (s2<0) {
positive_s1_influence(k, 0.0, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(0.0, s2, &A_blend[1], &A_blend[3]);
} else {
positive_s1_influence(k, 0.0, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, 0.0, s2, &A_blend[1], &A_blend[3]);
}
point_computing(A_blend, px, py, &xstart, &ystart);
} else {
xstart = px[1];
ystart = py[1];
}
/* compute coordinates of the extremity */
if (s2>0) {
if (s1<0) {
negative_s1_influence(1.0, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, 1.0, s2, &A_blend[1], &A_blend[3]);
} else {
positive_s1_influence(k, 1.0, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, 1.0, s2, &A_blend[1], &A_blend[3]);
}
point_computing(A_blend, px, py, &xend, &yend);
} else {
xend = px[2];
yend = py[2];
}
/* compute coordinates of the middle */
if (s2>0) {
if (s1<0) {
negative_s1_influence(0.5, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, 0.5, s2, &A_blend[1], &A_blend[3]);
} else {
positive_s1_influence(k, 0.5, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, 0.5, s2, &A_blend[1], &A_blend[3]);
}
} else if (s1<0) {
negative_s1_influence(0.5, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(0.5, s2, &A_blend[1], &A_blend[3]);
} else {
positive_s1_influence(k, 0.5, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(0.5, s2, &A_blend[1], &A_blend[3]);
}
point_computing(A_blend, px, py, &xmid, &ymid);
xv1 = xstart - xmid;
yv1 = ystart - ymid;
xv2 = xend - xmid;
yv2 = yend - ymid;
scal_prod = xv1*xv2 + yv1*yv2;
sides_length_prod = sqrt((xv1*xv1 + yv1*yv1)*(xv2*xv2 + yv2*yv2));
/* compute cosinus of origin-middle-extremity angle, which approximates the
curve of the spline segment */
if (sides_length_prod == 0.0)
angle_cos = 0.0;
else
angle_cos = scal_prod/sides_length_prod;
xlength = xend - xstart;
ylength = yend - ystart;
start_to_end_dist = sqrt(xlength*xlength + ylength*ylength);
/* Paul 2009-01-25
* It is possible for origin and extremity to be very remote
* indeed (if the control points are located WAY off the device).
* In order to avoid having ridiculously many steps, limit
* the start_to_end_dist to being the length of the diagonal of the
* device.
*/
devWidth = fromDeviceWidth(toDeviceWidth(1, GE_NDC, dd),
GE_INCHES, dd)*1200;
devHeight = fromDeviceHeight(toDeviceHeight(1, GE_NDC, dd),
GE_INCHES, dd)*1200;
devDiag = sqrt(devWidth* devWidth + devHeight*devHeight);
if (start_to_end_dist > devDiag)
start_to_end_dist = devDiag;
/* more steps if segment's origin and extremity are remote */
number_of_steps = sqrt(start_to_end_dist)/2;
/* more steps if the curve is high */
number_of_steps += (int)((1 + angle_cos)*10);
if (number_of_steps == 0)
step = 1;
else
step = precision/number_of_steps;
if ((step > MAX_SPLINE_STEP) || (step == 0))
step = MAX_SPLINE_STEP;
return (step);
}
static void
spline_segment_computing(double step, int k,
double *px, double *py,
double s1, double s2,
pGEDevDesc dd)
{
double A_blend[4];
double t;
if (s1<0) {
if (s2<0) {
for (t=0.0 ; t<1 ; t+=step) {
negative_s1_influence(t, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
}
} else {
for (t=0.0 ; t<1 ; t+=step) {
negative_s1_influence(t, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
}
}
} else if (s2<0) {
for (t=0.0 ; t<1 ; t+=step) {
positive_s1_influence(k, t, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
}
} else {
for (t=0.0 ; t<1 ; t+=step) {
positive_s1_influence(k, t, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
}
}
}
/*
* For adding last line segment when computing open spline
* WITHOUT end control points repeated
* (i.e., can't just connect to last control point)
*/
static void
spline_last_segment_computing(double step, int k,
double *px, double *py,
double s1, double s2,
pGEDevDesc dd)
{
double A_blend[4];
double t = 1;
if (s1<0) {
if (s2<0) {
negative_s1_influence(t, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
} else {
negative_s1_influence(t, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
}
} else if (s2<0) {
positive_s1_influence(k, t, s1, &A_blend[0], &A_blend[2]);
negative_s2_influence(t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
} else {
positive_s1_influence(k, t, s1, &A_blend[0], &A_blend[2]);
positive_s2_influence(k, t, s2, &A_blend[1], &A_blend[3]);
point_adding(A_blend, px, py, dd);
}
}
/********************* MAIN METHODS *************************************/
/*
* x and y are in DEVICE coordinates
* xfig works in 1200ppi
* (http://www.csit.fsu.edu/~burkardt/data/fig/fig_format.html)
* so convert to 1200ppi so that step calculations are correct
*/
#define COPY_CONTROL_POINT(PI, I, N) \
px[PI] = fromDeviceX(x[(I) % N], GE_INCHES, dd) * 1200; \
py[PI] = fromDeviceY(y[(I) % N], GE_INCHES, dd) * 1200; \
ps[PI] = s[(I) % N]
#define NEXT_CONTROL_POINTS(K, N) \
COPY_CONTROL_POINT(0, K, N); \
COPY_CONTROL_POINT(1, K + 1, N); \
COPY_CONTROL_POINT(2, K + 2, N); \
COPY_CONTROL_POINT(3, K + 3, N)
#define INIT_CONTROL_POINTS(N) \
COPY_CONTROL_POINT(0, N - 1, N); \
COPY_CONTROL_POINT(1, 0, N); \
COPY_CONTROL_POINT(2, 1, N); \
COPY_CONTROL_POINT(3, 2, N)
#define SPLINE_SEGMENT_LOOP(K, PX, PY, S1, S2, PREC) \
step = step_computing(K, PX, PY, S1, S2, PREC, dd); \
spline_segment_computing(step, K, PX, PY, S1, S2, dd)
static Rboolean
compute_open_spline(int n, double *x, double *y, double *s,
Rboolean repEnds,
double precision,
pGEDevDesc dd)
{
int k;
double step = 0.0 /* -Wall */;
double px[4];
double py[4];
double ps[4]={0.,0.,0.,0.};
max_points = 0;
npoints = 0;
xpoints = NULL;
ypoints = NULL;
if (repEnds && n < 2)
error(_("there must be at least two control points"));
if (!repEnds && n < 4)
error(_("there must be at least four control points"));
if (repEnds) {
/* first control point is needed twice for the first segment */
COPY_CONTROL_POINT(0, 0, n);
COPY_CONTROL_POINT(1, 0, n);
COPY_CONTROL_POINT(2, 1, n);
if (n == 2) {
COPY_CONTROL_POINT(3, 1, n);
} else {
COPY_CONTROL_POINT(3, 2, n);
}
for (k = 0 ; ; k++) {
SPLINE_SEGMENT_LOOP(k, px, py, ps[1], ps[2], precision);
/* Paul 2008-12-05
* >= rather than == to handle special case of n == 2
*/
if (k + 3 >= n)
break;
NEXT_CONTROL_POINTS(k, n);
}
/* last control point is needed twice for the last segment */
COPY_CONTROL_POINT(0, n - 3, n);
COPY_CONTROL_POINT(1, n - 2, n);
COPY_CONTROL_POINT(2, n - 1, n);
COPY_CONTROL_POINT(3, n - 1, n);
SPLINE_SEGMENT_LOOP(k, px, py, ps[1], ps[2], precision);
add_point(px[3], py[3], dd);
} else {
for (k = 0 ; k + 3 < n ; k++) {
NEXT_CONTROL_POINTS(k, n);
SPLINE_SEGMENT_LOOP(k, px, py, ps[1], ps[2], precision);
}
spline_last_segment_computing(step, n - 4, px, py, ps[1], ps[2], dd);
}
return TRUE;
}
static Rboolean
compute_closed_spline(int n, double *x, double *y, double *s,
double precision,
pGEDevDesc dd)
{
int k;
double step;
double px[4];
double py[4];
double ps[4];
max_points = 0;
npoints = 0;
xpoints = NULL;
ypoints = NULL;
if (n < 3)
error(_("There must be at least three control points"));
INIT_CONTROL_POINTS(n);
for (k = 0 ; k < n ; k++) {
SPLINE_SEGMENT_LOOP(k, px, py, ps[1], ps[2], precision);
NEXT_CONTROL_POINTS(k, n);
}
return TRUE;
}