www.pudn.com > emGUI.rar > draw-arc.c


/* 
 *  ARC Drawing API 
 * 
 * 
 *  COPYRIGHT (c) 2001 - 2010. 
 *  emTech System Corporation. 
 * 
 *  The license and distribution terms for this file may be 
 *  found in found in the file LICENSE. 
 */ 
 
/*	Huangf emcore@263.net 
 */ 
 
/*	Stolen from microwindows 
 */ 
  
#include "emGUI.h" 
 
/* argument holder for pie, arc and ellipse functions*/ 
typedef struct { 
	WndID	wID; 
	GID		gID; 
	int		x0, y0; 
	int		rx, ry; 
	int		ax, ay; 
	int		bx, by; 
	int		adir, bdir; 
	int		type; 
} SLICE; 
 
/* 
 * Clip a line segment for arc or pie drawing. 
 * Returns 0 if line is clipped or on acceptable side, 1 if it's vertically 
 * on other side, otherwise 3. 
 */ 
static int 
clip_line( 
	SLICE 	*slice,  
	int 	xe,  
	int 	ye,  
	int 	dir,  
	int 	y,  
	int 	*x0, 
	int 	*x1 
) 
{ 
	/* hline on the same vertical side with the given edge? */ 
	if ((y >= 0 && ye >= 0) || (y < 0 && ye < 0)) { 
		int x; 
 
		if (ye == 0) x = xe; else 
		x = (int)(long)xe * y / ye; 
 
		if (x >= *x0 && x <= *x1) { 
			if (dir > 0) 
				*x0 = x; 
			else 
				*x1 = x; 
			return 0; 
		} else { 
			if (dir > 0) { 
				if (x <= *x0) 
					return 0; 
			} else { 
				if (x >= *x1) 
					return 0; 
			} 
		} 
		return 3; 
	} 
	return 1; 
} 
 
/* relative offsets, direction from left to right. */ 
static void 
draw_line( 
	SLICE	*slice,  
	int 	x0,  
	int 	y,  
	int 	x1 
) 
{ 
	int	dbl = (slice->adir > 0 && slice->bdir < 0); 
	int discard, ret; 
	int	x2 = x0, x3 = x1; 
 
	if (y == 0) { 
		if (slice->type != GRPIE) 
			return; 
		/* edges on different sides */ 
		if ((slice->ay <= 0 && slice->by >= 0) || 
		    (slice->ay >= 0 && slice->by <= 0)) { 
			if (slice->adir < 0)  { 
				if (x1 > 0) 
					x1 = 0; 
			} 
			if (slice->bdir > 0) { 
				if (x0 < 0) 
					x0 = 0; 
			} 
		} else { 
			if (!dbl) { 
				/* FIXME leaving in draws dot in center*/ 
				GrPoint(slice->wID, slice->gID, slice->x0, slice->y0); 
				return; 
			} 
		} 
		GrLine(slice->wID, slice->gID, slice->x0 + x0, slice->y0, slice->x0 + x1, slice->y0, TRUE); 
		return; 
	} 
 
	/* clip left edge / line */ 
	ret = clip_line(slice, slice->ax, slice->ay, slice->adir, y, &x0, &x1); 
 
	if (dbl) { 
		if (!ret) { 
			/* edges separate line to two parts */ 
			GrLine( 
				slice->wID,  
				slice->gID, 
				slice->x0 + x0,  
				slice->y0 + y, 
				slice->x0 + x1, 
				slice->y0 + y, 
				TRUE 
			); 
			x0 = x2; 
			x1 = x3; 
		} 
	}  
	else { 
		if (ret > 1) { 
			return; 
		} 
	} 
 
	discard = ret; 
	ret = clip_line(slice, slice->bx, slice->by, slice->bdir, y, &x0, &x1); 
 
	discard += ret; 
	if (discard > 2 && !(dbl && ret == 0 && discard == 3)) { 
		return; 
	} 
	if (discard == 2) { 
		/* line on other side than slice */ 
		if (slice->adir < 0 || slice->bdir > 0) { 
			return; 
		} 
	} 
	GrLine( 
		slice->wID,  
		slice->gID, 
		slice->x0 + x0,  
		slice->y0 + y, 
		slice->x0 + x1,  
		slice->y0 + y, 
		TRUE 
	); 
} 
 
/* draw one line segment or set of points, called from drawarc routine*/ 
static void 
drawarcsegment(SLICE *slice, int xp, int yp) 
{ 
	switch (slice->type & 0x0f) { 
		case GRELLIPSEFILL: 
			/* draw ellipse fill segment*/ 
			GrLine( 
				slice->wID,  
				slice->gID, 
				slice->x0 - xp,  
				slice->y0 - yp, 
				slice->x0 + xp,  
				slice->y0 - yp, 
				TRUE 
			); 
			GrLine( 
				slice->wID,  
				slice->gID, 
				slice->x0 - xp,  
				slice->y0 + yp, 
				slice->x0 + xp,  
				slice->y0 + yp, 
				TRUE 
			); 
			return; 
 
		case GRELLIPSE: 
			/* set four points symmetrically situated around a point*/ 
			GrPoint( 
				slice->wID,  
				slice->gID, 
				slice->x0 + xp,  
				slice->y0 + yp 
			); 
			GrPoint( 
				slice->wID,  
				slice->gID, 
				slice->x0 - xp,  
				slice->y0 + yp 
			); 
			GrPoint( 
				slice->wID,  
				slice->gID, 
				slice->x0 + xp,  
				slice->y0 - yp 
			); 
			GrPoint( 
				slice->wID,  
				slice->gID, 
				slice->x0 - xp,  
				slice->y0 - yp 
			); 
			return; 
 
		case GRPIE: 
			/* draw top and bottom halfs of pie*/ 
			if (slice->type & GRLEFTTOP){ 
				draw_line(slice, -xp, -yp, 0); 
			} 
			if (slice->type & GRRIGHTTOP){ 
				draw_line(slice, 0, -yp, +xp); 
			} 
			if (slice->type & GRLEFTBOTTOM){ 
				draw_line(slice, -xp, +yp, 0); 
			}	 
			if (slice->type & GRRIGHTBOTTOM){ 
				draw_line(slice, 0, +yp, +xp); 
			}	 
			return; 
 
		default:	/* GRARC, GRARCOUTLINE*/ 
			/* set four points symmetrically around a point and clip*/ 
			if (slice->type & GRRIGHTBOTTOM){ 
				draw_line(slice, +xp, +yp, +xp); 
			}	 
			if (slice->type & GRLEFTBOTTOM){ 
				draw_line(slice, -xp, +yp, -xp); 
			}	 
			if (slice->type & GRRIGHTTOP){ 
				draw_line(slice, +xp, -yp, +xp); 
			} 
			if (slice->type & GRLEFTTOP){ 
				draw_line(slice, -xp, -yp, -xp); 
			}	 
			return; 
	} 
} 
 
/* General routine to plot points on an arc.  Used by arc, pie and ellipse*/ 
static void 
drawarc(SLICE *slice) 
{ 
	int xp, yp;		/* current point (based on center) */ 
	int rx, ry; 
	long Asquared;		/* square of x semi axis */ 
	long TwoAsquared; 
	long Bsquared;		/* square of y semi axis */ 
	long TwoBsquared; 
	long d; 
	long dx, dy; 
 
	rx = slice->rx; 
	ry = slice->ry; 
 
	xp = 0; 
	yp = ry; 
	Asquared = rx * rx; 
	TwoAsquared = 2 * Asquared; 
	Bsquared = ry * ry; 
	TwoBsquared = 2 * Bsquared; 
	d = Bsquared - Asquared * ry + (Asquared >> 2); 
	dx = 0; 
	dy = TwoAsquared * ry; 
 
	while (dx < dy) { 
		drawarcsegment(slice, xp, yp); 
		if (d > 0) { 
			yp--; 
			dy -= TwoAsquared; 
			d -= dy; 
		} 
		xp++; 
		dx += TwoBsquared; 
		d += (Bsquared + dx); 
	} 
 
	d += ((3L * (Asquared - Bsquared) / 2L - (dx + dy)) >> 1); 
 
	while (yp >= 0) { 
		drawarcsegment(slice, xp, yp); 
		if (d < 0) { 
			xp++; 
			dx += TwoBsquared; 
			d += dx; 
		} 
		yp--; 
		dy -= TwoAsquared; 
		d += (Asquared - dy); 
	} 
 
} 
 
/*  
 * Draw an arc or pie using start/end points. 
 * Integer only routine.  To specify start/end angles,  
 * use GdArcAngle, which requires floating point. 
 */ 
void 
GrArc( 
	WndID	wID,  
	GID		gID, 
	int 	x0,  
	int 	y0,  
	int 	rx,  
	int 	ry, 
	int 	ax,  
	int 	ay,  
	int 	bx,  
	int 	by,  
	int 	type 
) 
{ 
	int	adir, bdir; 
	SLICE	slice; 
 
	if (rx <= 0 || ry <= 0) 
		return; 
 
	/* 
	 * Calculate right/left side clipping, based on quadrant. 
	 * dir is positive when right side is filled and negative when 
	 * left side is to be filled. 
	 * 
	 * >= 0 is bottom half 
	 */ 
	if (ay >= 0) 
		adir = 1; 
	else 
		adir = -1; 
 
	if (by >= 0) 
		bdir = -1; 
	else 
		bdir = 1; 
 
	/* 
	 * The clip_line routine has problems around the 0 and 
	 * 180 degree axes. 
	 * This  is required to make the clip_line algorithm 
	 * work.  Getting these routines to work for all angles is 
	 * a bitch.  And they're still buggy.  Doing this causes 
	 * half circles to be outlined with a slightly bent line 
	 * on the x axis. FIXME 
	 */ 
	if (ay == 0) ++ay; 
	if (by == 0) ++by; 
 
	/* swap rightmost edge first */ 
	if (bx > ax) { 
		int swap; 
 
		swap = ax; 
		ax = bx; 
		bx = swap; 
 
		swap = ay; 
		ay = by; 
		by = swap; 
 
		swap = adir; 
		adir = bdir; 
		bdir = swap; 
	} 
 
	slice.wID = wID; 
	slice.gID = gID; 
	slice.x0 = x0; 
	slice.y0 = y0; 
	slice.rx = rx; 
	slice.ry = ry; 
	slice.ax = ax; 
	slice.ay = ay; 
	slice.bx = bx; 
	slice.by = by; 
	slice.adir = adir; 
	slice.bdir = bdir; 
	slice.type = type; 
 
	drawarc(&slice); 
 
	if (type & GROUTLINE) { 
		/* draw two lines from rx,ry to arc endpoints*/ 
		GrLine(wID, gID, x0, y0, x0+ax, y0+ay, TRUE); 
		GrLine(wID, gID, x0, y0, x0+bx, y0+by, TRUE); 
	} 
}