Commit | Line | Data |
---|---|---|
66c27cd0 DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
8dd20b65 | 7 | #ifndef lint |
66c27cd0 DF |
8 | static char sccsid[] = "@(#)arc.c 5.2 (Berkeley) %G%"; |
9 | #endif not lint | |
10 | ||
8dd20b65 RC |
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 | } |