1 /* @(#)graphics2.c	1.2	04/18/83
2  *
3  * Copyright -C- 1982 Barry S. Roitblat
4  *
5  * This file contains more routines for implementing the graphics primitives
6  * for the gremlin picture editor
7  *
8  * (Modified from software written by John Ousterhout for the caesar
9  *  program)
10  */
11 
12 #include "gremlin.h"
13 #include "grem2.h"
14 
15 /* imports from graphics1.c */
16 
17 extern GRsetlinestyle(), GRsetcharstyle(), GRsetcharsize(),
18        GRsetpos(), GRoutxy20(), GRSetRead();
19 extern FILE *display;
20 extern int curx, cury, rmask, GrXMax, GrYMax, charysize, charxsize, descenders;
21 
22 /* imports from main.c */
23 
24 extern error();
25 
26 /* The following is available to the outside world */
27 
28 int artmode;
29 
30 GRVector(point1,point2,style)
31 POINT *point1,*point2;
32 int   style;
33 {
34 	GRsetlinestyle(style);
35 	GRsetpos((int) point1->x,(int) point1->y);
36 	putc('A',display);       /* Draw vector absolute */
37 	GRoutxy20((int) point2->x,(int) point2->y);
38 	curx = point2->x;
39 	cury = point2->y;
40         (void) fflush(display);
41 }
42 
43 
44 static RelativeVector(x, y)
45 float x, y;
46 /*
47  *     This routine outputs the proper character sequence to the AED
48  * to draw a vector in the current line style and from the current position
49  * to the point (x, y).
50  */
51 
52 {
53 	int nextx, nexty;
54 
55 	nextx = (int) x;
56 	nexty = (int) y;
57 
58 	putc('A', display);  /* draw vector absolute */
59 	GRoutxy20(nextx, nexty); /* output coordinates */
60 	curx = nextx;
61 	cury = nexty;
62 }  /* end RelativeVector */
63 
64 
65 #define pi 3.14159265357
66 #define log2_10 3.321915
67 
68 int
69 GRArc(center,cpoint,angle,style)
70 POINT *center, *cpoint;
71 double angle;
72 int   style;
73 {
74 	double xs, ys, radius, resolution, t1, epsalon, fullcircle;
75 	double degreesperpoint;
76 	int    i, extent;
77 	POINT  next;
78 
79 	xs = cpoint->x - center->x;
80 	ys = cpoint->y - center->y;
81 	if ((xs == 0) && (ys == 0))       /* radius = 0 */
82 	{
83 		error("zero radius");
84 		return(-1);
85 	}
86 
87 /* calculate drawing parameters */
88 
89 	radius = sqrt(xs * xs + ys * ys);
90 	t1 = floor(log10(radius) * log2_10);
91 	resolution = pow(2.0, t1);
92 	epsalon = 1.0 / resolution;
93 	fullcircle = ceil(2 * pi * resolution);
94 	degreesperpoint = 360.0 / fullcircle;
95 
96 	if (angle == 0)
97 	{
98 		extent = fullcircle;
99 		if ( (int) radius < 128)    /* manageable by AED hardware */
100 		{
101         		GRsetpos((int) center->x, (int) center->y);
102                 	GRsetlinestyle(style);
103 			putc('O',display);  /* draw circle */
104 			putc(((int) radius) &0377, display);
105 			return(0);
106 		}
107 	}
108 	else extent = angle/degreesperpoint;
109 
110         GRsetpos((int) cpoint->x, (int) cpoint->y);
111         GRsetlinestyle(style);
112 	for (i=0; i<extent; ++i)
113 	{
114 		xs -= epsalon * ys;
115 		next.x = xs + center->x ;   /* round */
116 		ys += epsalon * xs;
117 		next.y = ys + center->y ;   /* round */
118 		RelativeVector(next.x, next.y);
119 	}   /* end for */;
120 	return(0);
121 }  /* end GRArc */;
122 
123 
124 #define MAXPOINTS 200
125 
126 static Paramaterize(x, y, h, n)
127 float x[MAXPOINTS], y[MAXPOINTS], h[MAXPOINTS];
128 int n;
129 /*     This routine calculates parameteric values for use in calculating
130  * curves.  The parametric values are returned in the array u.  The values
131  * are an approximation of cumulative arc lengths of the curve (uses cord
132  * length).  For additional information, see paper cited below.
133  */
134 
135 {
136 	int i,j;
137 	float u[MAXPOINTS];
138 
139 	for (i=1; i<=n; ++i)
140 	{
141 		u[i] = 0;
142 		for (j=1; j<i; ++j)
143 		{
144 			u[i] += sqrt(pow((double) (x[j+1] - x[j]), (double) 2.0)
145 			         + pow((double) (y[j+1] - y[j]), (double) 2.0));
146 		}
147 	}
148 	for (i=1; i<n; ++i)
149 		h[i] = u[i+1] - u[i];
150 }  /* end Paramaterize */
151 
152 static PeriodicSpline(h, z, dz, d2z, d3z, npoints)
153 float h[MAXPOINTS], z[MAXPOINTS];	/* Point list and paramaterization  */
154 float dz[MAXPOINTS];			/* to return the 1st derivative */
155 float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
156 int npoints;				/* number of valid points */
157 /*
158  *     This routine solves for the cubic polynomial to fit a spline
159  * curve to the the points  specified by the list of values.
160  * The Curve generated is periodic.  The alogrithms for this
161  * curve are from the "Spline Curve Techniques" paper cited below.
162  */
163 
164 {
165 	float d[MAXPOINTS];
166 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
167 	float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
168 	int i;
169 
170 	            /* step 1 */
171 	for (i=1; i<npoints; ++i)
172 	{
173 		if (h[i] != 0)
174 			deltaz[i] = (z[i+1] - z[i]) / h[i];
175 		else
176 			deltaz[i] = 0;
177 	}
178 	h[0] = h[npoints-1];
179 	deltaz[0] = deltaz[npoints-1];
180 
181 	            /* step 2 */
182 	for (i=1; i<npoints-1; ++i)
183 	{
184 		d[i] = deltaz[i+1] - deltaz[i];
185 	}
186 	d[0] = deltaz[1] - deltaz[0];
187 
188 	            /* step 3a */
189 	a[1] = 2 * (h[0] + h[1]);
190 	if (a[1] == 0) return(-1);  /* 3 consecutive knots at same point */
191 	b[1] = d[0];
192 	c[1] = h[0];
193 	for (i=2; i<npoints-1; ++i)
194 	{
195 		a[i] = 2 * (h[i-1] + h[i]) - pow((double) h[i-1], (double) 2.0)
196 		            / a[i-1];
197 		if (a[i] == 0) return(-1);  /* 3 consec knots at same point */
198 		b[i] = d[i-1] - h[i-1] * b[i-1]/a[i-1];
199 		c[i] = -h[i-1] * c[i-1]/a[i-1];
200 	}
201 
202 	            /* step 3b */
203 	r[npoints-1] = 1;
204 	s[npoints-1] = 0;
205 	for (i=npoints-2; i>0; --i)
206 	{
207 		r[i] = -(h[i] * r[i+1] + c[i])/a[i];
208 		s[i] = (6 * b[i] - h[i] * s[i+1])/a[i];
209 	}
210 
211 	            /* step 4 */
212 	d2z[npoints-1] = (6 * d[npoints-2] - h[0] * s[1]
213 	                   - h[npoints-1] * s[npoints-2])
214 	                 / (h[0] * r[1] + h[npoints-1] * r[npoints-2]
215 	                    + 2 * (h[npoints-2] + h[0]));
216 	for (i=1; i<npoints-1; ++i)
217 	{
218 		d2z[i] = r[i] * d2z[npoints-1] + s[i];
219 	}
220 	d2z[npoints] = d2z[1];
221 
222 	            /* step 5 */
223 	for (i=1; i<npoints; ++i)
224 	{
225 		dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
226 		if (h[i] != 0)
227 			d3z[i] = (d2z[i+1] - d2z[i])/h[i];
228 		else
229 			d3z[i] = 0;
230 	}
231 	return(0);
232 }  /* end PeriodicSpline */
233 
234 
235 static NaturalEndSpline(h, z, dz, d2z, d3z, npoints)
236 float h[MAXPOINTS], z[MAXPOINTS];	/* Point list and parameterization */
237 float dz[MAXPOINTS];			/* to return the 1st derivative */
238 float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
239 int npoints;				/* number of valid points */
240 /*
241  *     This routine solves for the cubic polynomial to fit a spline
242  * curve the the points  specified by the list of values.  The alogrithms for
243  * this curve are from the "Spline Curve Techniques" paper cited below.
244  */
245 
246 {
247 	float d[MAXPOINTS];
248 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
249 	int i;
250 
251 	            /* step 1 */
252 	for (i=1; i<npoints; ++i)
253 	{
254 		if (h[i] != 0)
255 			deltaz[i] = (z[i+1] - z[i]) / h[i];
256 		else
257 			deltaz[i] = 0;
258 	}
259 	deltaz[0] = deltaz[npoints-1];
260 
261 	            /* step 2 */
262 	for (i=1; i<npoints-1; ++i)
263 	{
264 		d[i] = deltaz[i+1] - deltaz[i];
265 	}
266 	d[0] = deltaz[1] - deltaz[0];
267 
268 	            /* step 3 */
269 	a[0] = 2 * (h[2] + h[1]);
270 	if (a[0] == 0) return(-1);  /* 3 consec knots at same point */
271 	b[0] = d[1];
272 	for (i=1; i<npoints-2; ++i)
273 	{
274 		a[i] = 2 * (h[i+1] + h[i+2]) - pow((double) h[i+1],(double) 2.0)
275 		             / a[i-1];
276 		if (a[i] == 0) return(-1);  /* 3 consec knots at same point */
277 		b[i] = d[i+1] - h[i+1] * b[i-1]/a[i-1];
278 	}
279 
280 	            /* step 4 */
281 	d2z[npoints] = d2z[1] = 0;
282 	for (i=npoints-1; i>1; --i)
283 	{
284 		d2z[i] = (6 * b[i-2] - h[i] *d2z[i+1])/a[i-2];
285 	}
286 
287 	            /* step 5 */
288 	for (i=1; i<npoints; ++i)
289 	{
290 		dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
291 		if (h[i] != 0)
292 			d3z[i] = (d2z[i+1] - d2z[i])/h[i];
293 		else
294 			d3z[i] = 0;
295 	}
296 	return(0);
297 }  /* end NaturalEndSpline */
298 
299 
300 #define PointsPerInterval 16
301 
302 GRCurve(pointlist,style)
303 POINT *pointlist;
304 int   style;
305 /*
306  *    This routine generates a smooth curve through a set of points.  The
307  * method used is the parametric spline curve on unit knot mesh described
308  * in "Spline Curve Techniques" by Patrick Baudelaire, Robert Flegal, and
309  * Robert Sproull -- Xerox Parc.
310  */
311 {
312 	float h[MAXPOINTS];
313 	float x[MAXPOINTS], y[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
314 	float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
315 	float t, t2, t3, xinter, yinter;
316 	POINT *ptr;
317 	int numpoints, i, j, k, stat;
318 
319 	GRsetlinestyle(style);
320 	GRsetpos((int) pointlist->x, (int) pointlist->y);
321 
322               /* Copy point list to array for easier access */
323 
324 	ptr = pointlist;
325 	for (i=1; (!Nullpoint(ptr)); ++i)
326 	{
327 		x[i] = ptr->x;
328 		y[i] = ptr->y;
329 		if (i >= MAXPOINTS - 1) break;
330 		ptr = PTNextPoint(ptr);
331 	}
332 
333 	     /* Solve for derivatives of the curve at each point
334               * separately for x and y (parametric).
335 	      */
336 	numpoints = i - 1;
337 	Paramaterize(x, y, h, numpoints);
338   	stat = 0;
339 	if ((x[1] == x[numpoints]) && (y[1] == y[numpoints]))/* closed curve */
340 	{
341 		stat |= PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
342 		stat |= PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
343 	}
344 	else
345 	{
346 		stat |= NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
347 		stat |= NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
348 	}
349 	if (stat != 0) return(-1);  /* error occurred */
350 
351 	      /* generate the curve using the above information and
352 	       * PointsPerInterval vectors between each specified knot.
353 	       */
354 
355 	for (j=1; j<numpoints; ++j)
356 	{
357 		for (k=0; k<=PointsPerInterval; ++k)
358 		{
359 			t = (float) k * h[j] / (float) PointsPerInterval;
360 			t2 = t * t;
361 			t3 = t * t * t;
362 			xinter = x[j] + t * dx[j] + t2 * d2x[j]/2.0
363 			       + t3 * d3x[j]/6.0;
364 			yinter = y[j] + t * dy[j] + t2 * d2y[j]/2.0
365 			       + t3 * d3y[j]/6.0;
366 			RelativeVector(xinter, yinter);
367 		}  /* end for k */
368 	}  /* end for j */
369 	return(0);
370 }  /* end GRCurve */
371 
372 
373 
374 GRPutText(justify,point1,font,size,string,pos)
375 int justify, font, size;
376 POINT *point1, *pos;
377 char string[];
378 {
379 	int length, nchars, i;
380 
381 	GRsetcharstyle(font);
382 	GRsetcharsize(size);
383 	nchars = strlen(string);
384 	length = nchars * charxsize ;
385         switch (justify)
386 	{
387 		  case BOTLEFT:	pos->x = point1->x;
388 				pos->y = point1->y;
389 				break;
390 		  case BOTCENT:	pos->x = point1->x - (length/2);
391 				pos->y = point1->y;
392 				break;
393 		 case BOTRIGHT:	pos->x = point1->x - length;
394 				pos->y = point1->y;
395 				break;
396 		 case CENTLEFT:	pos->x = point1->x;
397 				pos->y = point1->y - (charysize/2);
398 				break;
399 		 case CENTCENT:	pos->x = point1->x - (length/2);
400 				pos->y = point1->y - (charysize/2);
401 				break;
402 		case CENTRIGHT:	pos->x = point1->x - length;
403 				pos->y = point1->y - (charysize/2);
404 				break;
405 		  case TOPLEFT:	pos->x = point1->x;
406 				pos->y = point1->y - charysize + descenders;
407 				break;
408 		  case TOPCENT:	pos->x = point1->x - (length/2);
409 				pos->y = point1->y - charysize + descenders;
410 				break;
411 		 case TOPRIGHT:	pos->x = point1->x - length;
412 				pos->y = point1->y - charysize + descenders;
413 				break;
414 	}
415 	if ((pos->x < 0) || ((pos->x + length - charxsize) > GrXMax))
416 	{
417 		TxPutMsg("\7string too long");
418 	}
419 	if (( pos->y + charysize ) > GrYMax )
420 		pos->y = GrYMax - charysize;
421 	if (pos->y < 0) pos->y = 0;
422 	GRsetpos((int) pos->x, (int) pos->y);
423 	putc('\6',display);    /* enter text mode */
424 	for ( i=0; i<nchars; ++i )
425 	{
426 		putc(string[i],display);
427 	}
428 	fputs("\33Q", display);  /* re-enter graphics mode */
429 	GRoutxy20(curx,cury);
430 	(void) fflush(display);
431 } /* end PutText */;
432 
433 
434 GRClear(mask)
435 int mask;
436 /*
437  *      This routine clears the memory planes enabled by mask.
438  * The read mask is first set to avoid an annoying flash when the
439  * clear is performed.
440  */
441 
442 {
443 	int savemask;
444 
445 	savemask = rmask;
446 	GRSetRead(rmask & ~mask);
447 	GRsetwmask(mask);
448 	putc('~',display);
449 	GRSetRead(savemask);
450 	(void) fflush(display);
451 }  /* end GRClear */
452 
453 GRDisplayPoint(x, y, number, mark)
454 int x, y, number, mark;
455 /*
456  *      This routine displays a point on the screen.  The point is
457  * displayed as a '+' centered about the point (x,y) and followed by
458  * number.
459  */
460 
461 {
462 	POINT p1, p2;
463 	int psize;
464 
465 	if (artmode) psize = 1;
466 	else psize = halfpoint;
467 	GRsetwmask(pointmask);
468 	p1.y = p2.y = y;
469 	p1.x = x - psize;
470 	p2.x = x + psize;
471 	GRVector(&p1, &p2, mark);
472 	p1.x = p2.x = x;
473 	p1.y = y - psize;
474 	p2.y = y + psize;
475 	GRVector(&p1, &p2, mark);
476 	if ( !artmode )
477 	{
478 		GRsetcharsize(pointchar);
479 		GRsetpos( (x + numspace), (y - charysize/2 + 1) );
480 		fprintf(display,"\6%d\33",number);
481 	}
482 	GRsetpos(x, y);
483 	(void) fflush(display);
484 }  /* end DisplayPoint */
485 
486 
487 
488 GRErasePoint(x, y, number)
489 int x, y, number;
490 /*
491  *      This routine erases the specified point by redrawing it in the
492  * background color
493  */
494 
495 {
496 	GRDisplayPoint(x, y, number, eraseany);
497 }  /* end ErasePoint */
498 
499 
500 GRBlankPoints()
501 /*
502  *      This routine clears all displayed points by clearing
503  * the memory plane they are written on.
504  */
505 
506 {
507 	GRClear(pointmask);
508 }  /* end BlankPoints */
509