xref: /original-bsd/lib/libplot/bitgraph/arc.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)arc.c	8.1 (Berkeley) 06/04/93";
10 #endif /* not lint */
11 
12 #include "bg.h"
13 
14 /* should include test for equality? */
15 #define side(x,y)	(a*(x)+b*(y)+c > 0.0 ? 1 : -1)
16 
17 /* The beginning and ending points must be distinct. */
18 arc(xc,yc,xbeg,ybeg,xend,yend)
19 int xc,yc,xbeg,ybeg,xend,yend;
20 {
21 	double r, radius, costheta, sintheta;
22 	double a, b, c, x, y, tempX;
23 	int right_side;
24 
25 	int screen_xc = scaleX(xc);
26 	int screen_yc = scaleY(yc);
27 
28 	/* It is more convienient to beg and end relative to center. */
29 	int screen_xbeg = scaleX(xbeg) - screen_xc;
30 	int screen_ybeg = scaleY(ybeg) - screen_yc;
31 
32 	int screen_xend = scaleX(xend) - screen_xc;
33 	int screen_yend = scaleY(yend) - screen_yc;
34 
35 	/* probably should check that arc is truely circular */
36 	r = sqrt( (double) (screen_xbeg*screen_xbeg + screen_ybeg*screen_ybeg) );
37 
38 	/*
39 	This method is reasonably efficient, clean, and clever.
40 	The easy part is generating the next point on the arc.  This is
41 	done by rotating the points by the angle theta.  Theta is chosen
42 	so that no rotation will cause more than one pixel of a move.
43 	This corresponds to a triangle having x side of r and y side of 1.
44 	The rotation is done (way) below inside the loop.
45 
46 	Note:  all calculations are done in screen coordinates.
47 	*/
48 	if (r <= 1.0) {
49 		/* radius is mapped to length < 1*/
50 		point(xc,yc);
51 		return;
52 		}
53 
54 	radius = sqrt(r*r + 1.0);
55 	sintheta = 1.0/radius;
56 	costheta = r/radius;
57 
58 	/*
59 	The hard part of drawing an arc is figuring out when to stop.
60 	This method works by drawing the line from the beginning point
61 	to the ending point.  This splits the plane in half, with the
62 	arc that we wish to draw on one side of the line.  If we evaluate
63 	side(x,y) = a*x + b*y + c, then all of the points on one side of the
64 	line will result in side being positive, and all the points on the
65 	other side of the line will result in side being negative.
66 
67 	We want to draw the arc in a counter-clockwise direction, so we
68 	must find out what the sign of "side" is for a point which is to the
69 	"right" of a line drawn from "beg" to "end".  A point which must lie
70 	on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)].  (This
71 	point is perpendicular to the line at "beg").
72 
73 	Thus, we compute side of the above point, and then compare the
74 	sign of side for each new point with the sign of the above point.
75 	When they are different, we terminate the loop.
76 	*/
77 
78 	a = (double) (screen_yend - screen_ybeg);
79 	b = (double) (screen_xend - screen_xbeg);
80 	c = (double) (screen_yend*screen_xbeg - screen_xend*screen_ybeg);
81 	right_side = side(screen_xbeg + (screen_yend-screen_ybeg),
82 			  screen_ybeg - (screen_xend-screen_xbeg) );
83 
84 	x = screen_xbeg;
85 	y = screen_ybeg;
86 	move(xbeg, ybeg);
87 	do {
88 		currentx = screen_xc + (int) (x + 0.5);
89 		currenty = screen_yc + (int) (y + 0.5);
90 		putchar( ESC );
91 		printf(":%d;%dd", currentx, currenty);
92 		tempX = x;
93 		x = x*costheta - y*sintheta;
94 		y = tempX*sintheta + y*costheta;
95 	} while( side(x,y) == right_side );
96 }
97