Ilya Zakharevich on Thu, 20 May 1999 03:22:12 -0400 (EDT)

 [PATCH 2.0.14] Clipping rectangles

```This patch adds one more plotting function, plotclip(r), which clips
the contents of the rectangle to the bounding box of the rectangle.
With the addition of this function one can do "almost arbitrary
complicated" plots using middle-level plotting functions only.

Say, to plot sin(x), x in -5..5, together with its first 20 Taylor
approximations is not possible with high-level plotting functions:
they will choose a y-scale too small, since Taylor polynomials are so
big at x=5.

Thus one may plot them into a pre-sized rectwindow by plotrecth() with
flag=4.  However, the parts of this graph will "overflow" out of this
window, and these parts outside of the graph will be still visible on
screen (or will core a plotting driver if it expect data which fits
the screen).  Using plotclip(), one can avoid this problem.

Here is an example program:

\\ Setup display size info
\\ plotterm("dumb")
t = plothsizes()
w=floor(t[1]*0.9)-2
h=floor(t[2]*0.9)-2
h1=floor(h/1.2)
dw=floor(t[1]*0.05)+1
dh=floor(t[2]*0.05)+1
cw=t[5]
ch=t[6]

\\
extrasize = 0.6

\\ which functions to plot
xlim=13
ordlim=25
default(seriesprecision,ordlim)
f(x)=sin(x)

\\ Workhorse provider of the function/approximations
{ farray(x)=vector(1+(ordlim+1)/2,k,
if(k==1,f(x),
subst(Pol(f(1.*tt+tt^(2*(k-1))*O(tt)),tt),
tt,x)))
}

plotinit(2, w+2*dw, h+2*dh)
plotinit(3, w, h1);

s = plotrecth(3, x= -xlim, xlim, sin(x), 2+8+16+32)
gh=s[4]-s[3]

plotinit(3, w, h);
plotscale(3,s[1],s[2],s[3]-gh*extrasize/2,s[4]+gh*extrasize/2)
plotrecth(3, x= -xlim, xlim, farray(x), 4)

plotclip(3)
plotcopy(3, 2, dw, dh)

plotmove(2,floor(dw+w/2-15*cw),floor(dh/2))
plotstring(2,"Multiple Taylor Approximations")

plotdraw([2, 0, 0])

Enjoy,
Ilya

--- ./src/graph/rect.h.orig	Fri Mar  5 02:52:19 1999
+++ ./src/graph/rect.h	Thu May 20 01:36:08 1999
@@ -228,6 +228,7 @@ void    rectrmove(long ne, GEN x, GEN y)
void    rectrpoint(long ne, GEN x, GEN y);
void    rectscale(long ne, GEN x1, GEN x2, GEN y1, GEN y2);
void    rectstring(long ne, char *x);
+void    rectclip(long rect);

/* architecture-dependent plot file (plotX.c, plotsun.c, plognuplot.c...) */

--- ./src/graph/plotport.c.orig	Thu May 20 01:23:15 1999
+++ ./src/graph/plotport.c	Thu May 20 03:06:03 1999
@@ -705,6 +705,275 @@ rectcopy(long source, long dest, long xo
RoNext(tail) = NULL; RTail(d) = tail;
}

+#define CLIPLINE_NONEMPTY	1
+#define CLIPLINE_CLIP_1		2
+#define CLIPLINE_CLIP_2		4
+
+/* A simpler way is to clip by 4 half-planes */
+static int
+clipline(long xmin, long xmax, long ymin, long ymax, long *x1p, long *y1p, long *x2p, long *y2p)
+{
+    int res = 1, xy_exch = 0, rc = CLIPLINE_NONEMPTY;
+    double t;
+    double xi, yi;
+    double sl;
+    double xmn, xmx;
+    double ymn, ymx;
+    int x1_is_ymn, x1_is_xmn;
+    double x1 = *x1p, x2 = *x2p, y1 = *y1p, y2 = *y2p;
+
+    if ((x1 < xmin &&  x2 < xmin) || (x1 > xmax && x2 > xmax))
+	return 0;
+    if (fabs(x1 - x2) < fabs(y1 - y2)) { /* Exchange x and y */
+	xy_exch = 1;
+	t = xmin, xmin = ymin, ymin = t;
+	t = xmax, xmax = ymax, ymax = t;
+	t = x1, x1 = y1, y1 = t;
+	t = x2, x2 = y2, y2 = t;
+    }
+
+    /* Build y as a function of x */
+    xi = x1, yi = y1;
+    sl = ( (x1==x2) ? 0 : (y2 - yi)/(x2 - xi) );
+
+    xmn = x1, xmx = x2;
+    if (x1 > x2) {
+	x1_is_xmn = 0;
+	xmn = x2, xmx = x1;
+    } else
+	x1_is_xmn = 1;
+
+    if (xmn < xmin) {
+	xmn = xmin;
+	rc |= (x1_is_xmn ? CLIPLINE_CLIP_1 : CLIPLINE_CLIP_2);
+    }
+
+    if (xmx > xmax) {
+	xmx = xmax;
+	rc |= (x1_is_xmn ? CLIPLINE_CLIP_2 : CLIPLINE_CLIP_1);
+    }
+
+    if (xmn > xmx)
+	return 0;
+
+    ymn = yi + (xmn - xi)*sl;
+    ymx = yi + (xmx - xi)*sl;
+
+    if (sl < 0)
+	t = ymn, ymn = ymx, ymx = t;
+    if (ymn > ymax || ymx < ymin)
+	return 0;
+
+    if (rc & CLIPLINE_CLIP_1)
+	x1 = (x1_is_xmn ? xmn : xmx);
+    if (rc & CLIPLINE_CLIP_2)
+	x2 = (x1_is_xmn ? xmx : xmn);
+
+    /* Now we know there is an intersection, need to move x1 and x2 */
+    x1_is_ymn = ((sl >= 0) == (x1 < x2));
+    if (ymn < ymin) {
+	double x = (ymin - yi)/sl + xi; /* slope != 0  ! */
+
+	if (x1_is_ymn)
+	    x1 = x, rc |= CLIPLINE_CLIP_1;
+	else
+	    x2 = x, rc |= CLIPLINE_CLIP_2;
+    }
+    if (ymx > ymax) {
+	double x = (ymax - yi)/sl + xi; /* slope != 0  ! */
+
+	if (x1_is_ymn)
+	    x2 = x, rc |= CLIPLINE_CLIP_2;
+	else
+	    x1 = x, rc |= CLIPLINE_CLIP_1;
+    }
+    if (rc & CLIPLINE_CLIP_1)
+	y1 = yi + (x1 - xi)*sl;
+    if (rc & CLIPLINE_CLIP_2)
+	y2 = yi + (x2 - xi)*sl;
+    if (xy_exch)			/* Exchange x and y */
+	*x1p = y1, *x2p = y2, *y1p = x1, *y2p = x2;
+    else
+	*x1p = x1, *x2p = x2, *y1p = y1, *y2p = y2;
+    return rc;
+}
+
+void
+rectclip(long rect)
+{
+  PariRect *s = rectgraph[rect];
+  RectObj *next;
+/*  double xmin = RXshift(s);
+  double xmax = RXshift(s) + RXsize(s)/RXscale(s);
+  double ymin = RYshift(s);
+  double ymax = RYshift(s) + RYsize(s)/RYscale(s); */
+  double xmin = 0;
+  double xmax = RXsize(s);
+  double ymin = 0;
+  double ymax = RYsize(s);
+
+  while (p1) {
+      int did_clip = 0;
+
+      next = RoNext(p1);
+      switch(RoType(p1)) {
+      case ROt_PT:
+	  if ( RoPTx(p1) < xmin || RoPTx(p1) > xmax
+	       || RoPTy(p1) < ymin || RoPTy(p1) > ymax) {
+		 remove:
+	      *prevp = next;
+	      free(p1);
+	      break;
+	  }
+	  goto do_next;
+      case ROt_BX:
+	  if (RoLNx1(p1) < xmin)
+	      RoLNx1(p1) = xmin, did_clip = 1;
+	  if (RoLNx2(p1) < xmin)
+	      RoLNx2(p1) = xmin, did_clip = 1;
+	  if (RoLNy1(p1) < ymin)
+	      RoLNy1(p1) = ymin, did_clip = 1;
+	  if (RoLNy2(p1) < ymin)
+	      RoLNy2(p1) = ymin, did_clip = 1;
+	  if (RoLNx1(p1) > xmax)
+	      RoLNx1(p1) = xmax, did_clip = 1;
+	  if (RoLNx2(p1) > xmax)
+	      RoLNx2(p1) = xmax, did_clip = 1;
+	  if (RoLNy1(p1) > ymax)
+	      RoLNy1(p1) = ymax, did_clip = 1;
+	  if (RoLNy2(p1) > ymax)
+	      RoLNy2(p1) = ymax, did_clip = 1;
+	  /* Remove zero-size clipped boxes */
+	  if ( did_clip
+	       && RoLNx1(p1) == RoLNx2(p1) && RoLNy1(p1) == RoLNy2(p1) )
+	      goto remove;
+	  goto do_next;
+      case ROt_LN:
+	  if (!clipline(xmin, xmax, ymin, ymax,
+			&RoLNx1(p1), &RoLNy1(p1), &RoLNx2(p1), &RoLNy2(p1)))
+	      goto remove;
+	  goto do_next;
+      case ROt_MP:
+      {
+	  int c = RoMPcnt(p1);
+	  int f = 0, t = 0;
+
+	  while (f < c) {
+	      if ( RoMPxs(p1)[f] >= xmin && RoMPxs(p1)[f] <= xmax
+		   && RoMPys(p1)[f] >= ymin && RoMPys(p1)[f] <= ymax) {
+		  if (t != f) {
+		      RoMPxs(p1)[t] = RoMPxs(p1)[f];
+		      RoMPys(p1)[t] = RoMPys(p1)[f];
+		  }
+		  t++;
+	      }
+	      f++;
+	  }
+	  if (t == 0)
+	      goto remove;
+	  RoMPcnt(p1) = t;
+	  goto do_next;
+      }
+      case ROt_ML:
+      {
+	  /* Hard case.  Here we need to break a multiline into
+	     several pieces if some part is clipped. */
+	  int c = RoMPcnt(p1) - 1;
+	  int f = 0, t = 0, had_lines = 0, had_hole = 0, rc;
+	  long ox = RoMLxs(p1)[0], oy = RoMLys(p1)[0], oxn, oyn;
+
+	  while (f < c) {
+	      /* Endpoint of this segment is the startpoint of the
+		 next one, so we need to preserve it if it is clipped. */
+	      oxn = RoMLxs(p1)[f + 1], oyn = RoMLys(p1)[f + 1];
+	      rc = clipline(xmin, xmax, ymin, ymax,
+			    /* &RoMLxs(p1)[f], &RoMLys(p1)[f], */
+			    &ox, &oy,
+			    &RoMLxs(p1)[f+1], &RoMLys(p1)[f+1]);
+	      RoMLxs(p1)[f] = ox; RoMLys(p1)[f] = oy;
+	      ox = oxn; oy = oyn;
+	      if (!rc) {
+		  f++;
+		  continue;
+	      }
+
+	      if ( !had_lines || (!(rc & CLIPLINE_CLIP_1) && !had_hole) ) {
+		  /* Continuous */
+		  if (t != f) {
+		      if (t == 0) {
+			  RoMPxs(p1)[t] = RoMPxs(p1)[f];
+			  RoMPys(p1)[t] = RoMPys(p1)[f];
+		      }
+		      RoMPxs(p1)[t+1] = RoMPxs(p1)[f+1];
+		      RoMPys(p1)[t+1] = RoMPys(p1)[f+1];
+		  }
+		  t++;
+		  f++;
+		  if ( rc & CLIPLINE_CLIP_2)
+		      had_hole = 1, RoMLcnt(p1) = t + 1;
+		  continue;
+	      }
+	      /* Is not continuous, automatically p1 is not free()ed.  */
+	      RoMLcnt(p1) = t + 1;
+	      if ( rc & CLIPLINE_CLIP_2) { /* Needs separate entry */
+		  RectObj *n = (RectObj*) gpmalloc(sizeof(RectObj2P));
+
+		  RoType(n) = ROt_LN;
+		  RoCol(n) = RoCol(p1);
+		  RoLNx1(n) = RoMLxs(p1)[f];	RoLNy1(n) = RoMLys(p1)[f];
+		  RoLNx2(n) = RoMLxs(p1)[f+1];	RoLNy2(n) = RoMLys(p1)[f+1];
+		  RoNext(n) = next;
+		  RoNext(p1) = n;
+		  /* Restore the unclipped value: */
+		  RoMLxs(p1)[f + 1] = oxn;	RoMLys(p1)[f + 1] = oyn;
+		  f++;
+		  prevp = &RoNext(n);
+	      }
+	      if (f + 1 < c) {		/* Are other lines */
+		  RectObj *n = (RectObj*) gpmalloc(sizeof(RectObjMP));
+		  RoType(n) = ROt_ML;
+		  RoCol(n) = RoCol(p1);
+		  RoMLcnt(n) = c - f;
+		  RoMLxs(n) = (long*) gpmalloc(sizeof(long)*(c - f));
+		  RoMLys(n) = (long*) gpmalloc(sizeof(long)*(c - f));
+		  memcpy(RoMPxs(n),RoMPxs(p1) + f, sizeof(long)*(c - f));
+		  memcpy(RoMPys(n),RoMPys(p1) + f, sizeof(long)*(c - f));
+		  RoMPxs(n)[0] = oxn; RoMPys(n)[0] = oyn;
+		  RoNext(n) = next;
+		  RoNext(p1) = n;
+		  next = n;
+	      }
+	      goto do_next;
+	  }
+	  if (t == 0)
+	      goto remove;
+	  goto do_next;
+      }
+#if 0
+      case ROt_ST:
+	  next = (RectObj*) gpmalloc(sizeof(RectObjMP));
+	  memcpy(next,p1,sizeof(RectObjMP));
+	  RoSTs(next) = (char*) gpmalloc(RoSTl(p1)+1);
+	  memcpy(RoSTs(next),RoSTs(p1),RoSTl(p1)+1);
+	  RoSTx(next) += xoff; RoSTy(next) += yoff;
+	  RoNext(tail) = next; tail = next;
+	  break;
+#endif
+      default: {
+	do_next:
+	      prevp = &RoNext(p1);
+	      break;
+	  }
+      }
+      p1 = next;
+  }
+}
+
/********************************************************************/
/**                                                                **/
/**                        HI-RES PLOT                             **/
--- ./src/gp/highlvl.c.orig	Fri Mar  5 02:52:05 1999
+++ ./src/gp/highlvl.c	Thu May 20 01:36:29 1999
@@ -163,6 +163,7 @@ entree functions_highlevel[]={
{"kill",85,(void*)kill0,11,"vS"},
{"plot",83,(void*)plot,10,"vV=GGI"},
{"plotbox",35,(void*)rectbox,10,"vLGG"},
+{"plotclip",35,(void*)rectclip,10,"vL"},
{"plotcolor",99,(void*)rectcolor,10,"vLL"},
{"plotcopy",99,(void*)rectcopy,10,"vLLLL"},
{"plotcursor",11,(void*)rectcursor,10,"L"},
@@ -202,6 +203,7 @@ char *helpmessages_highlevel[]={
"kill(x):  kills the present value of the variable or function x. Returns new value or 0",
"plot(X=a,b,expr): crude plot of expression expr, X goes from a to b",
"plotbox(w,x2,y2): if the cursor is at position (x1,y1), draw a box with diagonal (x1,y1) and (x2,y2) in rectwindow w (cursor does not move)",
+  "plotclip(w): clip the contents of the rectwindow to the bounding box (except strings)",
"plotcolor(w,c): in rectwindow w, set default color to c. Possible values for c are 1=black, 2=blue, 3=sienna, 4=red, 5=cornsilk, 6=grey, 7=gainsborough",
"plotcopy(sourcew,destw,dx,dy): copy the contents of rectwindow sourcew to rectwindow destw with offset (dx,dy)",
"plotcursor(w): current position of cursor in rectwindow w",
```