| /* |
| * 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, ¥d); |
| } 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; |
| } |