miarc.c revision 2f9f5fec
105b261ecSmrg/***********************************************************
205b261ecSmrg
305b261ecSmrgCopyright 1987, 1998  The Open Group
405b261ecSmrg
505b261ecSmrgPermission to use, copy, modify, distribute, and sell this software and its
605b261ecSmrgdocumentation for any purpose is hereby granted without fee, provided that
705b261ecSmrgthe above copyright notice appear in all copies and that both that
805b261ecSmrgcopyright notice and this permission notice appear in supporting
905b261ecSmrgdocumentation.
1005b261ecSmrg
1105b261ecSmrgThe above copyright notice and this permission notice shall be included in
1205b261ecSmrgall copies or substantial portions of the Software.
1305b261ecSmrg
1405b261ecSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1505b261ecSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1605b261ecSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
1705b261ecSmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
1805b261ecSmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1905b261ecSmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2005b261ecSmrg
2105b261ecSmrgExcept as contained in this notice, the name of The Open Group shall not be
2205b261ecSmrgused in advertising or otherwise to promote the sale, use or other dealings
2305b261ecSmrgin this Software without prior written authorization from The Open Group.
2405b261ecSmrg
2505b261ecSmrg
2605b261ecSmrgCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
2705b261ecSmrg
2805b261ecSmrg                        All Rights Reserved
2905b261ecSmrg
3005b261ecSmrgPermission to use, copy, modify, and distribute this software and its
3105b261ecSmrgdocumentation for any purpose and without fee is hereby granted,
3205b261ecSmrgprovided that the above copyright notice appear in all copies and that
3305b261ecSmrgboth that copyright notice and this permission notice appear in
3405b261ecSmrgsupporting documentation, and that the name of Digital not be
3505b261ecSmrgused in advertising or publicity pertaining to distribution of the
3605b261ecSmrgsoftware without specific, written prior permission.
3705b261ecSmrg
3805b261ecSmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
3905b261ecSmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
4005b261ecSmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
4105b261ecSmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
4205b261ecSmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
4305b261ecSmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
4405b261ecSmrgSOFTWARE.
4505b261ecSmrg
4605b261ecSmrg******************************************************************/
4705b261ecSmrg/* Author: Keith Packard and Bob Scheifler */
4805b261ecSmrg/* Warning: this code is toxic, do not dally very long here. */
4905b261ecSmrg
5005b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
5105b261ecSmrg#include <dix-config.h>
5205b261ecSmrg#endif
5305b261ecSmrg
5405b261ecSmrg#include <math.h>
5505b261ecSmrg#include <X11/X.h>
5605b261ecSmrg#include <X11/Xprotostr.h>
5705b261ecSmrg#include "misc.h"
5805b261ecSmrg#include "gcstruct.h"
5905b261ecSmrg#include "scrnintstr.h"
6005b261ecSmrg#include "pixmapstr.h"
6105b261ecSmrg#include "windowstr.h"
6205b261ecSmrg#include "mifpoly.h"
6305b261ecSmrg#include "mi.h"
6405b261ecSmrg#include "mifillarc.h"
6505b261ecSmrg#include <X11/Xfuncproto.h>
6605b261ecSmrg
6705b261ecSmrgstatic double miDsin(double a);
6805b261ecSmrgstatic double miDcos(double a);
6905b261ecSmrgstatic double miDasin(double v);
7005b261ecSmrgstatic double miDatan2(double dy, double dx);
7105b261ecSmrg
7205b261ecSmrg#ifndef HAVE_CBRT
7305b261ecSmrgstatic double
7405b261ecSmrgcbrt(double x)
7505b261ecSmrg{
7605b261ecSmrg    if (x > 0.0)
7705b261ecSmrg	return pow(x, 1.0/3.0);
7805b261ecSmrg    else
7905b261ecSmrg	return -pow(-x, 1.0/3.0);
8005b261ecSmrg}
8105b261ecSmrg#endif
8205b261ecSmrg
8305b261ecSmrg/*
8405b261ecSmrg * some interesting sematic interpretation of the protocol:
8505b261ecSmrg *
8605b261ecSmrg * Self intersecting arcs (i.e. those spanning 360 degrees)
8705b261ecSmrg *  never join with other arcs, and are drawn without caps
8805b261ecSmrg *  (unless on/off dashed, in which case each dash segment
8905b261ecSmrg *  is capped, except when the last segment meets the
9005b261ecSmrg *  first segment, when no caps are drawn)
9105b261ecSmrg *
9205b261ecSmrg * double dash arcs are drawn in two parts, first the
9305b261ecSmrg *  odd dashes (drawn in background) then the even dashes
9405b261ecSmrg *  (drawn in foreground).  This means that overlapping
9505b261ecSmrg *  sections of foreground/background are drawn twice,
9605b261ecSmrg *  first in background then in foreground.  The double-draw
9705b261ecSmrg *  occurs even when the function uses the destination values
9805b261ecSmrg *  (e.g. xor mode).  This is the same way the wide-line
9905b261ecSmrg *  code works and should be "fixed".
10005b261ecSmrg *
10105b261ecSmrg */
10205b261ecSmrg
10305b261ecSmrg#undef max
10405b261ecSmrg#undef min
10505b261ecSmrg
10605b261ecSmrg_X_INLINE static int max (const int x, const int y)
10705b261ecSmrg{
10805b261ecSmrg	return x>y? x:y;
10905b261ecSmrg}
11005b261ecSmrg
11105b261ecSmrg_X_INLINE static int min (const int x, const int y)
11205b261ecSmrg{
11305b261ecSmrg	return x<y? x:y;
11405b261ecSmrg}
11505b261ecSmrg
11605b261ecSmrgstruct bound {
11705b261ecSmrg	double	min, max;
11805b261ecSmrg};
11905b261ecSmrg
12005b261ecSmrgstruct ibound {
12105b261ecSmrg	int	min, max;
12205b261ecSmrg};
12305b261ecSmrg
12405b261ecSmrg#define boundedLe(value, bounds)\
12505b261ecSmrg	((bounds).min <= (value) && (value) <= (bounds).max)
12605b261ecSmrg
12705b261ecSmrgstruct line {
12805b261ecSmrg	double	m, b;
12905b261ecSmrg	int	valid;
13005b261ecSmrg};
13105b261ecSmrg
13205b261ecSmrg#define intersectLine(y,line) (line.m * (y) + line.b)
13305b261ecSmrg
13405b261ecSmrg/*
13505b261ecSmrg * these are all y value bounds
13605b261ecSmrg */
13705b261ecSmrg
13805b261ecSmrgstruct arc_bound {
13905b261ecSmrg	struct bound	ellipse;
14005b261ecSmrg	struct bound	inner;
14105b261ecSmrg	struct bound	outer;
14205b261ecSmrg	struct bound	right;
14305b261ecSmrg	struct bound	left;
14405b261ecSmrg	struct ibound	inneri;
14505b261ecSmrg	struct ibound	outeri;
14605b261ecSmrg};
14705b261ecSmrg
14805b261ecSmrgstruct accelerators {
14905b261ecSmrg	double		tail_y;
15005b261ecSmrg	double		h2;
15105b261ecSmrg	double		w2;
15205b261ecSmrg	double		h4;
15305b261ecSmrg	double		w4;
15405b261ecSmrg	double		h2mw2;
15505b261ecSmrg	double		h2l;
15605b261ecSmrg	double		w2l;
15705b261ecSmrg	double		fromIntX;
15805b261ecSmrg	double		fromIntY;
15905b261ecSmrg	struct line	left, right;
16005b261ecSmrg	int		yorgu;
16105b261ecSmrg	int		yorgl;
16205b261ecSmrg	int		xorg;
16305b261ecSmrg};
16405b261ecSmrg
16505b261ecSmrgstruct arc_def {
16605b261ecSmrg	double	w, h, l;
16705b261ecSmrg	double	a0, a1;
16805b261ecSmrg};
16905b261ecSmrg
17005b261ecSmrg# define todeg(xAngle)	(((double) (xAngle)) / 64.0)
17105b261ecSmrg
17205b261ecSmrg# define RIGHT_END	0
17305b261ecSmrg# define LEFT_END	1
17405b261ecSmrg
17505b261ecSmrgtypedef struct _miArcJoin {
17605b261ecSmrg	int	arcIndex0, arcIndex1;
17705b261ecSmrg	int	phase0, phase1;
17805b261ecSmrg	int	end0, end1;
17905b261ecSmrg} miArcJoinRec, *miArcJoinPtr;
18005b261ecSmrg
18105b261ecSmrgtypedef struct _miArcCap {
18205b261ecSmrg	int		arcIndex;
18305b261ecSmrg	int		end;
18405b261ecSmrg} miArcCapRec, *miArcCapPtr;
18505b261ecSmrg
18605b261ecSmrgtypedef struct _miArcFace {
18705b261ecSmrg	SppPointRec	clock;
18805b261ecSmrg	SppPointRec	center;
18905b261ecSmrg	SppPointRec	counterClock;
19005b261ecSmrg} miArcFaceRec, *miArcFacePtr;
19105b261ecSmrg
19205b261ecSmrgtypedef struct _miArcData {
19305b261ecSmrg	xArc		arc;
19405b261ecSmrg	int		render;		/* non-zero means render after drawing */
19505b261ecSmrg	int		join;		/* related join */
19605b261ecSmrg	int		cap;		/* related cap */
19705b261ecSmrg	int		selfJoin;	/* final dash meets first dash */
19805b261ecSmrg	miArcFaceRec	bounds[2];
19905b261ecSmrg	double		x0, y0, x1, y1;
20005b261ecSmrg} miArcDataRec, *miArcDataPtr;
20105b261ecSmrg
20205b261ecSmrg/*
20305b261ecSmrg * This is an entire sequence of arcs, computed and categorized according
20405b261ecSmrg * to operation.  miDashArcs generates either one or two of these.
20505b261ecSmrg */
20605b261ecSmrg
20705b261ecSmrgtypedef struct _miPolyArc {
20805b261ecSmrg	int		narcs;
20905b261ecSmrg	miArcDataPtr	arcs;
21005b261ecSmrg	int		ncaps;
21105b261ecSmrg	miArcCapPtr	caps;
21205b261ecSmrg	int		njoins;
21305b261ecSmrg	miArcJoinPtr	joins;
21405b261ecSmrg} miPolyArcRec, *miPolyArcPtr;
21505b261ecSmrg
21605b261ecSmrgstatic void fillSpans(DrawablePtr pDrawable, GCPtr pGC);
21705b261ecSmrgstatic void newFinalSpan(int y, int xmin, int xmax);
21805b261ecSmrgstatic void drawArc(xArc *tarc, int l, int a0, int a1, miArcFacePtr right,
21905b261ecSmrg		    miArcFacePtr left);
22005b261ecSmrgstatic void drawZeroArc(DrawablePtr pDraw, GCPtr pGC, xArc *tarc, int lw,
22105b261ecSmrg			miArcFacePtr left, miArcFacePtr right);
22205b261ecSmrgstatic void miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
22305b261ecSmrg		      miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
22405b261ecSmrg		      double xFtransLeft, double yFtransLeft,
22505b261ecSmrg		      int xOrgRight, int yOrgRight,
22605b261ecSmrg		      double xFtransRight, double yFtransRight);
22705b261ecSmrgstatic void miArcCap(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pFace,
22805b261ecSmrg		     int end, int xOrg, int yOrg, double xFtrans,
22905b261ecSmrg		     double yFtrans);
23005b261ecSmrgstatic void miRoundCap(DrawablePtr pDraw, GCPtr pGC, SppPointRec pCenter,
23105b261ecSmrg		       SppPointRec pEnd, SppPointRec pCorner,
23205b261ecSmrg		       SppPointRec pOtherCorner, int fLineEnd,
23305b261ecSmrg		       int xOrg, int yOrg, double xFtrans, double yFtrans);
23405b261ecSmrgstatic void miFreeArcs(miPolyArcPtr arcs, GCPtr pGC);
23505b261ecSmrgstatic miPolyArcPtr miComputeArcs(xArc *parcs, int narcs, GCPtr pGC);
23605b261ecSmrgstatic int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts);
23705b261ecSmrg
23805b261ecSmrg# define CUBED_ROOT_2	1.2599210498948732038115849718451499938964
23905b261ecSmrg# define CUBED_ROOT_4	1.5874010519681993173435330390930175781250
24005b261ecSmrg
24105b261ecSmrg/*
24205b261ecSmrg * draw one segment of the arc using the arc spans generation routines
24305b261ecSmrg */
24405b261ecSmrg
24505b261ecSmrgstatic void
24605b261ecSmrgmiArcSegment(
24705b261ecSmrg    DrawablePtr   pDraw,
24805b261ecSmrg    GCPtr         pGC,
24905b261ecSmrg    xArc          tarc,
25005b261ecSmrg    miArcFacePtr	right,
25105b261ecSmrg    miArcFacePtr	left)
25205b261ecSmrg{
25305b261ecSmrg    int l = pGC->lineWidth;
25405b261ecSmrg    int a0, a1, startAngle, endAngle;
25505b261ecSmrg    miArcFacePtr temp;
25605b261ecSmrg
25705b261ecSmrg    if (!l)
25805b261ecSmrg	l = 1;
25905b261ecSmrg
26005b261ecSmrg    if (tarc.width == 0 || tarc.height == 0) {
26105b261ecSmrg    	drawZeroArc (pDraw, pGC, &tarc, l, left, right);
26205b261ecSmrg	return;
26305b261ecSmrg    }
26405b261ecSmrg
26505b261ecSmrg    if (pGC->miTranslate) {
26605b261ecSmrg	tarc.x += pDraw->x;
26705b261ecSmrg	tarc.y += pDraw->y;
26805b261ecSmrg    }
26905b261ecSmrg
27005b261ecSmrg    a0 = tarc.angle1;
27105b261ecSmrg    a1 = tarc.angle2;
27205b261ecSmrg    if (a1 > FULLCIRCLE)
27305b261ecSmrg	a1 = FULLCIRCLE;
27405b261ecSmrg    else if (a1 < -FULLCIRCLE)
27505b261ecSmrg	a1 = -FULLCIRCLE;
27605b261ecSmrg    if (a1 < 0) {
27705b261ecSmrg    	startAngle = a0 + a1;
27805b261ecSmrg	endAngle = a0;
27905b261ecSmrg	temp = right;
28005b261ecSmrg	right = left;
28105b261ecSmrg	left = temp;
28205b261ecSmrg    } else {
28305b261ecSmrg	startAngle = a0;
28405b261ecSmrg	endAngle = a0 + a1;
28505b261ecSmrg    }
28605b261ecSmrg    /*
28705b261ecSmrg     * bounds check the two angles
28805b261ecSmrg     */
28905b261ecSmrg    if (startAngle < 0)
29005b261ecSmrg	startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
29105b261ecSmrg    if (startAngle >= FULLCIRCLE)
29205b261ecSmrg	startAngle = startAngle % FULLCIRCLE;
29305b261ecSmrg    if (endAngle < 0)
29405b261ecSmrg	endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
29505b261ecSmrg    if (endAngle > FULLCIRCLE)
29605b261ecSmrg	endAngle = (endAngle-1) % FULLCIRCLE + 1;
29705b261ecSmrg    if ((startAngle == endAngle) && a1) {
29805b261ecSmrg	startAngle = 0;
29905b261ecSmrg	endAngle = FULLCIRCLE;
30005b261ecSmrg    }
30105b261ecSmrg
30205b261ecSmrg    drawArc (&tarc, l, startAngle, endAngle, right, left);
30305b261ecSmrg}
30405b261ecSmrg
30505b261ecSmrg/*
30605b261ecSmrg
30705b261ecSmrgThree equations combine to describe the boundaries of the arc
30805b261ecSmrg
30905b261ecSmrgx^2/w^2 + y^2/h^2 = 1			ellipse itself
31005b261ecSmrg(X-x)^2 + (Y-y)^2 = r^2			circle at (x, y) on the ellipse
31105b261ecSmrg(Y-y) = (X-x)*w^2*y/(h^2*x)		normal at (x, y) on the ellipse
31205b261ecSmrg
31305b261ecSmrgThese lead to a quartic relating Y and y
31405b261ecSmrg
31505b261ecSmrgy^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
31605b261ecSmrg    - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
31705b261ecSmrg
31805b261ecSmrgThe reducible cubic obtained from this quartic is
31905b261ecSmrg
32005b261ecSmrgz^3 - (3N)z^2 - 2V = 0
32105b261ecSmrg
32205b261ecSmrgwhere
32305b261ecSmrg
32405b261ecSmrgN = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
32505b261ecSmrgV = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
32605b261ecSmrg
32705b261ecSmrgLet
32805b261ecSmrg
32905b261ecSmrgt = z - N
33005b261ecSmrgp = -N^2
33105b261ecSmrgq = -N^3 - V
33205b261ecSmrg
33305b261ecSmrgThen we get
33405b261ecSmrg
33505b261ecSmrgt^3 + 3pt + 2q = 0
33605b261ecSmrg
33705b261ecSmrgThe discriminant of this cubic is
33805b261ecSmrg
33905b261ecSmrgD = q^2 + p^3
34005b261ecSmrg
34105b261ecSmrgWhen D > 0, a real root is obtained as
34205b261ecSmrg
34305b261ecSmrgz = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
34405b261ecSmrg
34505b261ecSmrgWhen D < 0, a real root is obtained as
34605b261ecSmrg
34705b261ecSmrgz = N - 2m*cos(acos(-q/m^3)/3)
34805b261ecSmrg
34905b261ecSmrgwhere
35005b261ecSmrg
35105b261ecSmrgm = sqrt(|p|) * sign(q)
35205b261ecSmrg
35305b261ecSmrgGiven a real root Z of the cubic, the roots of the quartic are the roots
35405b261ecSmrgof the two quadratics
35505b261ecSmrg
35605b261ecSmrgy^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
35705b261ecSmrg
35805b261ecSmrgwhere
35905b261ecSmrg
36005b261ecSmrgA = +/- sqrt(8Z + b^2 - 4c)
36105b261ecSmrgb, c, d are the cubic, quadratic, and linear coefficients of the quartic
36205b261ecSmrg
36305b261ecSmrgSome experimentation is then required to determine which solutions
36405b261ecSmrgcorrespond to the inner and outer boundaries.
36505b261ecSmrg
36605b261ecSmrg*/
36705b261ecSmrg
36805b261ecSmrgtypedef struct {
36905b261ecSmrg    short lx, lw, rx, rw;
37005b261ecSmrg} miArcSpan;
37105b261ecSmrg
37205b261ecSmrgtypedef struct {
37305b261ecSmrg    miArcSpan *spans;
37405b261ecSmrg    int count1, count2, k;
37505b261ecSmrg    char top, bot, hole;
37605b261ecSmrg} miArcSpanData;
37705b261ecSmrg
37805b261ecSmrgstatic void drawQuadrant(struct arc_def *def, struct accelerators *acc,
37905b261ecSmrg			 int a0, int a1, int mask, miArcFacePtr right,
38005b261ecSmrg			 miArcFacePtr left, miArcSpanData *spdata);
38105b261ecSmrg
38205b261ecSmrgstatic void
38305b261ecSmrgmiComputeCircleSpans(
38405b261ecSmrg    int lw,
38505b261ecSmrg    xArc *parc,
38605b261ecSmrg    miArcSpanData *spdata)
38705b261ecSmrg{
38805b261ecSmrg    miArcSpan *span;
38905b261ecSmrg    int doinner;
39005b261ecSmrg    int x, y, e;
39105b261ecSmrg    int xk, yk, xm, ym, dx, dy;
39205b261ecSmrg    int slw, inslw;
39305b261ecSmrg    int inx = 0, iny, ine = 0;
39405b261ecSmrg    int inxk = 0, inyk = 0, inxm = 0, inym = 0;
39505b261ecSmrg
39605b261ecSmrg    doinner = -lw;
39705b261ecSmrg    slw = parc->width - doinner;
39805b261ecSmrg    y = parc->height >> 1;
39905b261ecSmrg    dy = parc->height & 1;
40005b261ecSmrg    dx = 1 - dy;
40105b261ecSmrg    MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
40205b261ecSmrg    inslw = parc->width + doinner;
40305b261ecSmrg    if (inslw > 0)
40405b261ecSmrg    {
40505b261ecSmrg	spdata->hole = spdata->top;
40605b261ecSmrg	MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
40705b261ecSmrg    }
40805b261ecSmrg    else
40905b261ecSmrg    {
41005b261ecSmrg	spdata->hole = FALSE;
41105b261ecSmrg	doinner = -y;
41205b261ecSmrg    }
41305b261ecSmrg    spdata->count1 = -doinner - spdata->top;
41405b261ecSmrg    spdata->count2 = y + doinner;
41505b261ecSmrg    span = spdata->spans;
41605b261ecSmrg    while (y)
41705b261ecSmrg    {
41805b261ecSmrg	MIFILLARCSTEP(slw);
41905b261ecSmrg	span->lx = dy - x;
42005b261ecSmrg	if (++doinner <= 0)
42105b261ecSmrg 	{
42205b261ecSmrg	    span->lw = slw;
42305b261ecSmrg	    span->rx = 0;
42405b261ecSmrg	    span->rw = span->lx + slw;
42505b261ecSmrg	}
42605b261ecSmrg	else
42705b261ecSmrg	{
42805b261ecSmrg	    MIFILLINARCSTEP(inslw);
42905b261ecSmrg	    span->lw = x - inx;
43005b261ecSmrg	    span->rx = dy - inx + inslw;
43105b261ecSmrg	    span->rw = inx - x + slw - inslw;
43205b261ecSmrg	}
43305b261ecSmrg	span++;
43405b261ecSmrg    }
43505b261ecSmrg    if (spdata->bot)
43605b261ecSmrg    {
43705b261ecSmrg	if (spdata->count2)
43805b261ecSmrg	    spdata->count2--;
43905b261ecSmrg	else
44005b261ecSmrg	{
44105b261ecSmrg	    if (lw > (int)parc->height)
44205b261ecSmrg		span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1);
44305b261ecSmrg	    else
44405b261ecSmrg		span[-1].rw = 0;
44505b261ecSmrg	    spdata->count1--;
44605b261ecSmrg	}
44705b261ecSmrg    }
44805b261ecSmrg}
44905b261ecSmrg
45005b261ecSmrgstatic void
45105b261ecSmrgmiComputeEllipseSpans(
45205b261ecSmrg    int lw,
45305b261ecSmrg    xArc *parc,
45405b261ecSmrg    miArcSpanData *spdata)
45505b261ecSmrg{
45605b261ecSmrg    miArcSpan *span;
45705b261ecSmrg    double w, h, r, xorg;
45805b261ecSmrg    double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
45905b261ecSmrg    double A, T, b, d, x, y, t, inx, outx = 0.0, hepp, hepm;
46005b261ecSmrg    int flip, solution;
46105b261ecSmrg
46205b261ecSmrg    w = (double)parc->width / 2.0;
46305b261ecSmrg    h = (double)parc->height / 2.0;
46405b261ecSmrg    r = lw / 2.0;
46505b261ecSmrg    rs = r * r;
46605b261ecSmrg    Hs = h * h;
46705b261ecSmrg    WH = w * w - Hs;
46805b261ecSmrg    Nk = w * r;
46905b261ecSmrg    Vk = (Nk * Hs) / (WH + WH);
47005b261ecSmrg    Hf = Hs * Hs;
47105b261ecSmrg    Nk = (Hf - Nk * Nk) / WH;
47205b261ecSmrg    Fk = Hf / WH;
47305b261ecSmrg    hepp = h + EPSILON;
47405b261ecSmrg    hepm = h - EPSILON;
47505b261ecSmrg    K = h + ((lw - 1) >> 1);
47605b261ecSmrg    span = spdata->spans;
47705b261ecSmrg    if (parc->width & 1)
47805b261ecSmrg	xorg = .5;
47905b261ecSmrg    else
48005b261ecSmrg	xorg = 0.0;
48105b261ecSmrg    if (spdata->top)
48205b261ecSmrg    {
48305b261ecSmrg	span->lx = 0;
48405b261ecSmrg	span->lw = 1;
48505b261ecSmrg	span++;
48605b261ecSmrg    }
48705b261ecSmrg    spdata->count1 = 0;
48805b261ecSmrg    spdata->count2 = 0;
48905b261ecSmrg    spdata->hole = (spdata->top &&
49005b261ecSmrg		 (int)parc->height * lw <= (int)(parc->width * parc->width) &&
49105b261ecSmrg		    lw < (int)parc->height);
49205b261ecSmrg    for (; K > 0.0; K -= 1.0)
49305b261ecSmrg    {
49405b261ecSmrg	N = (K * K + Nk) / 6.0;
49505b261ecSmrg	Nc = N * N * N;
49605b261ecSmrg	Vr = Vk * K;
49705b261ecSmrg	t = Nc + Vr * Vr;
49805b261ecSmrg	d = Nc + t;
49905b261ecSmrg	if (d < 0.0) {
50005b261ecSmrg	    d = Nc;
50105b261ecSmrg	    b = N;
50205b261ecSmrg	    if ( (b < 0.0) == (t < 0.0) )
50305b261ecSmrg	    {
50405b261ecSmrg		b = -b;
50505b261ecSmrg		d = -d;
50605b261ecSmrg	    }
50705b261ecSmrg	    Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
50805b261ecSmrg	    if ( (Z < 0.0) == (Vr < 0.0) )
50905b261ecSmrg		flip = 2;
51005b261ecSmrg	    else
51105b261ecSmrg		flip = 1;
51205b261ecSmrg	}
51305b261ecSmrg	else
51405b261ecSmrg	{
51505b261ecSmrg	    d = Vr * sqrt(d);
51605b261ecSmrg	    Z = N + cbrt(t + d) + cbrt(t - d);
51705b261ecSmrg	    flip = 0;
51805b261ecSmrg	}
51905b261ecSmrg	A = sqrt((Z + Z) - Nk);
52005b261ecSmrg	T = (Fk - Z) * K / A;
52105b261ecSmrg	inx = 0.0;
52205b261ecSmrg	solution = FALSE;
52305b261ecSmrg	b = -A + K;
52405b261ecSmrg	d = b * b - 4 * (Z + T);
52505b261ecSmrg	if (d >= 0)
52605b261ecSmrg	{
52705b261ecSmrg	    d = sqrt(d);
52805b261ecSmrg	    y = (b + d) / 2;
52905b261ecSmrg	    if ((y >= 0.0) && (y < hepp))
53005b261ecSmrg	    {
53105b261ecSmrg		solution = TRUE;
53205b261ecSmrg		if (y > hepm)
53305b261ecSmrg		    y = h;
53405b261ecSmrg		t = y / h;
53505b261ecSmrg		x = w * sqrt(1 - (t * t));
53605b261ecSmrg		t = K - y;
53705b261ecSmrg		if (rs - (t * t) >= 0)
53805b261ecSmrg		   t = sqrt(rs - (t * t));
53905b261ecSmrg		else
54005b261ecSmrg		   t = 0;
54105b261ecSmrg		if (flip == 2)
54205b261ecSmrg		    inx = x - t;
54305b261ecSmrg		else
54405b261ecSmrg		    outx = x + t;
54505b261ecSmrg	    }
54605b261ecSmrg	}
54705b261ecSmrg	b = A + K;
54805b261ecSmrg	d = b * b - 4 * (Z - T);
54905b261ecSmrg	/* Because of the large magnitudes involved, we lose enough precision
55005b261ecSmrg	 * that sometimes we end up with a negative value near the axis, when
55105b261ecSmrg	 * it should be positive.  This is a workaround.
55205b261ecSmrg	 */
55305b261ecSmrg	if (d < 0 && !solution)
55405b261ecSmrg	    d = 0.0;
55505b261ecSmrg	if (d >= 0) {
55605b261ecSmrg	    d = sqrt(d);
55705b261ecSmrg	    y = (b + d) / 2;
55805b261ecSmrg	    if (y < hepp)
55905b261ecSmrg	    {
56005b261ecSmrg		if (y > hepm)
56105b261ecSmrg		    y = h;
56205b261ecSmrg		t = y / h;
56305b261ecSmrg		x = w * sqrt(1 - (t * t));
56405b261ecSmrg		t = K - y;
56505b261ecSmrg		if (rs - (t * t) >= 0)
56605b261ecSmrg		   inx = x - sqrt(rs - (t * t));
56705b261ecSmrg		else
56805b261ecSmrg		   inx = x;
56905b261ecSmrg	    }
57005b261ecSmrg	    y = (b - d) / 2;
57105b261ecSmrg	    if (y >= 0.0)
57205b261ecSmrg	    {
57305b261ecSmrg		if (y > hepm)
57405b261ecSmrg		    y = h;
57505b261ecSmrg		t = y / h;
57605b261ecSmrg		x = w * sqrt(1 - (t * t));
57705b261ecSmrg		t = K - y;
57805b261ecSmrg		if (rs - (t * t) >= 0)
57905b261ecSmrg		   t = sqrt(rs - (t * t));
58005b261ecSmrg		else
58105b261ecSmrg		   t = 0;
58205b261ecSmrg		if (flip == 1)
58305b261ecSmrg		    inx = x - t;
58405b261ecSmrg		else
58505b261ecSmrg		    outx = x + t;
58605b261ecSmrg	    }
58705b261ecSmrg	}
58805b261ecSmrg	span->lx = ICEIL(xorg - outx);
58905b261ecSmrg	if (inx <= 0.0)
59005b261ecSmrg	{
59105b261ecSmrg	    spdata->count1++;
59205b261ecSmrg	    span->lw = ICEIL(xorg + outx) - span->lx;
59305b261ecSmrg	    span->rx = ICEIL(xorg + inx);
59405b261ecSmrg	    span->rw = -ICEIL(xorg - inx);
59505b261ecSmrg	}
59605b261ecSmrg	else
59705b261ecSmrg	{
59805b261ecSmrg	    spdata->count2++;
59905b261ecSmrg	    span->lw = ICEIL(xorg - inx) - span->lx;
60005b261ecSmrg	    span->rx = ICEIL(xorg + inx);
60105b261ecSmrg	    span->rw = ICEIL(xorg + outx) - span->rx;
60205b261ecSmrg	}
60305b261ecSmrg	span++;
60405b261ecSmrg    }
60505b261ecSmrg    if (spdata->bot)
60605b261ecSmrg    {
60705b261ecSmrg	outx = w + r;
60805b261ecSmrg	if (r >= h && r <= w)
60905b261ecSmrg	    inx = 0.0;
61005b261ecSmrg	else if (Nk < 0.0 && -Nk < Hs)
61105b261ecSmrg	{
61205b261ecSmrg	    inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
61305b261ecSmrg	    if (inx > w - r)
61405b261ecSmrg		inx = w - r;
61505b261ecSmrg	}
61605b261ecSmrg	else
61705b261ecSmrg	    inx = w - r;
61805b261ecSmrg	span->lx = ICEIL(xorg - outx);
61905b261ecSmrg	if (inx <= 0.0)
62005b261ecSmrg	{
62105b261ecSmrg	    span->lw = ICEIL(xorg + outx) - span->lx;
62205b261ecSmrg	    span->rx = ICEIL(xorg + inx);
62305b261ecSmrg	    span->rw = -ICEIL(xorg - inx);
62405b261ecSmrg	}
62505b261ecSmrg	else
62605b261ecSmrg	{
62705b261ecSmrg	    span->lw = ICEIL(xorg - inx) - span->lx;
62805b261ecSmrg	    span->rx = ICEIL(xorg + inx);
62905b261ecSmrg	    span->rw = ICEIL(xorg + outx) - span->rx;
63005b261ecSmrg	}
63105b261ecSmrg    }
63205b261ecSmrg    if (spdata->hole)
63305b261ecSmrg    {
63405b261ecSmrg	span = &spdata->spans[spdata->count1];
63505b261ecSmrg	span->lw = -span->lx;
63605b261ecSmrg	span->rx = 1;
63705b261ecSmrg	span->rw = span->lw;
63805b261ecSmrg	spdata->count1--;
63905b261ecSmrg	spdata->count2++;
64005b261ecSmrg    }
64105b261ecSmrg}
64205b261ecSmrg
64305b261ecSmrgstatic double
64405b261ecSmrgtailX(
64505b261ecSmrg    double K,
64605b261ecSmrg    struct arc_def *def,
64705b261ecSmrg    struct arc_bound *bounds,
64805b261ecSmrg    struct accelerators *acc)
64905b261ecSmrg{
65005b261ecSmrg    double w, h, r;
65105b261ecSmrg    double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
65205b261ecSmrg    double A, T, b, d, x, y, t, hepp, hepm;
65305b261ecSmrg    int flip, solution;
65405b261ecSmrg    double xs[2];
65505b261ecSmrg    double *xp;
65605b261ecSmrg
65705b261ecSmrg    w = def->w;
65805b261ecSmrg    h = def->h;
65905b261ecSmrg    r = def->l;
66005b261ecSmrg    rs = r * r;
66105b261ecSmrg    Hs = acc->h2;
66205b261ecSmrg    WH = -acc->h2mw2;
66305b261ecSmrg    Nk = def->w * r;
66405b261ecSmrg    Vk = (Nk * Hs) / (WH + WH);
66505b261ecSmrg    Hf = acc->h4;
66605b261ecSmrg    Nk = (Hf - Nk * Nk) / WH;
66705b261ecSmrg    if (K == 0.0) {
66805b261ecSmrg	if (Nk < 0.0 && -Nk < Hs) {
66905b261ecSmrg	    xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
67005b261ecSmrg	    xs[1] = w - r;
67105b261ecSmrg	    if (acc->left.valid && boundedLe(K, bounds->left) &&
67205b261ecSmrg		!boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
67305b261ecSmrg		return xs[1];
67405b261ecSmrg	    if (acc->right.valid && boundedLe(K, bounds->right) &&
67505b261ecSmrg		!boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
67605b261ecSmrg		return xs[1];
67705b261ecSmrg	    return xs[0];
67805b261ecSmrg	}
67905b261ecSmrg	return w - r;
68005b261ecSmrg    }
68105b261ecSmrg    Fk = Hf / WH;
68205b261ecSmrg    hepp = h + EPSILON;
68305b261ecSmrg    hepm = h - EPSILON;
68405b261ecSmrg    N = (K * K + Nk) / 6.0;
68505b261ecSmrg    Nc = N * N * N;
68605b261ecSmrg    Vr = Vk * K;
68705b261ecSmrg    xp = xs;
68805b261ecSmrg    xs[0] = 0.0;
68905b261ecSmrg    t = Nc + Vr * Vr;
69005b261ecSmrg    d = Nc + t;
69105b261ecSmrg    if (d < 0.0) {
69205b261ecSmrg	d = Nc;
69305b261ecSmrg	b = N;
69405b261ecSmrg	if ( (b < 0.0) == (t < 0.0) )
69505b261ecSmrg	{
69605b261ecSmrg	    b = -b;
69705b261ecSmrg	    d = -d;
69805b261ecSmrg	}
69905b261ecSmrg	Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
70005b261ecSmrg	if ( (Z < 0.0) == (Vr < 0.0) )
70105b261ecSmrg	    flip = 2;
70205b261ecSmrg	else
70305b261ecSmrg	    flip = 1;
70405b261ecSmrg    }
70505b261ecSmrg    else
70605b261ecSmrg    {
70705b261ecSmrg	d = Vr * sqrt(d);
70805b261ecSmrg	Z = N + cbrt(t + d) + cbrt(t - d);
70905b261ecSmrg	flip = 0;
71005b261ecSmrg    }
71105b261ecSmrg    A = sqrt((Z + Z) - Nk);
71205b261ecSmrg    T = (Fk - Z) * K / A;
71305b261ecSmrg    solution = FALSE;
71405b261ecSmrg    b = -A + K;
71505b261ecSmrg    d = b * b - 4 * (Z + T);
71605b261ecSmrg    if (d >= 0 && flip == 2)
71705b261ecSmrg    {
71805b261ecSmrg	d = sqrt(d);
71905b261ecSmrg	y = (b + d) / 2;
72005b261ecSmrg	if ((y >= 0.0) && (y < hepp))
72105b261ecSmrg	{
72205b261ecSmrg	    solution = TRUE;
72305b261ecSmrg	    if (y > hepm)
72405b261ecSmrg		y = h;
72505b261ecSmrg	    t = y / h;
72605b261ecSmrg	    x = w * sqrt(1 - (t * t));
72705b261ecSmrg	    t = K - y;
72805b261ecSmrg	    if (rs - (t * t) >= 0)
72905b261ecSmrg	       t = sqrt(rs - (t * t));
73005b261ecSmrg	    else
73105b261ecSmrg	       t = 0;
73205b261ecSmrg	    *xp++ = x - t;
73305b261ecSmrg	}
73405b261ecSmrg    }
73505b261ecSmrg    b = A + K;
73605b261ecSmrg    d = b * b - 4 * (Z - T);
73705b261ecSmrg    /* Because of the large magnitudes involved, we lose enough precision
73805b261ecSmrg     * that sometimes we end up with a negative value near the axis, when
73905b261ecSmrg     * it should be positive.  This is a workaround.
74005b261ecSmrg     */
74105b261ecSmrg    if (d < 0 && !solution)
74205b261ecSmrg	d = 0.0;
74305b261ecSmrg    if (d >= 0) {
74405b261ecSmrg	d = sqrt(d);
74505b261ecSmrg	y = (b + d) / 2;
74605b261ecSmrg	if (y < hepp)
74705b261ecSmrg	{
74805b261ecSmrg	    if (y > hepm)
74905b261ecSmrg		y = h;
75005b261ecSmrg	    t = y / h;
75105b261ecSmrg	    x = w * sqrt(1 - (t * t));
75205b261ecSmrg	    t = K - y;
75305b261ecSmrg	    if (rs - (t * t) >= 0)
75405b261ecSmrg	       *xp++ = x - sqrt(rs - (t * t));
75505b261ecSmrg	    else
75605b261ecSmrg	       *xp++ = x;
75705b261ecSmrg	}
75805b261ecSmrg	y = (b - d) / 2;
75905b261ecSmrg	if (y >= 0.0 && flip == 1)
76005b261ecSmrg	{
76105b261ecSmrg	    if (y > hepm)
76205b261ecSmrg		y = h;
76305b261ecSmrg	    t = y / h;
76405b261ecSmrg	    x = w * sqrt(1 - (t * t));
76505b261ecSmrg	    t = K - y;
76605b261ecSmrg	    if (rs - (t * t) >= 0)
76705b261ecSmrg	       t = sqrt(rs - (t * t));
76805b261ecSmrg	    else
76905b261ecSmrg	       t = 0;
77005b261ecSmrg	    *xp++ = x - t;
77105b261ecSmrg	}
77205b261ecSmrg    }
77305b261ecSmrg    if (xp > &xs[1]) {
77405b261ecSmrg	if (acc->left.valid && boundedLe(K, bounds->left) &&
77505b261ecSmrg	    !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
77605b261ecSmrg	    return xs[1];
77705b261ecSmrg	if (acc->right.valid && boundedLe(K, bounds->right) &&
77805b261ecSmrg	    !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
77905b261ecSmrg	    return xs[1];
78005b261ecSmrg    }
78105b261ecSmrg    return xs[0];
78205b261ecSmrg}
78305b261ecSmrg
78405b261ecSmrgstatic miArcSpanData *
7856747b715SmrgmiComputeWideEllipse(int lw, xArc *parc)
78605b261ecSmrg{
7876747b715Smrg    miArcSpanData *spdata = NULL;
78805b261ecSmrg    int k;
78905b261ecSmrg
79005b261ecSmrg    if (!lw)
79105b261ecSmrg	lw = 1;
79205b261ecSmrg    k = (parc->height >> 1) + ((lw - 1) >> 1);
7936747b715Smrg    spdata = malloc(sizeof(miArcSpanData) + sizeof(miArcSpan) * (k + 2));
7946747b715Smrg    if (!spdata)
7956747b715Smrg	return NULL;
7966747b715Smrg    spdata->spans = (miArcSpan *)(spdata + 1);
7976747b715Smrg    spdata->k = k;
79805b261ecSmrg    spdata->top = !(lw & 1) && !(parc->width & 1);
79905b261ecSmrg    spdata->bot = !(parc->height & 1);
80005b261ecSmrg    if (parc->width == parc->height)
80105b261ecSmrg	miComputeCircleSpans(lw, parc, spdata);
80205b261ecSmrg    else
80305b261ecSmrg	miComputeEllipseSpans(lw, parc, spdata);
80405b261ecSmrg    return spdata;
80505b261ecSmrg}
80605b261ecSmrg
80705b261ecSmrgstatic void
80805b261ecSmrgmiFillWideEllipse(
80905b261ecSmrg    DrawablePtr	pDraw,
81005b261ecSmrg    GCPtr	pGC,
81105b261ecSmrg    xArc	*parc)
81205b261ecSmrg{
81305b261ecSmrg    DDXPointPtr points;
81405b261ecSmrg    DDXPointPtr pts;
81505b261ecSmrg    int *widths;
81605b261ecSmrg    int *wids;
81705b261ecSmrg    miArcSpanData *spdata;
81805b261ecSmrg    miArcSpan *span;
81905b261ecSmrg    int xorg, yorgu, yorgl;
82005b261ecSmrg    int n;
82105b261ecSmrg
82205b261ecSmrg    yorgu = parc->height + pGC->lineWidth;
82305b261ecSmrg    n = (sizeof(int) * 2) * yorgu;
8246747b715Smrg    widths = malloc(n + (sizeof(DDXPointRec) * 2) * yorgu);
82505b261ecSmrg    if (!widths)
82605b261ecSmrg	return;
82705b261ecSmrg    points = (DDXPointPtr)((char *)widths + n);
8286747b715Smrg    spdata = miComputeWideEllipse((int)pGC->lineWidth, parc);
82905b261ecSmrg    if (!spdata)
83005b261ecSmrg    {
8316747b715Smrg	free(widths);
83205b261ecSmrg	return;
83305b261ecSmrg    }
83405b261ecSmrg    pts = points;
83505b261ecSmrg    wids = widths;
83605b261ecSmrg    span = spdata->spans;
83705b261ecSmrg    xorg = parc->x + (parc->width >> 1);
83805b261ecSmrg    yorgu = parc->y + (parc->height >> 1);
83905b261ecSmrg    yorgl = yorgu + (parc->height & 1);
84005b261ecSmrg    if (pGC->miTranslate)
84105b261ecSmrg    {
84205b261ecSmrg	xorg += pDraw->x;
84305b261ecSmrg	yorgu += pDraw->y;
84405b261ecSmrg	yorgl += pDraw->y;
84505b261ecSmrg    }
84605b261ecSmrg    yorgu -= spdata->k;
84705b261ecSmrg    yorgl += spdata->k;
84805b261ecSmrg    if (spdata->top)
84905b261ecSmrg    {
85005b261ecSmrg	pts->x = xorg;
85105b261ecSmrg	pts->y = yorgu - 1;
85205b261ecSmrg	pts++;
85305b261ecSmrg	*wids++ = 1;
85405b261ecSmrg	span++;
85505b261ecSmrg    }
85605b261ecSmrg    for (n = spdata->count1; --n >= 0; )
85705b261ecSmrg    {
85805b261ecSmrg	pts[0].x = xorg + span->lx;
85905b261ecSmrg	pts[0].y = yorgu;
86005b261ecSmrg	wids[0] = span->lw;
86105b261ecSmrg	pts[1].x = pts[0].x;
86205b261ecSmrg	pts[1].y = yorgl;
86305b261ecSmrg	wids[1] = wids[0];
86405b261ecSmrg	yorgu++;
86505b261ecSmrg	yorgl--;
86605b261ecSmrg	pts += 2;
86705b261ecSmrg	wids += 2;
86805b261ecSmrg	span++;
86905b261ecSmrg    }
87005b261ecSmrg    if (spdata->hole)
87105b261ecSmrg    {
87205b261ecSmrg	pts[0].x = xorg;
87305b261ecSmrg	pts[0].y = yorgl;
87405b261ecSmrg	wids[0] = 1;
87505b261ecSmrg	pts++;
87605b261ecSmrg	wids++;
87705b261ecSmrg    }
87805b261ecSmrg    for (n = spdata->count2; --n >= 0; )
87905b261ecSmrg    {
88005b261ecSmrg	pts[0].x = xorg + span->lx;
88105b261ecSmrg	pts[0].y = yorgu;
88205b261ecSmrg	wids[0] = span->lw;
88305b261ecSmrg	pts[1].x = xorg + span->rx;
88405b261ecSmrg	pts[1].y = pts[0].y;
88505b261ecSmrg	wids[1] = span->rw;
88605b261ecSmrg	pts[2].x = pts[0].x;
88705b261ecSmrg	pts[2].y = yorgl;
88805b261ecSmrg	wids[2] = wids[0];
88905b261ecSmrg	pts[3].x = pts[1].x;
89005b261ecSmrg	pts[3].y = pts[2].y;
89105b261ecSmrg	wids[3] = wids[1];
89205b261ecSmrg	yorgu++;
89305b261ecSmrg	yorgl--;
89405b261ecSmrg	pts += 4;
89505b261ecSmrg	wids += 4;
89605b261ecSmrg	span++;
89705b261ecSmrg    }
89805b261ecSmrg    if (spdata->bot)
89905b261ecSmrg    {
90005b261ecSmrg	if (span->rw <= 0)
90105b261ecSmrg	{
90205b261ecSmrg	    pts[0].x = xorg + span->lx;
90305b261ecSmrg	    pts[0].y = yorgu;
90405b261ecSmrg	    wids[0] = span->lw;
90505b261ecSmrg	    pts++;
90605b261ecSmrg	    wids++;
90705b261ecSmrg	}
90805b261ecSmrg	else
90905b261ecSmrg	{
91005b261ecSmrg	    pts[0].x = xorg + span->lx;
91105b261ecSmrg	    pts[0].y = yorgu;
91205b261ecSmrg	    wids[0] = span->lw;
91305b261ecSmrg	    pts[1].x = xorg + span->rx;
91405b261ecSmrg	    pts[1].y = pts[0].y;
91505b261ecSmrg	    wids[1] = span->rw;
91605b261ecSmrg	    pts += 2;
91705b261ecSmrg	    wids += 2;
91805b261ecSmrg	}
91905b261ecSmrg    }
9206747b715Smrg    free(spdata);
92105b261ecSmrg    (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE);
92205b261ecSmrg
9236747b715Smrg    free(widths);
92405b261ecSmrg}
92505b261ecSmrg
92605b261ecSmrg/*
92705b261ecSmrg * miPolyArc strategy:
92805b261ecSmrg *
92905b261ecSmrg * If arc is zero width and solid, we don't have to worry about the rasterop
93005b261ecSmrg * or join styles.  For wide solid circles, we use a fast integer algorithm.
93105b261ecSmrg * For wide solid ellipses, we use special case floating point code.
93205b261ecSmrg * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
93305b261ecSmrg * draw using pGCTo and pDrawTo.  If the raster-op was "tricky," that is,
93405b261ecSmrg * if it involves the destination, then we use PushPixels to move the bits
93505b261ecSmrg * from the scratch drawable to pDraw. (See the wide line code for a
93605b261ecSmrg * fuller explanation of this.)
93705b261ecSmrg */
93805b261ecSmrg
9396747b715Smrgvoid
9404642e01fSmrgmiPolyArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc *parcs)
94105b261ecSmrg{
94205b261ecSmrg    int		i;
94305b261ecSmrg    xArc	*parc;
94405b261ecSmrg    int		xMin, xMax, yMin, yMax;
94505b261ecSmrg    int		pixmapWidth = 0, pixmapHeight = 0;
94605b261ecSmrg    int		xOrg = 0, yOrg = 0;
94705b261ecSmrg    int		width;
94805b261ecSmrg    Bool	fTricky;
94905b261ecSmrg    DrawablePtr	pDrawTo;
95005b261ecSmrg    CARD32	fg, bg;
95105b261ecSmrg    GCPtr	pGCTo;
95205b261ecSmrg    miPolyArcPtr polyArcs;
95305b261ecSmrg    int		cap[2], join[2];
95405b261ecSmrg    int		iphase;
95505b261ecSmrg    int		halfWidth;
95605b261ecSmrg
95705b261ecSmrg    width = pGC->lineWidth;
95805b261ecSmrg    if(width == 0 && pGC->lineStyle == LineSolid)
95905b261ecSmrg    {
96005b261ecSmrg	for(i = narcs, parc = parcs; --i >= 0; parc++)
96105b261ecSmrg	    miArcSegment( pDraw, pGC, *parc,
96205b261ecSmrg 	    (miArcFacePtr) 0, (miArcFacePtr) 0 );
96305b261ecSmrg	fillSpans (pDraw, pGC);
96405b261ecSmrg    }
96505b261ecSmrg    else
96605b261ecSmrg    {
96705b261ecSmrg	if ((pGC->lineStyle == LineSolid) && narcs)
96805b261ecSmrg	{
96905b261ecSmrg	    while (parcs->width && parcs->height &&
97005b261ecSmrg		   (parcs->angle2 >= FULLCIRCLE ||
97105b261ecSmrg		    parcs->angle2 <= -FULLCIRCLE))
97205b261ecSmrg	    {
97305b261ecSmrg		miFillWideEllipse(pDraw, pGC, parcs);
97405b261ecSmrg		if (!--narcs)
97505b261ecSmrg		    return;
97605b261ecSmrg		parcs++;
97705b261ecSmrg	    }
97805b261ecSmrg	}
97905b261ecSmrg
98005b261ecSmrg	/* Set up pDrawTo and pGCTo based on the rasterop */
98105b261ecSmrg	switch(pGC->alu)
98205b261ecSmrg	{
98305b261ecSmrg	  case GXclear:		/* 0 */
98405b261ecSmrg	  case GXcopy:		/* src */
98505b261ecSmrg	  case GXcopyInverted:	/* NOT src */
98605b261ecSmrg	  case GXset:		/* 1 */
98705b261ecSmrg	    fTricky = FALSE;
98805b261ecSmrg	    pDrawTo = pDraw;
98905b261ecSmrg	    pGCTo = pGC;
99005b261ecSmrg	    break;
99105b261ecSmrg	  default:
99205b261ecSmrg	    fTricky = TRUE;
99305b261ecSmrg
99405b261ecSmrg	    /* find bounding box around arcs */
99505b261ecSmrg	    xMin = yMin = MAXSHORT;
99605b261ecSmrg	    xMax = yMax = MINSHORT;
99705b261ecSmrg
99805b261ecSmrg	    for(i = narcs, parc = parcs; --i >= 0; parc++)
99905b261ecSmrg	    {
100005b261ecSmrg		xMin = min (xMin, parc->x);
100105b261ecSmrg		yMin = min (yMin, parc->y);
100205b261ecSmrg		xMax = max (xMax, (parc->x + (int) parc->width));
100305b261ecSmrg		yMax = max (yMax, (parc->y + (int) parc->height));
100405b261ecSmrg	    }
100505b261ecSmrg
100605b261ecSmrg	    /* expand box to deal with line widths */
100705b261ecSmrg	    halfWidth = (width + 1)/2;
100805b261ecSmrg	    xMin -= halfWidth;
100905b261ecSmrg	    yMin -= halfWidth;
101005b261ecSmrg	    xMax += halfWidth;
101105b261ecSmrg	    yMax += halfWidth;
101205b261ecSmrg
101305b261ecSmrg	    /* compute pixmap size; limit it to size of drawable */
101405b261ecSmrg	    xOrg = max(xMin, 0);
101505b261ecSmrg	    yOrg = max(yMin, 0);
101605b261ecSmrg	    pixmapWidth = min(xMax, pDraw->width) - xOrg;
101705b261ecSmrg	    pixmapHeight = min(yMax, pDraw->height) - yOrg;
101805b261ecSmrg
101905b261ecSmrg	    /* if nothing left, return */
102005b261ecSmrg	    if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return;
102105b261ecSmrg
102205b261ecSmrg	    for(i = narcs, parc = parcs; --i >= 0; parc++)
102305b261ecSmrg	    {
102405b261ecSmrg		parc->x -= xOrg;
102505b261ecSmrg		parc->y -= yOrg;
102605b261ecSmrg	    }
102705b261ecSmrg	    if (pGC->miTranslate)
102805b261ecSmrg	    {
102905b261ecSmrg		xOrg += pDraw->x;
103005b261ecSmrg		yOrg += pDraw->y;
103105b261ecSmrg	    }
103205b261ecSmrg
103305b261ecSmrg	    /* set up scratch GC */
103405b261ecSmrg
103505b261ecSmrg	    pGCTo = GetScratchGC(1, pDraw->pScreen);
103605b261ecSmrg	    if (!pGCTo)
103705b261ecSmrg		return;
10386747b715Smrg	    {
10396747b715Smrg		ChangeGCVal gcvals[6];
10406747b715Smrg		gcvals[0].val = GXcopy;
10416747b715Smrg		gcvals[1].val = 1;
10426747b715Smrg		gcvals[2].val = 0;
10436747b715Smrg		gcvals[3].val = pGC->lineWidth;
10446747b715Smrg		gcvals[4].val = pGC->capStyle;
10456747b715Smrg		gcvals[5].val = pGC->joinStyle;
10466747b715Smrg		ChangeGC(NullClient, pGCTo, GCFunction |
10476747b715Smrg			GCForeground | GCBackground | GCLineWidth |
10486747b715Smrg			GCCapStyle | GCJoinStyle, gcvals);
10496747b715Smrg	    }
105005b261ecSmrg
105105b261ecSmrg	    /* allocate a 1 bit deep pixmap of the appropriate size, and
105205b261ecSmrg	     * validate it */
105305b261ecSmrg	    pDrawTo = (DrawablePtr)(*pDraw->pScreen->CreatePixmap)
10544642e01fSmrg				(pDraw->pScreen, pixmapWidth, pixmapHeight, 1,
10554642e01fSmrg				 CREATE_PIXMAP_USAGE_SCRATCH);
105605b261ecSmrg	    if (!pDrawTo)
105705b261ecSmrg	    {
105805b261ecSmrg		FreeScratchGC(pGCTo);
105905b261ecSmrg		return;
106005b261ecSmrg	    }
106105b261ecSmrg	    ValidateGC(pDrawTo, pGCTo);
106205b261ecSmrg	    miClearDrawable(pDrawTo, pGCTo);
106305b261ecSmrg	}
106405b261ecSmrg
106505b261ecSmrg	fg = pGC->fgPixel;
106605b261ecSmrg	bg = pGC->bgPixel;
106705b261ecSmrg	if ((pGC->fillStyle == FillTiled) ||
106805b261ecSmrg	    (pGC->fillStyle == FillOpaqueStippled))
106905b261ecSmrg	    bg = fg; /* the protocol sez these don't cause color changes */
107005b261ecSmrg
107105b261ecSmrg	polyArcs = miComputeArcs (parcs, narcs, pGC);
107205b261ecSmrg
107305b261ecSmrg	if (!polyArcs)
107405b261ecSmrg	{
107505b261ecSmrg	    if (fTricky) {
107605b261ecSmrg		(*pDraw->pScreen->DestroyPixmap) ((PixmapPtr)pDrawTo);
107705b261ecSmrg		FreeScratchGC (pGCTo);
107805b261ecSmrg	    }
107905b261ecSmrg	    return;
108005b261ecSmrg	}
108105b261ecSmrg
108205b261ecSmrg	cap[0] = cap[1] = 0;
108305b261ecSmrg	join[0] = join[1] = 0;
108405b261ecSmrg	for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
108505b261ecSmrg 	     iphase >= 0;
108605b261ecSmrg	     iphase--)
108705b261ecSmrg	{
10886747b715Smrg	    ChangeGCVal gcval;
108905b261ecSmrg	    if (iphase == 1) {
10906747b715Smrg		gcval.val = bg;
10916747b715Smrg		ChangeGC (NullClient, pGC, GCForeground, &gcval);
109205b261ecSmrg		ValidateGC (pDraw, pGC);
109305b261ecSmrg	    } else if (pGC->lineStyle == LineDoubleDash) {
10946747b715Smrg		gcval.val = fg;
10956747b715Smrg		ChangeGC (NullClient, pGC, GCForeground, &gcval);
109605b261ecSmrg		ValidateGC (pDraw, pGC);
109705b261ecSmrg	    }
109805b261ecSmrg	    for (i = 0; i < polyArcs[iphase].narcs; i++) {
109905b261ecSmrg		miArcDataPtr	arcData;
110005b261ecSmrg
110105b261ecSmrg		arcData = &polyArcs[iphase].arcs[i];
110205b261ecSmrg		miArcSegment(pDrawTo, pGCTo, arcData->arc,
110305b261ecSmrg			     &arcData->bounds[RIGHT_END],
110405b261ecSmrg			     &arcData->bounds[LEFT_END]);
110505b261ecSmrg		if (polyArcs[iphase].arcs[i].render) {
110605b261ecSmrg		    fillSpans (pDrawTo, pGCTo);
110705b261ecSmrg		    /*
110805b261ecSmrg		     * don't cap self-joining arcs
110905b261ecSmrg		     */
111005b261ecSmrg		    if (polyArcs[iphase].arcs[i].selfJoin &&
111105b261ecSmrg		        cap[iphase] < polyArcs[iphase].arcs[i].cap)
111205b261ecSmrg		    	cap[iphase]++;
111305b261ecSmrg		    while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
111405b261ecSmrg			int	arcIndex, end;
111505b261ecSmrg			miArcDataPtr	arcData0;
111605b261ecSmrg
111705b261ecSmrg			arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
111805b261ecSmrg			end = polyArcs[iphase].caps[cap[iphase]].end;
111905b261ecSmrg			arcData0 = &polyArcs[iphase].arcs[arcIndex];
112005b261ecSmrg			miArcCap (pDrawTo, pGCTo,
112105b261ecSmrg 				  &arcData0->bounds[end], end,
112205b261ecSmrg				  arcData0->arc.x, arcData0->arc.y,
112305b261ecSmrg				  (double) arcData0->arc.width / 2.0,
112405b261ecSmrg 				  (double) arcData0->arc.height / 2.0);
112505b261ecSmrg			++cap[iphase];
112605b261ecSmrg		    }
112705b261ecSmrg		    while (join[iphase] < polyArcs[iphase].arcs[i].join) {
112805b261ecSmrg			int	arcIndex0, arcIndex1, end0, end1;
112905b261ecSmrg			int	phase0, phase1;
113005b261ecSmrg			miArcDataPtr	arcData0, arcData1;
113105b261ecSmrg			miArcJoinPtr	joinp;
113205b261ecSmrg
113305b261ecSmrg			joinp = &polyArcs[iphase].joins[join[iphase]];
113405b261ecSmrg			arcIndex0 = joinp->arcIndex0;
113505b261ecSmrg			end0 = joinp->end0;
113605b261ecSmrg			arcIndex1 = joinp->arcIndex1;
113705b261ecSmrg			end1 = joinp->end1;
113805b261ecSmrg			phase0 = joinp->phase0;
113905b261ecSmrg			phase1 = joinp->phase1;
114005b261ecSmrg			arcData0 = &polyArcs[phase0].arcs[arcIndex0];
114105b261ecSmrg			arcData1 = &polyArcs[phase1].arcs[arcIndex1];
114205b261ecSmrg			miArcJoin (pDrawTo, pGCTo,
114305b261ecSmrg				  &arcData0->bounds[end0],
114405b261ecSmrg 				  &arcData1->bounds[end1],
114505b261ecSmrg				  arcData0->arc.x, arcData0->arc.y,
114605b261ecSmrg				  (double) arcData0->arc.width / 2.0,
114705b261ecSmrg 				  (double) arcData0->arc.height / 2.0,
114805b261ecSmrg				  arcData1->arc.x, arcData1->arc.y,
114905b261ecSmrg				  (double) arcData1->arc.width / 2.0,
115005b261ecSmrg 				  (double) arcData1->arc.height / 2.0);
115105b261ecSmrg			++join[iphase];
115205b261ecSmrg		    }
115305b261ecSmrg		    if (fTricky) {
115405b261ecSmrg			if (pGC->serialNumber != pDraw->serialNumber)
115505b261ecSmrg			    ValidateGC (pDraw, pGC);
115605b261ecSmrg		    	(*pGC->ops->PushPixels) (pGC, (PixmapPtr)pDrawTo,
115705b261ecSmrg				 pDraw, pixmapWidth, pixmapHeight, xOrg, yOrg);
115805b261ecSmrg			miClearDrawable ((DrawablePtr) pDrawTo, pGCTo);
115905b261ecSmrg		    }
116005b261ecSmrg		}
116105b261ecSmrg	    }
116205b261ecSmrg	}
116305b261ecSmrg	miFreeArcs(polyArcs, pGC);
116405b261ecSmrg
116505b261ecSmrg	if(fTricky)
116605b261ecSmrg	{
116705b261ecSmrg	    (*pGCTo->pScreen->DestroyPixmap)((PixmapPtr)pDrawTo);
116805b261ecSmrg	    FreeScratchGC(pGCTo);
116905b261ecSmrg	}
117005b261ecSmrg    }
117105b261ecSmrg}
117205b261ecSmrg
117305b261ecSmrgstatic double
117405b261ecSmrgangleBetween (SppPointRec center, SppPointRec point1, SppPointRec point2)
117505b261ecSmrg{
117605b261ecSmrg	double	a1, a2, a;
117705b261ecSmrg
117805b261ecSmrg	/*
117905b261ecSmrg	 * reflect from X coordinates back to ellipse
118005b261ecSmrg	 * coordinates -- y increasing upwards
118105b261ecSmrg	 */
118205b261ecSmrg	a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
118305b261ecSmrg	a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
118405b261ecSmrg	a = a2 - a1;
118505b261ecSmrg	if (a <= -180.0)
118605b261ecSmrg		a += 360.0;
118705b261ecSmrg	else if (a > 180.0)
118805b261ecSmrg		a -= 360.0;
118905b261ecSmrg	return a;
119005b261ecSmrg}
119105b261ecSmrg
119205b261ecSmrgstatic void
119305b261ecSmrgtranslateBounds (
119405b261ecSmrg	miArcFacePtr	b,
119505b261ecSmrg	int		x,
119605b261ecSmrg	int		y,
119705b261ecSmrg	double		fx,
119805b261ecSmrg	double		fy)
119905b261ecSmrg{
120005b261ecSmrg	fx += x;
120105b261ecSmrg	fy += y;
120205b261ecSmrg	b->clock.x -= fx;
120305b261ecSmrg	b->clock.y -= fy;
120405b261ecSmrg	b->center.x -= fx;
120505b261ecSmrg	b->center.y -= fy;
120605b261ecSmrg	b->counterClock.x -= fx;
120705b261ecSmrg	b->counterClock.y -= fy;
120805b261ecSmrg}
120905b261ecSmrg
121005b261ecSmrgstatic void
121105b261ecSmrgmiArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
121205b261ecSmrg	  miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
121305b261ecSmrg	  double xFtransLeft, double yFtransLeft,
121405b261ecSmrg	  int xOrgRight, int yOrgRight,
121505b261ecSmrg	  double xFtransRight, double yFtransRight)
121605b261ecSmrg{
121705b261ecSmrg	SppPointRec	center, corner, otherCorner;
121805b261ecSmrg	SppPointRec	poly[5], e;
121905b261ecSmrg	SppPointPtr	pArcPts;
122005b261ecSmrg	int		cpt;
122105b261ecSmrg	SppArcRec	arc;
122205b261ecSmrg	miArcFaceRec	Right, Left;
122305b261ecSmrg	int		polyLen = 0;
122405b261ecSmrg	int		xOrg, yOrg;
122505b261ecSmrg	double		xFtrans, yFtrans;
122605b261ecSmrg	double		a;
122705b261ecSmrg	double		ae, ac2, ec2, bc2, de;
122805b261ecSmrg	double		width;
122905b261ecSmrg
123005b261ecSmrg	xOrg = (xOrgRight + xOrgLeft) / 2;
123105b261ecSmrg	yOrg = (yOrgRight + yOrgLeft) / 2;
123205b261ecSmrg	xFtrans = (xFtransLeft + xFtransRight) / 2;
123305b261ecSmrg	yFtrans = (yFtransLeft + yFtransRight) / 2;
123405b261ecSmrg	Right = *pRight;
123505b261ecSmrg	translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
123605b261ecSmrg				 xFtrans - xFtransRight, yFtrans - yFtransRight);
123705b261ecSmrg	Left = *pLeft;
123805b261ecSmrg	translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
123905b261ecSmrg				 xFtrans - xFtransLeft, yFtrans - yFtransLeft);
124005b261ecSmrg	pRight = &Right;
124105b261ecSmrg	pLeft = &Left;
124205b261ecSmrg
124305b261ecSmrg	if (pRight->clock.x == pLeft->counterClock.x &&
124405b261ecSmrg	    pRight->clock.y == pLeft->counterClock.y)
124505b261ecSmrg		return;
124605b261ecSmrg	center = pRight->center;
124705b261ecSmrg	if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
124805b261ecSmrg 	    && a <= 180.0)
124905b261ecSmrg 	{
125005b261ecSmrg		corner = pRight->clock;
125105b261ecSmrg		otherCorner = pLeft->counterClock;
125205b261ecSmrg	} else {
125305b261ecSmrg		a = angleBetween (center, pLeft->clock, pRight->counterClock);
125405b261ecSmrg		corner = pLeft->clock;
125505b261ecSmrg		otherCorner = pRight->counterClock;
125605b261ecSmrg	}
125705b261ecSmrg	switch (pGC->joinStyle) {
125805b261ecSmrg	case JoinRound:
125905b261ecSmrg		width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
126005b261ecSmrg
126105b261ecSmrg		arc.x = center.x - width/2;
126205b261ecSmrg		arc.y = center.y - width/2;
126305b261ecSmrg		arc.width = width;
126405b261ecSmrg		arc.height = width;
126505b261ecSmrg		arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
126605b261ecSmrg		arc.angle2 = a;
12676747b715Smrg		pArcPts = malloc(3 * sizeof (SppPointRec));
126805b261ecSmrg		if (!pArcPts)
126905b261ecSmrg		    return;
127005b261ecSmrg		pArcPts[0].x = otherCorner.x;
127105b261ecSmrg		pArcPts[0].y = otherCorner.y;
127205b261ecSmrg		pArcPts[1].x = center.x;
127305b261ecSmrg		pArcPts[1].y = center.y;
127405b261ecSmrg		pArcPts[2].x = corner.x;
127505b261ecSmrg		pArcPts[2].y = corner.y;
127605b261ecSmrg		if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
127705b261ecSmrg		{
127805b261ecSmrg			/* by drawing with miFillSppPoly and setting the endpoints of the arc
127905b261ecSmrg			 * to be the corners, we assure that the cap will meet up with the
128005b261ecSmrg			 * rest of the line */
128105b261ecSmrg			miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
128205b261ecSmrg		}
12836747b715Smrg		free(pArcPts);
128405b261ecSmrg		return;
128505b261ecSmrg	case JoinMiter:
128605b261ecSmrg		/*
128705b261ecSmrg		 * don't miter arcs with less than 11 degrees between them
128805b261ecSmrg		 */
128905b261ecSmrg		if (a < 169.0) {
129005b261ecSmrg			poly[0] = corner;
129105b261ecSmrg			poly[1] = center;
129205b261ecSmrg			poly[2] = otherCorner;
129305b261ecSmrg			bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
129405b261ecSmrg			      (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
129505b261ecSmrg			ec2 = bc2 / 4;
129605b261ecSmrg			ac2 = (corner.x - center.x) * (corner.x - center.x) +
129705b261ecSmrg			      (corner.y - center.y) * (corner.y - center.y);
129805b261ecSmrg			ae = sqrt (ac2 - ec2);
129905b261ecSmrg			de = ec2 / ae;
130005b261ecSmrg			e.x = (corner.x + otherCorner.x) / 2;
130105b261ecSmrg			e.y = (corner.y + otherCorner.y) / 2;
130205b261ecSmrg			poly[3].x = e.x + de * (e.x - center.x) / ae;
130305b261ecSmrg			poly[3].y = e.y + de * (e.y - center.y) / ae;
130405b261ecSmrg			poly[4] = corner;
130505b261ecSmrg			polyLen = 5;
130605b261ecSmrg			break;
130705b261ecSmrg		}
130805b261ecSmrg	case JoinBevel:
130905b261ecSmrg		poly[0] = corner;
131005b261ecSmrg		poly[1] = center;
131105b261ecSmrg		poly[2] = otherCorner;
131205b261ecSmrg		poly[3] = corner;
131305b261ecSmrg		polyLen = 4;
131405b261ecSmrg		break;
131505b261ecSmrg	}
131605b261ecSmrg	miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
131705b261ecSmrg}
131805b261ecSmrg
131905b261ecSmrg/*ARGSUSED*/
132005b261ecSmrgstatic void
132105b261ecSmrgmiArcCap (
132205b261ecSmrg	DrawablePtr	pDraw,
132305b261ecSmrg	GCPtr		pGC,
132405b261ecSmrg	miArcFacePtr	pFace,
132505b261ecSmrg	int		end,
132605b261ecSmrg	int		xOrg,
132705b261ecSmrg	int		yOrg,
132805b261ecSmrg	double		xFtrans,
132905b261ecSmrg	double		yFtrans)
133005b261ecSmrg{
133105b261ecSmrg	SppPointRec	corner, otherCorner, center, endPoint, poly[5];
133205b261ecSmrg
133305b261ecSmrg	corner = pFace->clock;
133405b261ecSmrg	otherCorner = pFace->counterClock;
133505b261ecSmrg	center = pFace->center;
133605b261ecSmrg	switch (pGC->capStyle) {
133705b261ecSmrg	case CapProjecting:
133805b261ecSmrg		poly[0].x = otherCorner.x;
133905b261ecSmrg		poly[0].y = otherCorner.y;
134005b261ecSmrg		poly[1].x = corner.x;
134105b261ecSmrg		poly[1].y = corner.y;
134205b261ecSmrg		poly[2].x = corner.x -
134305b261ecSmrg 				(center.y - corner.y);
134405b261ecSmrg		poly[2].y = corner.y +
134505b261ecSmrg 				(center.x - corner.x);
134605b261ecSmrg		poly[3].x = otherCorner.x -
134705b261ecSmrg 				(otherCorner.y - center.y);
134805b261ecSmrg		poly[3].y = otherCorner.y +
134905b261ecSmrg 				(otherCorner.x - center.x);
135005b261ecSmrg		poly[4].x = otherCorner.x;
135105b261ecSmrg		poly[4].y = otherCorner.y;
135205b261ecSmrg		miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
135305b261ecSmrg		break;
135405b261ecSmrg	case CapRound:
135505b261ecSmrg		/*
135605b261ecSmrg		 * miRoundCap just needs these to be unequal.
135705b261ecSmrg		 */
135805b261ecSmrg		endPoint = center;
135905b261ecSmrg		endPoint.x = endPoint.x + 100;
136005b261ecSmrg		miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
136105b261ecSmrg			    -xOrg, -yOrg, xFtrans, yFtrans);
136205b261ecSmrg		break;
136305b261ecSmrg	}
136405b261ecSmrg}
136505b261ecSmrg
136605b261ecSmrg/* MIROUNDCAP -- a private helper function
136705b261ecSmrg * Put Rounded cap on end. pCenter is the center of this end of the line
136805b261ecSmrg * pEnd is the center of the other end of the line. pCorner is one of the
136905b261ecSmrg * two corners at this end of the line.
137005b261ecSmrg * NOTE:  pOtherCorner must be counter-clockwise from pCorner.
137105b261ecSmrg */
137205b261ecSmrg/*ARGSUSED*/
137305b261ecSmrgstatic void
137405b261ecSmrgmiRoundCap(
137505b261ecSmrg    DrawablePtr	pDraw,
137605b261ecSmrg    GCPtr	pGC,
137705b261ecSmrg    SppPointRec	pCenter,
137805b261ecSmrg    SppPointRec	pEnd,
137905b261ecSmrg    SppPointRec	pCorner,
138005b261ecSmrg    SppPointRec	pOtherCorner,
138105b261ecSmrg    int		fLineEnd,
138205b261ecSmrg    int		xOrg,
138305b261ecSmrg    int		yOrg,
138405b261ecSmrg    double	xFtrans,
138505b261ecSmrg    double	yFtrans)
138605b261ecSmrg{
138705b261ecSmrg    int		cpt;
138805b261ecSmrg    double	width;
138905b261ecSmrg    SppArcRec	arc;
139005b261ecSmrg    SppPointPtr	pArcPts;
139105b261ecSmrg
139205b261ecSmrg    width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
139305b261ecSmrg
139405b261ecSmrg    arc.x = pCenter.x - width/2;
139505b261ecSmrg    arc.y = pCenter.y - width/2;
139605b261ecSmrg    arc.width = width;
139705b261ecSmrg    arc.height = width;
139805b261ecSmrg    arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x);
139905b261ecSmrg    if(PTISEQUAL(pCenter, pEnd))
140005b261ecSmrg	arc.angle2 = - 180.0;
140105b261ecSmrg    else {
140205b261ecSmrg	arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
140305b261ecSmrg	if (arc.angle2 < 0)
140405b261ecSmrg	    arc.angle2 += 360.0;
140505b261ecSmrg    }
140605b261ecSmrg    pArcPts = (SppPointPtr) NULL;
140705b261ecSmrg    if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
140805b261ecSmrg    {
140905b261ecSmrg	/* by drawing with miFillSppPoly and setting the endpoints of the arc
141005b261ecSmrg	 * to be the corners, we assure that the cap will meet up with the
141105b261ecSmrg	 * rest of the line */
141205b261ecSmrg	miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
141305b261ecSmrg    }
14146747b715Smrg    free(pArcPts);
141505b261ecSmrg}
141605b261ecSmrg
141705b261ecSmrg/*
141805b261ecSmrg * To avoid inaccuracy at the cardinal points, use trig functions
141905b261ecSmrg * which are exact for those angles
142005b261ecSmrg */
142105b261ecSmrg
142205b261ecSmrg#ifndef M_PI
142305b261ecSmrg#define M_PI	3.14159265358979323846
142405b261ecSmrg#endif
142505b261ecSmrg#ifndef M_PI_2
142605b261ecSmrg#define M_PI_2	1.57079632679489661923
142705b261ecSmrg#endif
142805b261ecSmrg
142905b261ecSmrg# define Dsin(d)	((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
143005b261ecSmrg# define Dcos(d)	((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
14316747b715Smrg# define mod(a,b)	((a) >= 0 ? (a) % (b) : (b) - (-(a)) % (b))
143205b261ecSmrg
143305b261ecSmrgstatic double
143405b261ecSmrgmiDcos (double a)
143505b261ecSmrg{
143605b261ecSmrg	int	i;
143705b261ecSmrg
143805b261ecSmrg	if (floor (a/90) == a/90) {
143905b261ecSmrg		i = (int) (a/90.0);
144005b261ecSmrg		switch (mod (i, 4)) {
144105b261ecSmrg		case 0: return 1;
144205b261ecSmrg		case 1: return 0;
144305b261ecSmrg		case 2: return -1;
144405b261ecSmrg		case 3: return 0;
144505b261ecSmrg		}
144605b261ecSmrg	}
144705b261ecSmrg	return cos (a * M_PI / 180.0);
144805b261ecSmrg}
144905b261ecSmrg
145005b261ecSmrgstatic double
145105b261ecSmrgmiDsin (double a)
145205b261ecSmrg{
145305b261ecSmrg	int	i;
145405b261ecSmrg
145505b261ecSmrg	if (floor (a/90) == a/90) {
145605b261ecSmrg		i = (int) (a/90.0);
145705b261ecSmrg		switch (mod (i, 4)) {
145805b261ecSmrg		case 0: return 0;
145905b261ecSmrg		case 1: return 1;
146005b261ecSmrg		case 2: return 0;
146105b261ecSmrg		case 3: return -1;
146205b261ecSmrg		}
146305b261ecSmrg	}
146405b261ecSmrg	return sin (a * M_PI / 180.0);
146505b261ecSmrg}
146605b261ecSmrg
146705b261ecSmrgstatic double
146805b261ecSmrgmiDasin (double v)
146905b261ecSmrg{
147005b261ecSmrg    if (v == 0)
147105b261ecSmrg	return 0.0;
147205b261ecSmrg    if (v == 1.0)
147305b261ecSmrg	return 90.0;
147405b261ecSmrg    if (v == -1.0)
147505b261ecSmrg	return -90.0;
147605b261ecSmrg    return asin(v) * (180.0 / M_PI);
147705b261ecSmrg}
147805b261ecSmrg
147905b261ecSmrgstatic double
148005b261ecSmrgmiDatan2 (double dy, double dx)
148105b261ecSmrg{
148205b261ecSmrg    if (dy == 0) {
148305b261ecSmrg	if (dx >= 0)
148405b261ecSmrg	    return 0.0;
148505b261ecSmrg	return 180.0;
148605b261ecSmrg    } else if (dx == 0) {
148705b261ecSmrg	if (dy > 0)
148805b261ecSmrg	    return 90.0;
148905b261ecSmrg	return -90.0;
149005b261ecSmrg    } else if (Fabs (dy) == Fabs (dx)) {
149105b261ecSmrg	if (dy > 0) {
149205b261ecSmrg	    if (dx > 0)
149305b261ecSmrg		return 45.0;
149405b261ecSmrg	    return 135.0;
149505b261ecSmrg	} else {
149605b261ecSmrg	    if (dx > 0)
149705b261ecSmrg		return 315.0;
149805b261ecSmrg	    return 225.0;
149905b261ecSmrg	}
150005b261ecSmrg    } else {
150105b261ecSmrg	return atan2 (dy, dx) * (180.0 / M_PI);
150205b261ecSmrg    }
150305b261ecSmrg}
150405b261ecSmrg
150505b261ecSmrg/* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
150605b261ecSmrg * routine for filled arc and line (round cap) code.
150705b261ecSmrg * Returns the number of points in the arc.  Note that it takes a pointer
150805b261ecSmrg * to a pointer to where it should put the points and an index (cpt).
150905b261ecSmrg * This procedure allocates the space necessary to fit the arc points.
151005b261ecSmrg * Sometimes it's convenient for those points to be at the end of an existing
151105b261ecSmrg * array. (For example, if we want to leave a spare point to make sectors
15126747b715Smrg * instead of segments.)  So we pass in the malloc()ed chunk that contains the
151305b261ecSmrg * array and an index saying where we should start stashing the points.
151405b261ecSmrg * If there isn't an array already, we just pass in a null pointer and
15156747b715Smrg * count on realloc() to handle the null pointer correctly.
151605b261ecSmrg */
151705b261ecSmrgstatic int
151805b261ecSmrgmiGetArcPts(
151905b261ecSmrg    SppArcPtr	parc,	/* points to an arc */
152005b261ecSmrg    int		cpt,	/* number of points already in arc list */
152105b261ecSmrg    SppPointPtr	*ppPts) /* pointer to pointer to arc-list -- modified */
152205b261ecSmrg{
152305b261ecSmrg    double 	st,	/* Start Theta, start angle */
152405b261ecSmrg                et,	/* End Theta, offset from start theta */
152505b261ecSmrg		dt,	/* Delta Theta, angle to sweep ellipse */
152605b261ecSmrg		cdt,	/* Cos Delta Theta, actually 2 cos(dt) */
152705b261ecSmrg    		x0, y0,	/* the recurrence formula needs two points to start */
152805b261ecSmrg		x1, y1,
152905b261ecSmrg		x2, y2, /* this will be the new point generated */
153005b261ecSmrg		xc, yc; /* the center point */
153105b261ecSmrg    int		count, i;
153205b261ecSmrg    SppPointPtr	poly;
153305b261ecSmrg
153405b261ecSmrg    /* The spec says that positive angles indicate counterclockwise motion.
153505b261ecSmrg     * Given our coordinate system (with 0,0 in the upper left corner),
153605b261ecSmrg     * the screen appears flipped in Y.  The easiest fix is to negate the
153705b261ecSmrg     * angles given */
153805b261ecSmrg
153905b261ecSmrg    st = - parc->angle1;
154005b261ecSmrg
154105b261ecSmrg    et = - parc->angle2;
154205b261ecSmrg
154305b261ecSmrg    /* Try to get a delta theta that is within 1/2 pixel.  Then adjust it
154405b261ecSmrg     * so that it divides evenly into the total.
154505b261ecSmrg     * I'm just using cdt 'cause I'm lazy.
154605b261ecSmrg     */
154705b261ecSmrg    cdt = parc->width;
154805b261ecSmrg    if (parc->height > cdt)
154905b261ecSmrg	cdt = parc->height;
155005b261ecSmrg    cdt /= 2.0;
155105b261ecSmrg    if(cdt <= 0)
155205b261ecSmrg	return 0;
155305b261ecSmrg    if (cdt < 1.0)
155405b261ecSmrg	cdt = 1.0;
155505b261ecSmrg    dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
155605b261ecSmrg    count = et/dt;
155705b261ecSmrg    count = abs(count) + 1;
155805b261ecSmrg    dt = et/count;
155905b261ecSmrg    count++;
156005b261ecSmrg
156105b261ecSmrg    cdt = 2 * miDcos(dt);
15626747b715Smrg    if (!(poly = (SppPointPtr) realloc((pointer)*ppPts,
156305b261ecSmrg					(cpt + count) * sizeof(SppPointRec))))
15646747b715Smrg	return 0;
156505b261ecSmrg    *ppPts = poly;
156605b261ecSmrg
156705b261ecSmrg    xc = parc->width/2.0;		/* store half width and half height */
156805b261ecSmrg    yc = parc->height/2.0;
156905b261ecSmrg
157005b261ecSmrg    x0 = xc * miDcos(st);
157105b261ecSmrg    y0 = yc * miDsin(st);
157205b261ecSmrg    x1 = xc * miDcos(st + dt);
157305b261ecSmrg    y1 = yc * miDsin(st + dt);
157405b261ecSmrg    xc += parc->x;		/* by adding initial point, these become */
157505b261ecSmrg    yc += parc->y;		/* the center point */
157605b261ecSmrg
157705b261ecSmrg    poly[cpt].x = (xc + x0);
157805b261ecSmrg    poly[cpt].y = (yc + y0);
157905b261ecSmrg    poly[cpt + 1].x = (xc + x1);
158005b261ecSmrg    poly[cpt + 1].y = (yc + y1);
158105b261ecSmrg
158205b261ecSmrg    for(i = 2; i < count; i++)
158305b261ecSmrg    {
158405b261ecSmrg	x2 = cdt * x1 - x0;
158505b261ecSmrg	y2 = cdt * y1 - y0;
158605b261ecSmrg
158705b261ecSmrg 	poly[cpt + i].x = (xc + x2);
158805b261ecSmrg 	poly[cpt + i].y = (yc + y2);
158905b261ecSmrg
159005b261ecSmrg	x0 = x1; y0 = y1;
159105b261ecSmrg	x1 = x2; y1 = y2;
159205b261ecSmrg    }
159305b261ecSmrg    /* adjust the last point */
15942f9f5fecSjoerg    if (fabs(parc->angle2) >= 360.0)
159505b261ecSmrg	poly[cpt +i -1] = poly[0];
159605b261ecSmrg    else {
159705b261ecSmrg	poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
159805b261ecSmrg	poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
159905b261ecSmrg    }
160005b261ecSmrg
16016747b715Smrg    return count;
160205b261ecSmrg}
160305b261ecSmrg
160405b261ecSmrgstruct arcData {
160505b261ecSmrg	double	x0, y0, x1, y1;
160605b261ecSmrg	int	selfJoin;
160705b261ecSmrg};
160805b261ecSmrg
160905b261ecSmrg# define ADD_REALLOC_STEP	20
161005b261ecSmrg
161105b261ecSmrgstatic void
161205b261ecSmrgaddCap (
161305b261ecSmrg	miArcCapPtr	*capsp,
161405b261ecSmrg	int		*ncapsp,
161505b261ecSmrg	int		*sizep,
161605b261ecSmrg	int		end,
161705b261ecSmrg	int		arcIndex)
161805b261ecSmrg{
161905b261ecSmrg	int newsize;
162005b261ecSmrg	miArcCapPtr	cap;
162105b261ecSmrg
162205b261ecSmrg	if (*ncapsp == *sizep)
162305b261ecSmrg	{
162405b261ecSmrg	    newsize = *sizep + ADD_REALLOC_STEP;
16256747b715Smrg	    cap = (miArcCapPtr) realloc(*capsp,
162605b261ecSmrg					  newsize * sizeof (**capsp));
162705b261ecSmrg	    if (!cap)
162805b261ecSmrg		return;
162905b261ecSmrg	    *sizep = newsize;
163005b261ecSmrg	    *capsp = cap;
163105b261ecSmrg	}
163205b261ecSmrg	cap = &(*capsp)[*ncapsp];
163305b261ecSmrg	cap->end = end;
163405b261ecSmrg	cap->arcIndex = arcIndex;
163505b261ecSmrg	++*ncapsp;
163605b261ecSmrg}
163705b261ecSmrg
163805b261ecSmrgstatic void
163905b261ecSmrgaddJoin (
164005b261ecSmrg	miArcJoinPtr	*joinsp,
164105b261ecSmrg	int		*njoinsp,
164205b261ecSmrg	int		*sizep,
164305b261ecSmrg	int		end0,
164405b261ecSmrg	int		index0,
164505b261ecSmrg	int		phase0,
164605b261ecSmrg	int		end1,
164705b261ecSmrg	int		index1,
164805b261ecSmrg	int		phase1)
164905b261ecSmrg{
165005b261ecSmrg	int newsize;
165105b261ecSmrg	miArcJoinPtr	join;
165205b261ecSmrg
165305b261ecSmrg	if (*njoinsp == *sizep)
165405b261ecSmrg	{
165505b261ecSmrg	    newsize = *sizep + ADD_REALLOC_STEP;
16566747b715Smrg	    join = (miArcJoinPtr) realloc(*joinsp,
165705b261ecSmrg					    newsize * sizeof (**joinsp));
165805b261ecSmrg	    if (!join)
165905b261ecSmrg		return;
166005b261ecSmrg	    *sizep = newsize;
166105b261ecSmrg	    *joinsp = join;
166205b261ecSmrg	}
166305b261ecSmrg	join = &(*joinsp)[*njoinsp];
166405b261ecSmrg	join->end0 = end0;
166505b261ecSmrg	join->arcIndex0 = index0;
166605b261ecSmrg	join->phase0 = phase0;
166705b261ecSmrg	join->end1 = end1;
166805b261ecSmrg	join->arcIndex1 = index1;
166905b261ecSmrg	join->phase1 = phase1;
167005b261ecSmrg	++*njoinsp;
167105b261ecSmrg}
167205b261ecSmrg
167305b261ecSmrgstatic miArcDataPtr
167405b261ecSmrgaddArc (
167505b261ecSmrg	miArcDataPtr	*arcsp,
167605b261ecSmrg	int		*narcsp,
167705b261ecSmrg	int		*sizep,
167805b261ecSmrg	xArc		*xarc)
167905b261ecSmrg{
168005b261ecSmrg	int newsize;
168105b261ecSmrg	miArcDataPtr	arc;
168205b261ecSmrg
168305b261ecSmrg	if (*narcsp == *sizep)
168405b261ecSmrg	{
168505b261ecSmrg	    newsize = *sizep + ADD_REALLOC_STEP;
16866747b715Smrg	    arc = (miArcDataPtr) realloc(*arcsp,
168705b261ecSmrg					   newsize * sizeof (**arcsp));
168805b261ecSmrg	    if (!arc)
16896747b715Smrg		return NULL;
169005b261ecSmrg	    *sizep = newsize;
169105b261ecSmrg	    *arcsp = arc;
169205b261ecSmrg	}
169305b261ecSmrg	arc = &(*arcsp)[*narcsp];
169405b261ecSmrg	arc->arc = *xarc;
169505b261ecSmrg	++*narcsp;
169605b261ecSmrg	return arc;
169705b261ecSmrg}
169805b261ecSmrg
169905b261ecSmrgstatic void
170005b261ecSmrgmiFreeArcs(
170105b261ecSmrg    miPolyArcPtr arcs,
170205b261ecSmrg    GCPtr pGC)
170305b261ecSmrg{
170405b261ecSmrg	int iphase;
170505b261ecSmrg
170605b261ecSmrg	for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
170705b261ecSmrg 	     iphase >= 0;
170805b261ecSmrg	     iphase--)
170905b261ecSmrg	{
171005b261ecSmrg	    if (arcs[iphase].narcs > 0)
17116747b715Smrg		free(arcs[iphase].arcs);
171205b261ecSmrg	    if (arcs[iphase].njoins > 0)
17136747b715Smrg		free(arcs[iphase].joins);
171405b261ecSmrg	    if (arcs[iphase].ncaps > 0)
17156747b715Smrg		free(arcs[iphase].caps);
171605b261ecSmrg	}
17176747b715Smrg	free(arcs);
171805b261ecSmrg}
171905b261ecSmrg
172005b261ecSmrg/*
172105b261ecSmrg * map angles to radial distance.  This only deals with the first quadrant
172205b261ecSmrg */
172305b261ecSmrg
172405b261ecSmrg/*
172505b261ecSmrg * a polygonal approximation to the arc for computing arc lengths
172605b261ecSmrg */
172705b261ecSmrg
172805b261ecSmrg# define DASH_MAP_SIZE	91
172905b261ecSmrg
173005b261ecSmrg# define dashIndexToAngle(di)	((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
173105b261ecSmrg# define xAngleToDashIndex(xa)	((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
173205b261ecSmrg# define dashIndexToXAngle(di)	((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
173305b261ecSmrg# define dashXAngleStep	(((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
173405b261ecSmrg
173505b261ecSmrgtypedef struct {
173605b261ecSmrg	double	map[DASH_MAP_SIZE];
173705b261ecSmrg} dashMap;
173805b261ecSmrg
173905b261ecSmrgstatic int computeAngleFromPath(int startAngle, int endAngle, dashMap *map,
174005b261ecSmrg				int *lenp, int backwards);
174105b261ecSmrg
174205b261ecSmrgstatic void
174305b261ecSmrgcomputeDashMap (
174405b261ecSmrg	xArc	*arcp,
174505b261ecSmrg	dashMap	*map)
174605b261ecSmrg{
174705b261ecSmrg	int	di;
174805b261ecSmrg	double	a, x, y, prevx = 0.0, prevy = 0.0, dist;
174905b261ecSmrg
175005b261ecSmrg	for (di = 0; di < DASH_MAP_SIZE; di++) {
175105b261ecSmrg		a = dashIndexToAngle (di);
175205b261ecSmrg		x = ((double) arcp->width / 2.0) * miDcos (a);
175305b261ecSmrg		y = ((double) arcp->height / 2.0) * miDsin (a);
175405b261ecSmrg		if (di == 0) {
175505b261ecSmrg			map->map[di] = 0.0;
175605b261ecSmrg		} else {
175705b261ecSmrg			dist = hypot (x - prevx, y - prevy);
175805b261ecSmrg			map->map[di] = map->map[di - 1] + dist;
175905b261ecSmrg		}
176005b261ecSmrg		prevx = x;
176105b261ecSmrg		prevy = y;
176205b261ecSmrg	}
176305b261ecSmrg}
176405b261ecSmrg
176505b261ecSmrgtypedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
176605b261ecSmrg
176705b261ecSmrg/* this routine is a bit gory */
176805b261ecSmrg
176905b261ecSmrgstatic miPolyArcPtr
177005b261ecSmrgmiComputeArcs (
177105b261ecSmrg	xArc	*parcs,
177205b261ecSmrg	int	narcs,
177305b261ecSmrg	GCPtr	pGC)
177405b261ecSmrg{
177505b261ecSmrg	int		isDashed, isDoubleDash;
177605b261ecSmrg	int		dashOffset;
177705b261ecSmrg	miPolyArcPtr	arcs;
177805b261ecSmrg	int		start, i, j, k = 0, nexti, nextk = 0;
177905b261ecSmrg	int		joinSize[2];
178005b261ecSmrg	int		capSize[2];
178105b261ecSmrg	int		arcSize[2];
178205b261ecSmrg	int		angle2;
178305b261ecSmrg	double		a0, a1;
178405b261ecSmrg	struct arcData	*data;
178505b261ecSmrg	miArcDataPtr	arc;
178605b261ecSmrg	xArc		xarc;
178705b261ecSmrg	int		iphase, prevphase = 0, joinphase;
178805b261ecSmrg	int		arcsJoin;
178905b261ecSmrg	int		selfJoin;
179005b261ecSmrg
17916747b715Smrg	int		iDash = 0, dashRemaining = 0;
179205b261ecSmrg	int		iDashStart = 0, dashRemainingStart = 0, iphaseStart;
179305b261ecSmrg	int		startAngle, spanAngle, endAngle, backwards = 0;
179405b261ecSmrg	int		prevDashAngle, dashAngle;
179505b261ecSmrg	dashMap		map;
179605b261ecSmrg
179705b261ecSmrg	isDashed = !(pGC->lineStyle == LineSolid);
179805b261ecSmrg	isDoubleDash = (pGC->lineStyle == LineDoubleDash);
179905b261ecSmrg	dashOffset = pGC->dashOffset;
180005b261ecSmrg
18016747b715Smrg	data = malloc(narcs * sizeof (struct arcData));
180205b261ecSmrg	if (!data)
18036747b715Smrg	    return NULL;
18046747b715Smrg	arcs = malloc(sizeof (*arcs) * (isDoubleDash ? 2 : 1));
180505b261ecSmrg	if (!arcs)
180605b261ecSmrg	{
18076747b715Smrg	    free(data);
18086747b715Smrg	    return NULL;
180905b261ecSmrg	}
181005b261ecSmrg	for (i = 0; i < narcs; i++) {
181105b261ecSmrg		a0 = todeg (parcs[i].angle1);
181205b261ecSmrg		angle2 = parcs[i].angle2;
181305b261ecSmrg		if (angle2 > FULLCIRCLE)
181405b261ecSmrg			angle2 = FULLCIRCLE;
181505b261ecSmrg		else if (angle2 < -FULLCIRCLE)
181605b261ecSmrg			angle2 = -FULLCIRCLE;
181705b261ecSmrg		data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
181805b261ecSmrg		a1 = todeg (parcs[i].angle1 + angle2);
181905b261ecSmrg		data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
182005b261ecSmrg		data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
182105b261ecSmrg		data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
182205b261ecSmrg		data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
182305b261ecSmrg	}
182405b261ecSmrg
182505b261ecSmrg	for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
182605b261ecSmrg		arcs[iphase].njoins = 0;
182705b261ecSmrg		arcs[iphase].joins = 0;
182805b261ecSmrg		joinSize[iphase] = 0;
182905b261ecSmrg
183005b261ecSmrg		arcs[iphase].ncaps = 0;
183105b261ecSmrg		arcs[iphase].caps = 0;
183205b261ecSmrg		capSize[iphase] = 0;
183305b261ecSmrg
183405b261ecSmrg		arcs[iphase].narcs = 0;
183505b261ecSmrg		arcs[iphase].arcs = 0;
183605b261ecSmrg		arcSize[iphase] = 0;
183705b261ecSmrg	}
183805b261ecSmrg
183905b261ecSmrg	iphase = 0;
184005b261ecSmrg	if (isDashed) {
184105b261ecSmrg		iDash = 0;
184205b261ecSmrg		dashRemaining = pGC->dash[0];
184305b261ecSmrg	 	while (dashOffset > 0) {
184405b261ecSmrg			if (dashOffset >= dashRemaining) {
184505b261ecSmrg				dashOffset -= dashRemaining;
184605b261ecSmrg				iphase = iphase ? 0 : 1;
184705b261ecSmrg				iDash++;
184805b261ecSmrg				if (iDash == pGC->numInDashList)
184905b261ecSmrg				    iDash = 0;
185005b261ecSmrg				dashRemaining = pGC->dash[iDash];
185105b261ecSmrg			} else {
185205b261ecSmrg				dashRemaining -= dashOffset;
185305b261ecSmrg				dashOffset = 0;
185405b261ecSmrg			}
185505b261ecSmrg		}
185605b261ecSmrg		iDashStart = iDash;
185705b261ecSmrg		dashRemainingStart = dashRemaining;
185805b261ecSmrg	}
185905b261ecSmrg	iphaseStart = iphase;
186005b261ecSmrg
186105b261ecSmrg	for (i = narcs - 1; i >= 0; i--) {
186205b261ecSmrg		j = i + 1;
186305b261ecSmrg		if (j == narcs)
186405b261ecSmrg			j = 0;
186505b261ecSmrg		if (data[i].selfJoin || i == j ||
186605b261ecSmrg		     (UNEQUAL (data[i].x1, data[j].x0) ||
186705b261ecSmrg		      UNEQUAL (data[i].y1, data[j].y0)))
186805b261ecSmrg 		{
186905b261ecSmrg			if (iphase == 0 || isDoubleDash)
187005b261ecSmrg				addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
187105b261ecSmrg	 				&capSize[iphase], RIGHT_END, 0);
187205b261ecSmrg			break;
187305b261ecSmrg		}
187405b261ecSmrg	}
187505b261ecSmrg	start = i + 1;
187605b261ecSmrg	if (start == narcs)
187705b261ecSmrg		start = 0;
187805b261ecSmrg	i = start;
187905b261ecSmrg	for (;;) {
188005b261ecSmrg		j = i + 1;
188105b261ecSmrg		if (j == narcs)
188205b261ecSmrg			j = 0;
188305b261ecSmrg		nexti = i+1;
188405b261ecSmrg		if (nexti == narcs)
188505b261ecSmrg			nexti = 0;
188605b261ecSmrg		if (isDashed) {
188705b261ecSmrg			/*
188805b261ecSmrg			** deal with dashed arcs.  Use special rules for certain 0 area arcs.
188905b261ecSmrg			** Presumably, the other 0 area arcs still aren't done right.
189005b261ecSmrg			*/
189105b261ecSmrg			arcTypes	arcType = OTHER;
189205b261ecSmrg			CARD16		thisLength;
189305b261ecSmrg
189405b261ecSmrg			if (parcs[i].height == 0
189505b261ecSmrg			    && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
189605b261ecSmrg			    && parcs[i].angle2 == 0x2d00)
189705b261ecSmrg				arcType = HORIZONTAL;
189805b261ecSmrg			else if (parcs[i].width == 0
189905b261ecSmrg			    && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
190005b261ecSmrg			    && parcs[i].angle2 == 0x2d00)
190105b261ecSmrg				arcType = VERTICAL;
190205b261ecSmrg			if (arcType == OTHER) {
190305b261ecSmrg				/*
190405b261ecSmrg				 * precompute an approximation map
190505b261ecSmrg				 */
190605b261ecSmrg				computeDashMap (&parcs[i], &map);
190705b261ecSmrg				/*
190805b261ecSmrg				 * compute each individual dash segment using the path
190905b261ecSmrg				 * length function
191005b261ecSmrg				 */
191105b261ecSmrg				startAngle = parcs[i].angle1;
191205b261ecSmrg				spanAngle = parcs[i].angle2;
191305b261ecSmrg				if (spanAngle > FULLCIRCLE)
191405b261ecSmrg					spanAngle = FULLCIRCLE;
191505b261ecSmrg				else if (spanAngle < -FULLCIRCLE)
191605b261ecSmrg					spanAngle = -FULLCIRCLE;
191705b261ecSmrg				if (startAngle < 0)
191805b261ecSmrg					startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
191905b261ecSmrg				if (startAngle >= FULLCIRCLE)
192005b261ecSmrg					startAngle = startAngle % FULLCIRCLE;
192105b261ecSmrg				endAngle = startAngle + spanAngle;
192205b261ecSmrg				backwards = spanAngle < 0;
192305b261ecSmrg			} else {
192405b261ecSmrg				xarc = parcs[i];
192505b261ecSmrg				if (arcType == VERTICAL) {
192605b261ecSmrg					xarc.angle1 = 0x1680;
192705b261ecSmrg					startAngle = parcs[i].y;
192805b261ecSmrg					endAngle = startAngle + parcs[i].height;
192905b261ecSmrg				} else {
193005b261ecSmrg					xarc.angle1 = 0x2d00;
193105b261ecSmrg					startAngle = parcs[i].x;
193205b261ecSmrg					endAngle = startAngle + parcs[i].width;
193305b261ecSmrg				}
193405b261ecSmrg			}
193505b261ecSmrg			dashAngle = startAngle;
193605b261ecSmrg			selfJoin = data[i].selfJoin &&
193705b261ecSmrg 				    (iphase == 0 || isDoubleDash);
193805b261ecSmrg			/*
193905b261ecSmrg			 * add dashed arcs to each bucket
194005b261ecSmrg			 */
194105b261ecSmrg			arc = 0;
194205b261ecSmrg			while (dashAngle != endAngle) {
194305b261ecSmrg				prevDashAngle = dashAngle;
194405b261ecSmrg				if (arcType == OTHER) {
194505b261ecSmrg					dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
194605b261ecSmrg								&map, &dashRemaining, backwards);
194705b261ecSmrg					/* avoid troubles with huge arcs and small dashes */
194805b261ecSmrg					if (dashAngle == prevDashAngle) {
194905b261ecSmrg						if (backwards)
195005b261ecSmrg							dashAngle--;
195105b261ecSmrg						else
195205b261ecSmrg							dashAngle++;
195305b261ecSmrg					}
195405b261ecSmrg				} else {
195505b261ecSmrg					thisLength = (dashAngle + dashRemaining <= endAngle) ?
195605b261ecSmrg					    dashRemaining : endAngle - dashAngle;
195705b261ecSmrg					if (arcType == VERTICAL) {
195805b261ecSmrg						xarc.y = dashAngle;
195905b261ecSmrg						xarc.height = thisLength;
196005b261ecSmrg					} else {
196105b261ecSmrg						xarc.x = dashAngle;
196205b261ecSmrg						xarc.width = thisLength;
196305b261ecSmrg					}
196405b261ecSmrg					dashAngle += thisLength;
196505b261ecSmrg					dashRemaining -= thisLength;
196605b261ecSmrg				}
196705b261ecSmrg				if (iphase == 0 || isDoubleDash) {
196805b261ecSmrg					if (arcType == OTHER) {
196905b261ecSmrg						xarc = parcs[i];
197005b261ecSmrg						spanAngle = prevDashAngle;
197105b261ecSmrg						if (spanAngle < 0)
197205b261ecSmrg						    spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
197305b261ecSmrg						if (spanAngle >= FULLCIRCLE)
197405b261ecSmrg						    spanAngle = spanAngle % FULLCIRCLE;
197505b261ecSmrg						xarc.angle1 = spanAngle;
197605b261ecSmrg						spanAngle = dashAngle - prevDashAngle;
197705b261ecSmrg						if (backwards) {
197805b261ecSmrg							if (dashAngle > prevDashAngle)
197905b261ecSmrg								spanAngle = - FULLCIRCLE + spanAngle;
198005b261ecSmrg						} else {
198105b261ecSmrg							if (dashAngle < prevDashAngle)
198205b261ecSmrg								spanAngle = FULLCIRCLE + spanAngle;
198305b261ecSmrg						}
198405b261ecSmrg						if (spanAngle > FULLCIRCLE)
198505b261ecSmrg						    spanAngle = FULLCIRCLE;
198605b261ecSmrg						if (spanAngle < -FULLCIRCLE)
198705b261ecSmrg						    spanAngle = -FULLCIRCLE;
198805b261ecSmrg						xarc.angle2 = spanAngle;
198905b261ecSmrg					}
199005b261ecSmrg					arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
199105b261ecSmrg 							&arcSize[iphase], &xarc);
199205b261ecSmrg					if (!arc)
199305b261ecSmrg					    goto arcfail;
199405b261ecSmrg					/*
199505b261ecSmrg					 * cap each end of an on/off dash
199605b261ecSmrg					 */
199705b261ecSmrg					if (!isDoubleDash) {
199805b261ecSmrg						if (prevDashAngle != startAngle) {
199905b261ecSmrg							addCap (&arcs[iphase].caps,
200005b261ecSmrg 								&arcs[iphase].ncaps,
200105b261ecSmrg 								&capSize[iphase], RIGHT_END,
200205b261ecSmrg 								arc - arcs[iphase].arcs);
200305b261ecSmrg
200405b261ecSmrg						}
200505b261ecSmrg						if (dashAngle != endAngle) {
200605b261ecSmrg							addCap (&arcs[iphase].caps,
200705b261ecSmrg 								&arcs[iphase].ncaps,
200805b261ecSmrg 								&capSize[iphase], LEFT_END,
200905b261ecSmrg 								arc - arcs[iphase].arcs);
201005b261ecSmrg						}
201105b261ecSmrg					}
201205b261ecSmrg					arc->cap = arcs[iphase].ncaps;
201305b261ecSmrg					arc->join = arcs[iphase].njoins;
201405b261ecSmrg					arc->render = 0;
201505b261ecSmrg					arc->selfJoin = 0;
201605b261ecSmrg					if (dashAngle == endAngle)
201705b261ecSmrg						arc->selfJoin = selfJoin;
201805b261ecSmrg				}
201905b261ecSmrg				prevphase = iphase;
202005b261ecSmrg				if (dashRemaining <= 0) {
202105b261ecSmrg					++iDash;
202205b261ecSmrg					if (iDash == pGC->numInDashList)
202305b261ecSmrg						iDash = 0;
202405b261ecSmrg					iphase = iphase ? 0:1;
202505b261ecSmrg					dashRemaining = pGC->dash[iDash];
202605b261ecSmrg				}
202705b261ecSmrg			}
202805b261ecSmrg			/*
202905b261ecSmrg			 * make sure a place exists for the position data when
203005b261ecSmrg			 * drawing a zero-length arc
203105b261ecSmrg			 */
203205b261ecSmrg			if (startAngle == endAngle) {
203305b261ecSmrg				prevphase = iphase;
203405b261ecSmrg				if (!isDoubleDash && iphase == 1)
203505b261ecSmrg					prevphase = 0;
203605b261ecSmrg				arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
203705b261ecSmrg					      &arcSize[prevphase], &parcs[i]);
203805b261ecSmrg				if (!arc)
203905b261ecSmrg				    goto arcfail;
204005b261ecSmrg				arc->join = arcs[prevphase].njoins;
204105b261ecSmrg				arc->cap = arcs[prevphase].ncaps;
204205b261ecSmrg				arc->selfJoin = data[i].selfJoin;
204305b261ecSmrg			}
204405b261ecSmrg		} else {
204505b261ecSmrg			arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
204605b261ecSmrg 				      &arcSize[iphase], &parcs[i]);
204705b261ecSmrg			if (!arc)
204805b261ecSmrg			    goto arcfail;
204905b261ecSmrg			arc->join = arcs[iphase].njoins;
205005b261ecSmrg			arc->cap = arcs[iphase].ncaps;
205105b261ecSmrg			arc->selfJoin = data[i].selfJoin;
205205b261ecSmrg			prevphase = iphase;
205305b261ecSmrg		}
205405b261ecSmrg		if (prevphase == 0 || isDoubleDash)
205505b261ecSmrg			k = arcs[prevphase].narcs - 1;
205605b261ecSmrg		if (iphase == 0 || isDoubleDash)
205705b261ecSmrg			nextk = arcs[iphase].narcs;
205805b261ecSmrg		if (nexti == start) {
205905b261ecSmrg			nextk = 0;
206005b261ecSmrg			if (isDashed) {
206105b261ecSmrg				iDash = iDashStart;
206205b261ecSmrg				iphase = iphaseStart;
206305b261ecSmrg				dashRemaining = dashRemainingStart;
206405b261ecSmrg			}
206505b261ecSmrg		}
206605b261ecSmrg		arcsJoin = narcs > 1 && i != j &&
206705b261ecSmrg	 		    ISEQUAL (data[i].x1, data[j].x0) &&
206805b261ecSmrg			    ISEQUAL (data[i].y1, data[j].y0) &&
206905b261ecSmrg			    !data[i].selfJoin && !data[j].selfJoin;
207005b261ecSmrg		if (arc)
207105b261ecSmrg		{
207205b261ecSmrg			if (arcsJoin)
207305b261ecSmrg				arc->render = 0;
207405b261ecSmrg			else
207505b261ecSmrg				arc->render = 1;
207605b261ecSmrg		}
207705b261ecSmrg		if (arcsJoin &&
207805b261ecSmrg		    (prevphase == 0 || isDoubleDash) &&
207905b261ecSmrg		    (iphase == 0 || isDoubleDash))
208005b261ecSmrg 		{
208105b261ecSmrg			joinphase = iphase;
208205b261ecSmrg			if (isDoubleDash) {
208305b261ecSmrg				if (nexti == start)
208405b261ecSmrg					joinphase = iphaseStart;
208505b261ecSmrg				/*
208605b261ecSmrg				 * if the join is right at the dash,
208705b261ecSmrg				 * draw the join in foreground
208805b261ecSmrg				 * This is because the foreground
208905b261ecSmrg				 * arcs are computed second, the results
209005b261ecSmrg				 * of which are needed to draw the join
209105b261ecSmrg				 */
209205b261ecSmrg				if (joinphase != prevphase)
209305b261ecSmrg					joinphase = 0;
209405b261ecSmrg			}
209505b261ecSmrg			if (joinphase == 0 || isDoubleDash) {
209605b261ecSmrg				addJoin (&arcs[joinphase].joins,
209705b261ecSmrg 					 &arcs[joinphase].njoins,
209805b261ecSmrg 					 &joinSize[joinphase],
209905b261ecSmrg	 				 LEFT_END, k, prevphase,
210005b261ecSmrg	 				 RIGHT_END, nextk, iphase);
210105b261ecSmrg				arc->join = arcs[prevphase].njoins;
210205b261ecSmrg			}
210305b261ecSmrg		} else {
210405b261ecSmrg			/*
210505b261ecSmrg			 * cap the left end of this arc
210605b261ecSmrg			 * unless it joins itself
210705b261ecSmrg			 */
210805b261ecSmrg			if ((prevphase == 0 || isDoubleDash) &&
210905b261ecSmrg			    !arc->selfJoin)
211005b261ecSmrg			{
211105b261ecSmrg				addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
211205b261ecSmrg 					&capSize[prevphase], LEFT_END, k);
211305b261ecSmrg				arc->cap = arcs[prevphase].ncaps;
211405b261ecSmrg			}
211505b261ecSmrg			if (isDashed && !arcsJoin) {
211605b261ecSmrg				iDash = iDashStart;
211705b261ecSmrg				iphase = iphaseStart;
211805b261ecSmrg				dashRemaining = dashRemainingStart;
211905b261ecSmrg			}
212005b261ecSmrg			nextk = arcs[iphase].narcs;
212105b261ecSmrg			if (nexti == start) {
212205b261ecSmrg				nextk = 0;
212305b261ecSmrg				iDash = iDashStart;
212405b261ecSmrg				iphase = iphaseStart;
212505b261ecSmrg				dashRemaining = dashRemainingStart;
212605b261ecSmrg			}
212705b261ecSmrg			/*
212805b261ecSmrg			 * cap the right end of the next arc.  If the
212905b261ecSmrg			 * next arc is actually the first arc, only
213005b261ecSmrg			 * cap it if it joins with this arc.  This
213105b261ecSmrg			 * case will occur when the final dash segment
213205b261ecSmrg			 * of an on/off dash is off.  Of course, this
213305b261ecSmrg			 * cap will be drawn at a strange time, but that
213405b261ecSmrg			 * hardly matters...
213505b261ecSmrg			 */
213605b261ecSmrg			if ((iphase == 0 || isDoubleDash) &&
213705b261ecSmrg			    (nexti != start || (arcsJoin && isDashed)))
213805b261ecSmrg				addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
213905b261ecSmrg 					&capSize[iphase], RIGHT_END, nextk);
214005b261ecSmrg		}
214105b261ecSmrg		i = nexti;
214205b261ecSmrg		if (i == start)
214305b261ecSmrg			break;
214405b261ecSmrg	}
214505b261ecSmrg	/*
214605b261ecSmrg	 * make sure the last section is rendered
214705b261ecSmrg	 */
214805b261ecSmrg	for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
214905b261ecSmrg		if (arcs[iphase].narcs > 0) {
215005b261ecSmrg			arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
215105b261ecSmrg			arcs[iphase].arcs[arcs[iphase].narcs-1].join =
215205b261ecSmrg			         arcs[iphase].njoins;
215305b261ecSmrg			arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
215405b261ecSmrg			         arcs[iphase].ncaps;
215505b261ecSmrg		}
21566747b715Smrg	free(data);
215705b261ecSmrg	return arcs;
215805b261ecSmrgarcfail:
215905b261ecSmrg	miFreeArcs(arcs, pGC);
21606747b715Smrg	free(data);
21616747b715Smrg	return NULL;
216205b261ecSmrg}
216305b261ecSmrg
216405b261ecSmrgstatic double
216505b261ecSmrgangleToLength (
216605b261ecSmrg	int	angle,
216705b261ecSmrg	dashMap	*map)
216805b261ecSmrg{
216905b261ecSmrg	double	len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
217005b261ecSmrg	int	di;
217105b261ecSmrg	int	excess;
217205b261ecSmrg	Bool	oddSide = FALSE;
217305b261ecSmrg
217405b261ecSmrg	totallen = 0;
217505b261ecSmrg	if (angle >= 0) {
217605b261ecSmrg		while (angle >= 90 * 64) {
217705b261ecSmrg			angle -= 90 * 64;
217805b261ecSmrg			totallen += sidelen;
217905b261ecSmrg			oddSide = !oddSide;
218005b261ecSmrg		}
218105b261ecSmrg	} else {
218205b261ecSmrg		while (angle < 0) {
218305b261ecSmrg			angle += 90 * 64;
218405b261ecSmrg			totallen -= sidelen;
218505b261ecSmrg			oddSide = !oddSide;
218605b261ecSmrg		}
218705b261ecSmrg	}
218805b261ecSmrg	if (oddSide)
218905b261ecSmrg		angle = 90 * 64 - angle;
219005b261ecSmrg
219105b261ecSmrg	di = xAngleToDashIndex (angle);
219205b261ecSmrg	excess = angle - dashIndexToXAngle (di);
219305b261ecSmrg
219405b261ecSmrg	len = map->map[di];
219505b261ecSmrg	/*
219605b261ecSmrg	 * linearly interpolate between this point and the next
219705b261ecSmrg	 */
219805b261ecSmrg	if (excess > 0) {
219905b261ecSmrg		excesslen = (map->map[di + 1] - map->map[di]) *
220005b261ecSmrg				((double) excess) / dashXAngleStep;
220105b261ecSmrg		len += excesslen;
220205b261ecSmrg	}
220305b261ecSmrg	if (oddSide)
220405b261ecSmrg		totallen += (sidelen - len);
220505b261ecSmrg	else
220605b261ecSmrg		totallen += len;
220705b261ecSmrg	return totallen;
220805b261ecSmrg}
220905b261ecSmrg
221005b261ecSmrg/*
221105b261ecSmrg * len is along the arc, but may be more than one rotation
221205b261ecSmrg */
221305b261ecSmrg
221405b261ecSmrgstatic int
221505b261ecSmrglengthToAngle (
221605b261ecSmrg	double	len,
221705b261ecSmrg	dashMap	*map)
221805b261ecSmrg{
221905b261ecSmrg	double	sidelen = map->map[DASH_MAP_SIZE - 1];
222005b261ecSmrg	int	angle, angleexcess;
222105b261ecSmrg	Bool	oddSide = FALSE;
222205b261ecSmrg	int	a0, a1, a;
222305b261ecSmrg
222405b261ecSmrg	angle = 0;
222505b261ecSmrg	/*
222605b261ecSmrg	 * step around the ellipse, subtracting sidelens and
222705b261ecSmrg	 * adding 90 degrees.  oddSide will tell if the
222805b261ecSmrg	 * map should be interpolated in reverse
222905b261ecSmrg	 */
223005b261ecSmrg	if (len >= 0) {
223105b261ecSmrg		if (sidelen == 0)
223205b261ecSmrg			return 2 * FULLCIRCLE;	/* infinity */
223305b261ecSmrg		while (len >= sidelen) {
223405b261ecSmrg			angle += 90 * 64;
223505b261ecSmrg			len -= sidelen;
223605b261ecSmrg			oddSide = !oddSide;
223705b261ecSmrg		}
223805b261ecSmrg	} else {
223905b261ecSmrg		if (sidelen == 0)
224005b261ecSmrg			return -2 * FULLCIRCLE;	/* infinity */
224105b261ecSmrg		while (len < 0) {
224205b261ecSmrg			angle -= 90 * 64;
224305b261ecSmrg			len += sidelen;
224405b261ecSmrg			oddSide = !oddSide;
224505b261ecSmrg		}
224605b261ecSmrg	}
224705b261ecSmrg	if (oddSide)
224805b261ecSmrg		len = sidelen - len;
224905b261ecSmrg	a0 = 0;
225005b261ecSmrg	a1 = DASH_MAP_SIZE - 1;
225105b261ecSmrg	/*
225205b261ecSmrg	 * binary search for the closest pre-computed length
225305b261ecSmrg	 */
225405b261ecSmrg	while (a1 - a0 > 1) {
225505b261ecSmrg		a = (a0 + a1) / 2;
225605b261ecSmrg		if (len > map->map[a])
225705b261ecSmrg			a0 = a;
225805b261ecSmrg		else
225905b261ecSmrg			a1 = a;
226005b261ecSmrg	}
226105b261ecSmrg	angleexcess = dashIndexToXAngle (a0);
226205b261ecSmrg	/*
226305b261ecSmrg	 * linearly interpolate to the next point
226405b261ecSmrg	 */
226505b261ecSmrg	angleexcess += (len - map->map[a0]) /
226605b261ecSmrg			(map->map[a0+1] - map->map[a0]) * dashXAngleStep;
226705b261ecSmrg	if (oddSide)
226805b261ecSmrg		angle += (90 * 64) - angleexcess;
226905b261ecSmrg	else
227005b261ecSmrg		angle += angleexcess;
227105b261ecSmrg	return angle;
227205b261ecSmrg}
227305b261ecSmrg
227405b261ecSmrg/*
227505b261ecSmrg * compute the angle of an ellipse which cooresponds to
227605b261ecSmrg * the given path length.  Note that the correct solution
227705b261ecSmrg * to this problem is an eliptic integral, we'll punt and
227805b261ecSmrg * approximate (it's only for dashes anyway).  This
227905b261ecSmrg * approximation uses a polygon.
228005b261ecSmrg *
228105b261ecSmrg * The remaining portion of len is stored in *lenp -
228205b261ecSmrg * this will be negative if the arc extends beyond
228305b261ecSmrg * len and positive if len extends beyond the arc.
228405b261ecSmrg */
228505b261ecSmrg
228605b261ecSmrgstatic int
228705b261ecSmrgcomputeAngleFromPath (
228805b261ecSmrg	int	startAngle,
228905b261ecSmrg	int	endAngle,	/* normalized absolute angles in *64 degrees */
229005b261ecSmrg	dashMap	*map,
229105b261ecSmrg	int	*lenp,
229205b261ecSmrg	int	backwards)
229305b261ecSmrg{
229405b261ecSmrg	int	a0, a1, a;
229505b261ecSmrg	double	len0;
229605b261ecSmrg	int	len;
229705b261ecSmrg
229805b261ecSmrg	a0 = startAngle;
229905b261ecSmrg	a1 = endAngle;
230005b261ecSmrg	len = *lenp;
230105b261ecSmrg	if (backwards) {
230205b261ecSmrg		/*
230305b261ecSmrg		 * flip the problem around to always be
230405b261ecSmrg		 * forwards
230505b261ecSmrg		 */
230605b261ecSmrg		a0 = FULLCIRCLE - a0;
230705b261ecSmrg		a1 = FULLCIRCLE - a1;
230805b261ecSmrg	}
230905b261ecSmrg	if (a1 < a0)
231005b261ecSmrg		a1 += FULLCIRCLE;
231105b261ecSmrg	len0 = angleToLength (a0, map);
231205b261ecSmrg	a = lengthToAngle (len0 + len, map);
231305b261ecSmrg	if (a > a1) {
231405b261ecSmrg		a = a1;
231505b261ecSmrg		len -= angleToLength (a1, map) - len0;
231605b261ecSmrg	} else
231705b261ecSmrg		len = 0;
231805b261ecSmrg	if (backwards)
231905b261ecSmrg		a = FULLCIRCLE - a;
232005b261ecSmrg	*lenp = len;
232105b261ecSmrg	return a;
232205b261ecSmrg}
232305b261ecSmrg
232405b261ecSmrg/*
232505b261ecSmrg * scan convert wide arcs.
232605b261ecSmrg */
232705b261ecSmrg
232805b261ecSmrg/*
232905b261ecSmrg * draw zero width/height arcs
233005b261ecSmrg */
233105b261ecSmrg
233205b261ecSmrgstatic void
233305b261ecSmrgdrawZeroArc (
233405b261ecSmrg    DrawablePtr   pDraw,
233505b261ecSmrg    GCPtr         pGC,
233605b261ecSmrg    xArc          *tarc,
233705b261ecSmrg    int		  lw,
233805b261ecSmrg    miArcFacePtr	left,
233905b261ecSmrg    miArcFacePtr	right)
234005b261ecSmrg{
234105b261ecSmrg	double	x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
234205b261ecSmrg	double	xmax, ymax, xmin, ymin;
234305b261ecSmrg	int	a0, a1;
234405b261ecSmrg	double	a, startAngle, endAngle;
234505b261ecSmrg	double	l, lx, ly;
234605b261ecSmrg
234705b261ecSmrg	l = lw / 2.0;
234805b261ecSmrg	a0 = tarc->angle1;
234905b261ecSmrg	a1 = tarc->angle2;
235005b261ecSmrg	if (a1 > FULLCIRCLE)
235105b261ecSmrg		a1 = FULLCIRCLE;
235205b261ecSmrg	else if (a1 < -FULLCIRCLE)
235305b261ecSmrg		a1 = -FULLCIRCLE;
235405b261ecSmrg	w = (double)tarc->width / 2.0;
235505b261ecSmrg	h = (double)tarc->height / 2.0;
235605b261ecSmrg	/*
235705b261ecSmrg	 * play in X coordinates right away
235805b261ecSmrg	 */
235905b261ecSmrg	startAngle = - ((double) a0 / 64.0);
236005b261ecSmrg	endAngle = - ((double) (a0 + a1) / 64.0);
236105b261ecSmrg
236205b261ecSmrg	xmax = -w;
236305b261ecSmrg	xmin = w;
236405b261ecSmrg	ymax = -h;
236505b261ecSmrg	ymin = h;
236605b261ecSmrg	a = startAngle;
236705b261ecSmrg	for (;;)
236805b261ecSmrg	{
236905b261ecSmrg		x = w * miDcos(a);
237005b261ecSmrg		y = h * miDsin(a);
237105b261ecSmrg		if (a == startAngle)
237205b261ecSmrg		{
237305b261ecSmrg			x0 = x;
237405b261ecSmrg			y0 = y;
237505b261ecSmrg		}
237605b261ecSmrg		if (a == endAngle)
237705b261ecSmrg		{
237805b261ecSmrg			x1 = x;
237905b261ecSmrg			y1 = y;
238005b261ecSmrg		}
238105b261ecSmrg		if (x > xmax)
238205b261ecSmrg			xmax = x;
238305b261ecSmrg		if (x < xmin)
238405b261ecSmrg			xmin = x;
238505b261ecSmrg		if (y > ymax)
238605b261ecSmrg			ymax = y;
238705b261ecSmrg		if (y < ymin)
238805b261ecSmrg			ymin = y;
238905b261ecSmrg		if (a == endAngle)
239005b261ecSmrg			break;
239105b261ecSmrg		if (a1 < 0)	/* clockwise */
239205b261ecSmrg		{
239305b261ecSmrg			if (floor (a / 90.0) == floor (endAngle / 90.0))
239405b261ecSmrg				a = endAngle;
239505b261ecSmrg			else
239605b261ecSmrg				a = 90 * (floor (a/90.0) + 1);
239705b261ecSmrg		}
239805b261ecSmrg		else
239905b261ecSmrg		{
240005b261ecSmrg			if (ceil (a / 90.0) == ceil (endAngle / 90.0))
240105b261ecSmrg				a = endAngle;
240205b261ecSmrg			else
240305b261ecSmrg				a = 90 * (ceil (a/90.0) - 1);
240405b261ecSmrg		}
240505b261ecSmrg	}
240605b261ecSmrg	lx = ly = l;
240705b261ecSmrg	if ((x1 - x0) + (y1 - y0) < 0)
240805b261ecSmrg	    lx = ly = -l;
240905b261ecSmrg	if (h)
241005b261ecSmrg	{
241105b261ecSmrg	    ly = 0.0;
241205b261ecSmrg	    lx = -lx;
241305b261ecSmrg	}
241405b261ecSmrg	else
241505b261ecSmrg	    lx = 0.0;
241605b261ecSmrg	if (right)
241705b261ecSmrg	{
241805b261ecSmrg	    right->center.x = x0;
241905b261ecSmrg	    right->center.y = y0;
242005b261ecSmrg	    right->clock.x = x0 - lx;
242105b261ecSmrg	    right->clock.y = y0 - ly;
242205b261ecSmrg	    right->counterClock.x = x0 + lx;
242305b261ecSmrg	    right->counterClock.y = y0 + ly;
242405b261ecSmrg	}
242505b261ecSmrg	if (left)
242605b261ecSmrg 	{
242705b261ecSmrg	    left->center.x = x1;
242805b261ecSmrg	    left->center.y = y1;
242905b261ecSmrg	    left->clock.x = x1 + lx;
243005b261ecSmrg	    left->clock.y = y1 + ly;
243105b261ecSmrg	    left->counterClock.x = x1 - lx;
243205b261ecSmrg	    left->counterClock.y = y1 - ly;
243305b261ecSmrg	}
243405b261ecSmrg
243505b261ecSmrg	x0 = xmin;
243605b261ecSmrg	x1 = xmax;
243705b261ecSmrg	y0 = ymin;
243805b261ecSmrg	y1 = ymax;
243905b261ecSmrg	if (ymin != y1) {
244005b261ecSmrg		xmin = -l;
244105b261ecSmrg		xmax = l;
244205b261ecSmrg	} else {
244305b261ecSmrg		ymin = -l;
244405b261ecSmrg		ymax = l;
244505b261ecSmrg	}
244605b261ecSmrg	if (xmax != xmin && ymax != ymin) {
244705b261ecSmrg		int	minx, maxx, miny, maxy;
244805b261ecSmrg		xRectangle  rect;
244905b261ecSmrg
245005b261ecSmrg		minx = ICEIL (xmin + w) + tarc->x;
245105b261ecSmrg		maxx = ICEIL (xmax + w) + tarc->x;
245205b261ecSmrg		miny = ICEIL (ymin + h) + tarc->y;
245305b261ecSmrg		maxy = ICEIL (ymax + h) + tarc->y;
245405b261ecSmrg		rect.x = minx;
245505b261ecSmrg		rect.y = miny;
245605b261ecSmrg		rect.width = maxx - minx;
245705b261ecSmrg		rect.height = maxy - miny;
245805b261ecSmrg		(*pGC->ops->PolyFillRect) (pDraw, pGC, 1, &rect);
245905b261ecSmrg	}
246005b261ecSmrg}
246105b261ecSmrg
246205b261ecSmrg/*
246305b261ecSmrg * this computes the ellipse y value associated with the
246405b261ecSmrg * bottom of the tail.
246505b261ecSmrg */
246605b261ecSmrg
246705b261ecSmrgstatic void
246805b261ecSmrgtailEllipseY (
246905b261ecSmrg	struct arc_def		*def,
247005b261ecSmrg	struct accelerators	*acc)
247105b261ecSmrg{
247205b261ecSmrg	double		t;
247305b261ecSmrg
247405b261ecSmrg	acc->tail_y = 0.0;
247505b261ecSmrg	if (def->w == def->h)
247605b261ecSmrg	    return;
247705b261ecSmrg	t = def->l * def->w;
247805b261ecSmrg	if (def->w > def->h) {
247905b261ecSmrg	    if (t < acc->h2)
248005b261ecSmrg		return;
248105b261ecSmrg	} else {
248205b261ecSmrg	    if (t > acc->h2)
248305b261ecSmrg		return;
248405b261ecSmrg	}
248505b261ecSmrg	t = 2.0 * def->h * t;
248605b261ecSmrg	t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
248705b261ecSmrg	if (t > 0.0)
248805b261ecSmrg	    acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
248905b261ecSmrg}
249005b261ecSmrg
249105b261ecSmrg/*
249205b261ecSmrg * inverse functions -- compute edge coordinates
249305b261ecSmrg * from the ellipse
249405b261ecSmrg */
249505b261ecSmrg
249605b261ecSmrgstatic double
249705b261ecSmrgouterXfromXY (
249805b261ecSmrg	double			x,
249905b261ecSmrg	double			y,
250005b261ecSmrg	struct arc_def		*def,
250105b261ecSmrg	struct accelerators	*acc)
250205b261ecSmrg{
250305b261ecSmrg	return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
250405b261ecSmrg}
250505b261ecSmrg
250605b261ecSmrgstatic double
250705b261ecSmrgouterYfromXY (
250805b261ecSmrg	double		x,
250905b261ecSmrg	double		y,
251005b261ecSmrg	struct arc_def		*def,
251105b261ecSmrg	struct accelerators	*acc)
251205b261ecSmrg{
251305b261ecSmrg	return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
251405b261ecSmrg}
251505b261ecSmrg
251605b261ecSmrgstatic double
251705b261ecSmrginnerXfromXY (
251805b261ecSmrg	double			x,
251905b261ecSmrg	double			y,
252005b261ecSmrg	struct arc_def		*def,
252105b261ecSmrg	struct accelerators	*acc)
252205b261ecSmrg{
252305b261ecSmrg	return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
252405b261ecSmrg}
252505b261ecSmrg
252605b261ecSmrgstatic double
252705b261ecSmrginnerYfromXY (
252805b261ecSmrg	double			x,
252905b261ecSmrg	double			y,
253005b261ecSmrg	struct arc_def		*def,
253105b261ecSmrg	struct accelerators	*acc)
253205b261ecSmrg{
253305b261ecSmrg	return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
253405b261ecSmrg}
253505b261ecSmrg
253605b261ecSmrgstatic double
253705b261ecSmrginnerYfromY (
253805b261ecSmrg	double	y,
253905b261ecSmrg	struct arc_def		*def,
254005b261ecSmrg	struct accelerators	*acc)
254105b261ecSmrg{
254205b261ecSmrg	double	x;
254305b261ecSmrg
254405b261ecSmrg	x = (def->w / def->h) * sqrt (acc->h2 - y*y);
254505b261ecSmrg
254605b261ecSmrg	return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
254705b261ecSmrg}
254805b261ecSmrg
254905b261ecSmrgstatic void
255005b261ecSmrgcomputeLine (
255105b261ecSmrg	double		x1,
255205b261ecSmrg	double		y1,
255305b261ecSmrg	double		x2,
255405b261ecSmrg	double		y2,
255505b261ecSmrg	struct line	*line)
255605b261ecSmrg{
255705b261ecSmrg	if (y1 == y2)
255805b261ecSmrg		line->valid = 0;
255905b261ecSmrg	else {
256005b261ecSmrg		line->m = (x1 - x2) / (y1 - y2);
256105b261ecSmrg		line->b = x1  - y1 * line->m;
256205b261ecSmrg		line->valid = 1;
256305b261ecSmrg	}
256405b261ecSmrg}
256505b261ecSmrg
256605b261ecSmrg/*
256705b261ecSmrg * compute various accelerators for an ellipse.  These
256805b261ecSmrg * are simply values that are used repeatedly in
256905b261ecSmrg * the computations
257005b261ecSmrg */
257105b261ecSmrg
257205b261ecSmrgstatic void
257305b261ecSmrgcomputeAcc (
257405b261ecSmrg	xArc			*tarc,
257505b261ecSmrg	int			lw,
257605b261ecSmrg	struct arc_def		*def,
257705b261ecSmrg	struct accelerators	*acc)
257805b261ecSmrg{
257905b261ecSmrg	def->w = ((double) tarc->width) / 2.0;
258005b261ecSmrg	def->h = ((double) tarc->height) / 2.0;
258105b261ecSmrg	def->l = ((double) lw) / 2.0;
258205b261ecSmrg	acc->h2 = def->h * def->h;
258305b261ecSmrg	acc->w2 = def->w * def->w;
258405b261ecSmrg	acc->h4 = acc->h2 * acc->h2;
258505b261ecSmrg	acc->w4 = acc->w2 * acc->w2;
258605b261ecSmrg	acc->h2l = acc->h2 * def->l;
258705b261ecSmrg	acc->w2l = acc->w2 * def->l;
258805b261ecSmrg	acc->h2mw2 = acc->h2 - acc->w2;
258905b261ecSmrg	acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
259005b261ecSmrg	acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
259105b261ecSmrg	acc->xorg = tarc->x + (tarc->width >> 1);
259205b261ecSmrg	acc->yorgu = tarc->y + (tarc->height >> 1);
259305b261ecSmrg	acc->yorgl = acc->yorgu + (tarc->height & 1);
259405b261ecSmrg	tailEllipseY (def, acc);
259505b261ecSmrg}
259605b261ecSmrg
259705b261ecSmrg/*
259805b261ecSmrg * compute y value bounds of various portions of the arc,
259905b261ecSmrg * the outer edge, the ellipse and the inner edge.
260005b261ecSmrg */
260105b261ecSmrg
260205b261ecSmrgstatic void
260305b261ecSmrgcomputeBound (
260405b261ecSmrg	struct arc_def		*def,
260505b261ecSmrg	struct arc_bound	*bound,
260605b261ecSmrg	struct accelerators	*acc,
260705b261ecSmrg	miArcFacePtr		right,
260805b261ecSmrg	miArcFacePtr		left)
260905b261ecSmrg{
261005b261ecSmrg	double		t;
261105b261ecSmrg	double		innerTaily;
261205b261ecSmrg	double		tail_y;
261305b261ecSmrg	struct bound	innerx, outerx;
261405b261ecSmrg	struct bound	ellipsex;
261505b261ecSmrg
261605b261ecSmrg	bound->ellipse.min = Dsin (def->a0) * def->h;
261705b261ecSmrg	bound->ellipse.max = Dsin (def->a1) * def->h;
261805b261ecSmrg	if (def->a0 == 45 && def->w == def->h)
261905b261ecSmrg		ellipsex.min = bound->ellipse.min;
262005b261ecSmrg	else
262105b261ecSmrg		ellipsex.min = Dcos (def->a0) * def->w;
262205b261ecSmrg	if (def->a1 == 45 && def->w == def->h)
262305b261ecSmrg		ellipsex.max = bound->ellipse.max;
262405b261ecSmrg	else
262505b261ecSmrg		ellipsex.max = Dcos (def->a1) * def->w;
262605b261ecSmrg	bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
262705b261ecSmrg	bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
262805b261ecSmrg	bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
262905b261ecSmrg	bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
263005b261ecSmrg
263105b261ecSmrg	outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
263205b261ecSmrg	outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
263305b261ecSmrg	innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
263405b261ecSmrg	innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
263505b261ecSmrg
263605b261ecSmrg	/*
263705b261ecSmrg	 * save the line end points for the
263805b261ecSmrg	 * cap code to use.  Careful here, these are
263905b261ecSmrg	 * in cartesean coordinates (y increasing upwards)
264005b261ecSmrg	 * while the cap code uses inverted coordinates
264105b261ecSmrg	 * (y increasing downwards)
264205b261ecSmrg	 */
264305b261ecSmrg
264405b261ecSmrg	if (right) {
264505b261ecSmrg		right->counterClock.y = bound->outer.min;
264605b261ecSmrg		right->counterClock.x = outerx.min;
264705b261ecSmrg		right->center.y = bound->ellipse.min;
264805b261ecSmrg		right->center.x = ellipsex.min;
264905b261ecSmrg		right->clock.y = bound->inner.min;
265005b261ecSmrg		right->clock.x = innerx.min;
265105b261ecSmrg	}
265205b261ecSmrg
265305b261ecSmrg	if (left) {
265405b261ecSmrg		left->clock.y = bound->outer.max;
265505b261ecSmrg		left->clock.x = outerx.max;
265605b261ecSmrg		left->center.y = bound->ellipse.max;
265705b261ecSmrg		left->center.x = ellipsex.max;
265805b261ecSmrg		left->counterClock.y = bound->inner.max;
265905b261ecSmrg		left->counterClock.x = innerx.max;
266005b261ecSmrg	}
266105b261ecSmrg
266205b261ecSmrg	bound->left.min = bound->inner.max;
266305b261ecSmrg	bound->left.max = bound->outer.max;
266405b261ecSmrg	bound->right.min = bound->inner.min;
266505b261ecSmrg	bound->right.max = bound->outer.min;
266605b261ecSmrg
266705b261ecSmrg	computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
266805b261ecSmrg		      &acc->right);
266905b261ecSmrg	computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
267005b261ecSmrg		     &acc->left);
267105b261ecSmrg
267205b261ecSmrg	if (bound->inner.min > bound->inner.max) {
267305b261ecSmrg		t = bound->inner.min;
267405b261ecSmrg		bound->inner.min = bound->inner.max;
267505b261ecSmrg		bound->inner.max = t;
267605b261ecSmrg	}
267705b261ecSmrg	tail_y = acc->tail_y;
267805b261ecSmrg	if (tail_y > bound->ellipse.max)
267905b261ecSmrg		tail_y = bound->ellipse.max;
268005b261ecSmrg	else if (tail_y < bound->ellipse.min)
268105b261ecSmrg		tail_y = bound->ellipse.min;
268205b261ecSmrg	innerTaily = innerYfromY (tail_y, def, acc);
268305b261ecSmrg	if (bound->inner.min > innerTaily)
268405b261ecSmrg		bound->inner.min = innerTaily;
268505b261ecSmrg	if (bound->inner.max < innerTaily)
268605b261ecSmrg		bound->inner.max = innerTaily;
268705b261ecSmrg	bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
268805b261ecSmrg	bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
268905b261ecSmrg	bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
269005b261ecSmrg	bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
269105b261ecSmrg}
269205b261ecSmrg
269305b261ecSmrg/*
269405b261ecSmrg * this section computes the x value of the span at y
269505b261ecSmrg * intersected with the specified face of the ellipse.
269605b261ecSmrg *
269705b261ecSmrg * this is the min/max X value over the set of normal
269805b261ecSmrg * lines to the entire ellipse,  the equation of the
269905b261ecSmrg * normal lines is:
270005b261ecSmrg *
270105b261ecSmrg *     ellipse_x h^2                   h^2
270205b261ecSmrg * x = ------------ y + ellipse_x (1 - --- )
270305b261ecSmrg *     ellipse_y w^2                   w^2
270405b261ecSmrg *
270505b261ecSmrg * compute the derivative with-respect-to ellipse_y and solve
270605b261ecSmrg * for zero:
270705b261ecSmrg *
270805b261ecSmrg *       (w^2 - h^2) ellipse_y^3 + h^4 y
270905b261ecSmrg * 0 = - ----------------------------------
271005b261ecSmrg *       h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
271105b261ecSmrg *
271205b261ecSmrg *             (   h^4 y     )
271305b261ecSmrg * ellipse_y = ( ----------  ) ^ (1/3)
271405b261ecSmrg *             ( (h^2 - w^2) )
271505b261ecSmrg *
271605b261ecSmrg * The other two solutions to the equation are imaginary.
271705b261ecSmrg *
271805b261ecSmrg * This gives the position on the ellipse which generates
271905b261ecSmrg * the normal with the largest/smallest x intersection point.
272005b261ecSmrg *
272105b261ecSmrg * Now compute the second derivative to check whether
272205b261ecSmrg * the intersection is a minimum or maximum:
272305b261ecSmrg *
272405b261ecSmrg *    h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
272505b261ecSmrg * -  -------------------------------------------
272605b261ecSmrg *          w y0^3 (sqrt (h^2 - y^2)) ^ 3
272705b261ecSmrg *
272805b261ecSmrg * as we only care about the sign,
272905b261ecSmrg *
273005b261ecSmrg * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
273105b261ecSmrg *
273205b261ecSmrg * or (to use accelerators),
273305b261ecSmrg *
273405b261ecSmrg * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
273505b261ecSmrg *
273605b261ecSmrg */
273705b261ecSmrg
273805b261ecSmrg/*
273905b261ecSmrg * computes the position on the ellipse whose normal line
274005b261ecSmrg * intersects the given scan line maximally
274105b261ecSmrg */
274205b261ecSmrg
274305b261ecSmrgstatic double
274405b261ecSmrghookEllipseY (
274505b261ecSmrg	double			scan_y,
274605b261ecSmrg	struct arc_bound	*bound,
274705b261ecSmrg	struct accelerators	*acc,
274805b261ecSmrg	int			left)
274905b261ecSmrg{
275005b261ecSmrg	double	ret;
275105b261ecSmrg
275205b261ecSmrg	if (acc->h2mw2 == 0) {
275305b261ecSmrg		if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
275405b261ecSmrg			return bound->ellipse.min;
275505b261ecSmrg		return bound->ellipse.max;
275605b261ecSmrg	}
275705b261ecSmrg	ret = (acc->h4 * scan_y) / (acc->h2mw2);
275805b261ecSmrg	if (ret >= 0)
275905b261ecSmrg		return cbrt (ret);
276005b261ecSmrg	else
276105b261ecSmrg		return -cbrt (-ret);
276205b261ecSmrg}
276305b261ecSmrg
276405b261ecSmrg/*
276505b261ecSmrg * computes the X value of the intersection of the
276605b261ecSmrg * given scan line with the right side of the lower hook
276705b261ecSmrg */
276805b261ecSmrg
276905b261ecSmrgstatic double
277005b261ecSmrghookX (
277105b261ecSmrg	double			scan_y,
277205b261ecSmrg	struct arc_def		*def,
277305b261ecSmrg	struct arc_bound	*bound,
277405b261ecSmrg	struct accelerators	*acc,
277505b261ecSmrg	int			left)
277605b261ecSmrg{
277705b261ecSmrg	double	ellipse_y, x;
277805b261ecSmrg	double	maxMin;
277905b261ecSmrg
278005b261ecSmrg	if (def->w != def->h) {
278105b261ecSmrg		ellipse_y = hookEllipseY (scan_y, bound, acc, left);
278205b261ecSmrg		if (boundedLe (ellipse_y, bound->ellipse)) {
278305b261ecSmrg			/*
278405b261ecSmrg		 	 * compute the value of the second
278505b261ecSmrg		 	 * derivative
278605b261ecSmrg		 	 */
278705b261ecSmrg			maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
278805b261ecSmrg		 	 acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
278905b261ecSmrg			if ((left && maxMin > 0) || (!left && maxMin < 0)) {
279005b261ecSmrg				if (ellipse_y == 0)
279105b261ecSmrg					return def->w + left ? -def->l : def->l;
279205b261ecSmrg				x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
279305b261ecSmrg					sqrt (acc->h2 - ellipse_y * ellipse_y) /
279405b261ecSmrg			 		(def->h * def->w * ellipse_y);
279505b261ecSmrg				return x;
279605b261ecSmrg			}
279705b261ecSmrg		}
279805b261ecSmrg	}
279905b261ecSmrg	if (left) {
280005b261ecSmrg		if (acc->left.valid && boundedLe (scan_y, bound->left)) {
280105b261ecSmrg			x = intersectLine (scan_y, acc->left);
280205b261ecSmrg		} else {
280305b261ecSmrg			if (acc->right.valid)
280405b261ecSmrg				x = intersectLine (scan_y, acc->right);
280505b261ecSmrg			else
280605b261ecSmrg				x = def->w - def->l;
280705b261ecSmrg		}
280805b261ecSmrg	} else {
280905b261ecSmrg		if (acc->right.valid && boundedLe (scan_y, bound->right)) {
281005b261ecSmrg			x = intersectLine (scan_y, acc->right);
281105b261ecSmrg		} else {
281205b261ecSmrg			if (acc->left.valid)
281305b261ecSmrg				x = intersectLine (scan_y, acc->left);
281405b261ecSmrg			else
281505b261ecSmrg				x = def->w - def->l;
281605b261ecSmrg		}
281705b261ecSmrg	}
281805b261ecSmrg	return x;
281905b261ecSmrg}
282005b261ecSmrg
282105b261ecSmrg/*
282205b261ecSmrg * generate the set of spans with
282305b261ecSmrg * the given y coordinate
282405b261ecSmrg */
282505b261ecSmrg
282605b261ecSmrgstatic void
282705b261ecSmrgarcSpan (
282805b261ecSmrg	int			y,
282905b261ecSmrg	int			lx,
283005b261ecSmrg	int			lw,
283105b261ecSmrg	int			rx,
283205b261ecSmrg	int			rw,
283305b261ecSmrg	struct arc_def		*def,
283405b261ecSmrg	struct arc_bound	*bounds,
283505b261ecSmrg	struct accelerators	*acc,
283605b261ecSmrg	int			mask)
283705b261ecSmrg{
283805b261ecSmrg	int linx, loutx, rinx, routx;
283905b261ecSmrg	double x, altx;
284005b261ecSmrg
284105b261ecSmrg	if (boundedLe (y, bounds->inneri)) {
284205b261ecSmrg	    linx = -(lx + lw);
284305b261ecSmrg	    rinx = rx;
284405b261ecSmrg	} else {
284505b261ecSmrg	    /*
284605b261ecSmrg	     * intersection with left face
284705b261ecSmrg	     */
284805b261ecSmrg	    x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
284905b261ecSmrg	    if (acc->right.valid &&
285005b261ecSmrg		boundedLe (y + acc->fromIntY, bounds->right))
285105b261ecSmrg	    {
285205b261ecSmrg		altx = intersectLine (y + acc->fromIntY, acc->right);
285305b261ecSmrg		if (altx < x)
285405b261ecSmrg		    x = altx;
285505b261ecSmrg	    }
285605b261ecSmrg	    linx = -ICEIL(acc->fromIntX - x);
285705b261ecSmrg	    rinx = ICEIL(acc->fromIntX + x);
285805b261ecSmrg	}
285905b261ecSmrg	if (boundedLe (y, bounds->outeri)) {
286005b261ecSmrg	    loutx = -lx;
286105b261ecSmrg	    routx = rx + rw;
286205b261ecSmrg	} else {
286305b261ecSmrg	    /*
286405b261ecSmrg	     * intersection with right face
286505b261ecSmrg	     */
286605b261ecSmrg	    x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
286705b261ecSmrg	    if (acc->left.valid &&
286805b261ecSmrg		boundedLe (y + acc->fromIntY, bounds->left))
286905b261ecSmrg	    {
287005b261ecSmrg		altx = x;
287105b261ecSmrg		x = intersectLine (y + acc->fromIntY, acc->left);
287205b261ecSmrg		if (x < altx)
287305b261ecSmrg		    x = altx;
287405b261ecSmrg	    }
287505b261ecSmrg	    loutx = -ICEIL(acc->fromIntX - x);
287605b261ecSmrg	    routx = ICEIL(acc->fromIntX + x);
287705b261ecSmrg	}
287805b261ecSmrg	if (routx > rinx) {
287905b261ecSmrg	    if (mask & 1)
288005b261ecSmrg		newFinalSpan (acc->yorgu - y,
288105b261ecSmrg			      acc->xorg + rinx, acc->xorg + routx);
288205b261ecSmrg	    if (mask & 8)
288305b261ecSmrg		newFinalSpan (acc->yorgl + y,
288405b261ecSmrg			      acc->xorg + rinx, acc->xorg + routx);
288505b261ecSmrg	}
288605b261ecSmrg	if (loutx > linx) {
288705b261ecSmrg	    if (mask & 2)
288805b261ecSmrg		newFinalSpan (acc->yorgu - y,
288905b261ecSmrg			      acc->xorg - loutx, acc->xorg - linx);
289005b261ecSmrg	    if (mask & 4)
289105b261ecSmrg		newFinalSpan (acc->yorgl + y,
289205b261ecSmrg			      acc->xorg - loutx, acc->xorg - linx);
289305b261ecSmrg	}
289405b261ecSmrg}
289505b261ecSmrg
289605b261ecSmrgstatic void
289705b261ecSmrgarcSpan0 (
289805b261ecSmrg	int			lx,
289905b261ecSmrg	int			lw,
290005b261ecSmrg	int			rx,
290105b261ecSmrg	int			rw,
290205b261ecSmrg	struct arc_def		*def,
290305b261ecSmrg	struct arc_bound	*bounds,
290405b261ecSmrg	struct accelerators	*acc,
290505b261ecSmrg	int			mask)
290605b261ecSmrg{
290705b261ecSmrg    double x;
290805b261ecSmrg
290905b261ecSmrg    if (boundedLe (0, bounds->inneri) &&
291005b261ecSmrg	acc->left.valid && boundedLe (0, bounds->left) &&
291105b261ecSmrg	acc->left.b > 0)
291205b261ecSmrg    {
291305b261ecSmrg	x = def->w - def->l;
291405b261ecSmrg	if (acc->left.b < x)
291505b261ecSmrg	    x = acc->left.b;
291605b261ecSmrg	lw = ICEIL(acc->fromIntX - x) - lx;
291705b261ecSmrg	rw += rx;
291805b261ecSmrg	rx = ICEIL(acc->fromIntX + x);
291905b261ecSmrg	rw -= rx;
292005b261ecSmrg    }
292105b261ecSmrg    arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
292205b261ecSmrg}
292305b261ecSmrg
292405b261ecSmrgstatic void
292505b261ecSmrgtailSpan (
292605b261ecSmrg	int			y,
292705b261ecSmrg	int			lw,
292805b261ecSmrg	int			rw,
292905b261ecSmrg	struct arc_def		*def,
293005b261ecSmrg	struct arc_bound	*bounds,
293105b261ecSmrg	struct accelerators	*acc,
293205b261ecSmrg	int			mask)
293305b261ecSmrg{
293405b261ecSmrg    double yy, xalt, x, lx, rx;
293505b261ecSmrg    int n;
293605b261ecSmrg
293705b261ecSmrg    if (boundedLe(y, bounds->outeri))
293805b261ecSmrg	arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
293905b261ecSmrg    else if (def->w != def->h) {
294005b261ecSmrg	yy = y + acc->fromIntY;
294105b261ecSmrg	x = tailX(yy, def, bounds, acc);
294205b261ecSmrg	if (yy == 0.0 && x == -rw - acc->fromIntX)
294305b261ecSmrg	    return;
294405b261ecSmrg	if (acc->right.valid && boundedLe (yy, bounds->right)) {
294505b261ecSmrg	    rx = x;
294605b261ecSmrg	    lx = -x;
294705b261ecSmrg	    xalt = intersectLine (yy, acc->right);
294805b261ecSmrg	    if (xalt >= -rw - acc->fromIntX && xalt <= rx)
294905b261ecSmrg		rx = xalt;
295005b261ecSmrg	    n = ICEIL(acc->fromIntX + lx);
295105b261ecSmrg	    if (lw > n) {
295205b261ecSmrg		if (mask & 2)
295305b261ecSmrg		    newFinalSpan (acc->yorgu - y,
295405b261ecSmrg				  acc->xorg + n, acc->xorg + lw);
295505b261ecSmrg		if (mask & 4)
295605b261ecSmrg		    newFinalSpan (acc->yorgl + y,
295705b261ecSmrg				  acc->xorg + n, acc->xorg + lw);
295805b261ecSmrg	    }
295905b261ecSmrg	    n = ICEIL(acc->fromIntX + rx);
296005b261ecSmrg	    if (n > -rw) {
296105b261ecSmrg		if (mask & 1)
296205b261ecSmrg		    newFinalSpan (acc->yorgu - y,
296305b261ecSmrg				  acc->xorg - rw, acc->xorg + n);
296405b261ecSmrg		if (mask & 8)
296505b261ecSmrg		    newFinalSpan (acc->yorgl + y,
296605b261ecSmrg				  acc->xorg - rw, acc->xorg + n);
296705b261ecSmrg	    }
296805b261ecSmrg	}
296905b261ecSmrg	arcSpan (y,
297005b261ecSmrg		 ICEIL(acc->fromIntX - x), 0,
297105b261ecSmrg		 ICEIL(acc->fromIntX + x), 0,
297205b261ecSmrg		 def, bounds, acc, mask);
297305b261ecSmrg    }
297405b261ecSmrg}
297505b261ecSmrg
297605b261ecSmrg/*
297705b261ecSmrg * create whole arcs out of pieces.  This code is
297805b261ecSmrg * very bad.
297905b261ecSmrg */
298005b261ecSmrg
298105b261ecSmrgstatic struct finalSpan	**finalSpans = NULL;
298205b261ecSmrgstatic int		finalMiny = 0, finalMaxy = -1;
298305b261ecSmrgstatic int		finalSize = 0;
298405b261ecSmrg
298505b261ecSmrgstatic int		nspans = 0;	/* total spans, not just y coords */
298605b261ecSmrg
298705b261ecSmrgstruct finalSpan {
298805b261ecSmrg	struct finalSpan	*next;
298905b261ecSmrg	int			min, max;	/* x values */
299005b261ecSmrg};
299105b261ecSmrg
299205b261ecSmrgstatic struct finalSpan    *freeFinalSpans, *tmpFinalSpan;
299305b261ecSmrg
299405b261ecSmrg# define allocFinalSpan()   (freeFinalSpans ?\
299505b261ecSmrg				((tmpFinalSpan = freeFinalSpans), \
299605b261ecSmrg				 (freeFinalSpans = freeFinalSpans->next), \
299705b261ecSmrg				 (tmpFinalSpan->next = 0), \
299805b261ecSmrg				 tmpFinalSpan) : \
299905b261ecSmrg			     realAllocSpan ())
300005b261ecSmrg
300105b261ecSmrg# define SPAN_CHUNK_SIZE    128
300205b261ecSmrg
300305b261ecSmrgstruct finalSpanChunk {
300405b261ecSmrg	struct finalSpan	data[SPAN_CHUNK_SIZE];
300505b261ecSmrg	struct finalSpanChunk	*next;
300605b261ecSmrg};
300705b261ecSmrg
300805b261ecSmrgstatic struct finalSpanChunk	*chunks;
300905b261ecSmrg
301005b261ecSmrgstatic struct finalSpan *
301105b261ecSmrgrealAllocSpan (void)
301205b261ecSmrg{
301305b261ecSmrg	struct finalSpanChunk	*newChunk;
301405b261ecSmrg	struct finalSpan	*span;
301505b261ecSmrg	int			i;
301605b261ecSmrg
30176747b715Smrg	newChunk = malloc(sizeof (struct finalSpanChunk));
301805b261ecSmrg	if (!newChunk)
301905b261ecSmrg		return (struct finalSpan *) NULL;
302005b261ecSmrg	newChunk->next = chunks;
302105b261ecSmrg	chunks = newChunk;
302205b261ecSmrg	freeFinalSpans = span = newChunk->data + 1;
302305b261ecSmrg	for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
302405b261ecSmrg		span->next = span+1;
302505b261ecSmrg		span++;
302605b261ecSmrg	}
302705b261ecSmrg	span->next = 0;
302805b261ecSmrg	span = newChunk->data;
302905b261ecSmrg	span->next = 0;
303005b261ecSmrg	return span;
303105b261ecSmrg}
303205b261ecSmrg
303305b261ecSmrgstatic void
303405b261ecSmrgdisposeFinalSpans (void)
303505b261ecSmrg{
303605b261ecSmrg	struct finalSpanChunk	*chunk, *next;
303705b261ecSmrg
303805b261ecSmrg	for (chunk = chunks; chunk; chunk = next) {
303905b261ecSmrg		next = chunk->next;
30406747b715Smrg		free(chunk);
304105b261ecSmrg	}
304205b261ecSmrg	chunks = 0;
304305b261ecSmrg	freeFinalSpans = 0;
30446747b715Smrg	free(finalSpans);
304505b261ecSmrg	finalSpans = 0;
304605b261ecSmrg}
304705b261ecSmrg
304805b261ecSmrgstatic void
304905b261ecSmrgfillSpans (
305005b261ecSmrg    DrawablePtr	pDrawable,
305105b261ecSmrg    GCPtr	pGC)
305205b261ecSmrg{
305305b261ecSmrg	struct finalSpan	*span;
305405b261ecSmrg	DDXPointPtr		xSpan;
305505b261ecSmrg	int			*xWidth;
305605b261ecSmrg	int			i;
305705b261ecSmrg	struct finalSpan	**f;
305805b261ecSmrg	int			spany;
305905b261ecSmrg	DDXPointPtr		xSpans;
306005b261ecSmrg	int			*xWidths;
306105b261ecSmrg
306205b261ecSmrg	if (nspans == 0)
306305b261ecSmrg		return;
30646747b715Smrg	xSpan = xSpans = malloc(nspans * sizeof (DDXPointRec));
30656747b715Smrg	xWidth = xWidths = malloc(nspans * sizeof (int));
306605b261ecSmrg	if (xSpans && xWidths)
306705b261ecSmrg	{
306805b261ecSmrg	    i = 0;
306905b261ecSmrg	    f = finalSpans;
307005b261ecSmrg	    for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
307105b261ecSmrg		    for (span = *f; span; span=span->next) {
307205b261ecSmrg			    if (span->max <= span->min)
307305b261ecSmrg				    continue;
307405b261ecSmrg			    xSpan->x = span->min;
307505b261ecSmrg			    xSpan->y = spany;
307605b261ecSmrg			    ++xSpan;
307705b261ecSmrg			    *xWidth++ = span->max - span->min;
307805b261ecSmrg			    ++i;
307905b261ecSmrg		    }
308005b261ecSmrg	    }
308105b261ecSmrg	    (*pGC->ops->FillSpans) (pDrawable, pGC, i, xSpans, xWidths, TRUE);
308205b261ecSmrg	}
308305b261ecSmrg	disposeFinalSpans ();
30846747b715Smrg	free(xSpans);
30856747b715Smrg	free(xWidths);
308605b261ecSmrg	finalMiny = 0;
308705b261ecSmrg	finalMaxy = -1;
308805b261ecSmrg	finalSize = 0;
308905b261ecSmrg	nspans = 0;
309005b261ecSmrg}
309105b261ecSmrg
309205b261ecSmrg# define SPAN_REALLOC	100
309305b261ecSmrg
309405b261ecSmrg# define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
309505b261ecSmrg			  &finalSpans[(y) - finalMiny] : \
309605b261ecSmrg			  realFindSpan (y))
309705b261ecSmrg
309805b261ecSmrgstatic struct finalSpan **
309905b261ecSmrgrealFindSpan (int y)
310005b261ecSmrg{
310105b261ecSmrg	struct finalSpan	**newSpans;
310205b261ecSmrg	int			newSize, newMiny, newMaxy;
310305b261ecSmrg	int			change;
310405b261ecSmrg	int			i;
310505b261ecSmrg
310605b261ecSmrg	if (y < finalMiny || y > finalMaxy) {
310705b261ecSmrg		if (!finalSize) {
310805b261ecSmrg			finalMiny = y;
310905b261ecSmrg			finalMaxy = y - 1;
311005b261ecSmrg		}
311105b261ecSmrg		if (y < finalMiny)
311205b261ecSmrg			change = finalMiny - y;
311305b261ecSmrg		else
311405b261ecSmrg			change = y - finalMaxy;
311505b261ecSmrg		if (change >= SPAN_REALLOC)
311605b261ecSmrg			change += SPAN_REALLOC;
311705b261ecSmrg		else
311805b261ecSmrg			change = SPAN_REALLOC;
311905b261ecSmrg		newSize = finalSize + change;
31206747b715Smrg		newSpans = malloc(newSize * sizeof (struct finalSpan *));
312105b261ecSmrg		if (!newSpans)
31226747b715Smrg		    return NULL;
312305b261ecSmrg		newMiny = finalMiny;
312405b261ecSmrg		newMaxy = finalMaxy;
312505b261ecSmrg		if (y < finalMiny)
312605b261ecSmrg			newMiny = finalMiny - change;
312705b261ecSmrg		else
312805b261ecSmrg			newMaxy = finalMaxy + change;
312905b261ecSmrg		if (finalSpans) {
313005b261ecSmrg			memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
313105b261ecSmrg				(char *) finalSpans,
313205b261ecSmrg			       finalSize * sizeof (struct finalSpan *));
31336747b715Smrg			free(finalSpans);
313405b261ecSmrg		}
313505b261ecSmrg		if ((i = finalMiny - newMiny) > 0)
31366747b715Smrg			memset((char *)newSpans, 0, i * sizeof (struct finalSpan *));
313705b261ecSmrg		if ((i = newMaxy - finalMaxy) > 0)
31386747b715Smrg			memset((char *)(newSpans + newSize - i), 0,
313905b261ecSmrg			       i * sizeof (struct finalSpan *));
314005b261ecSmrg		finalSpans = newSpans;
314105b261ecSmrg		finalMaxy = newMaxy;
314205b261ecSmrg		finalMiny = newMiny;
314305b261ecSmrg		finalSize = newSize;
314405b261ecSmrg	}
314505b261ecSmrg	return &finalSpans[y - finalMiny];
314605b261ecSmrg}
314705b261ecSmrg
314805b261ecSmrgstatic void
314905b261ecSmrgnewFinalSpan (
315005b261ecSmrg    int		y,
315105b261ecSmrg    int	xmin,
315205b261ecSmrg    int	xmax)
315305b261ecSmrg{
315405b261ecSmrg	struct finalSpan	*x;
315505b261ecSmrg	struct finalSpan	**f;
315605b261ecSmrg	struct finalSpan	*oldx;
315705b261ecSmrg	struct finalSpan	*prev;
315805b261ecSmrg
315905b261ecSmrg	f = findSpan (y);
316005b261ecSmrg	if (!f)
316105b261ecSmrg	    return;
316205b261ecSmrg	oldx = 0;
316305b261ecSmrg	for (;;) {
316405b261ecSmrg		prev = 0;
316505b261ecSmrg		for (x = *f; x; x=x->next) {
316605b261ecSmrg			if (x == oldx) {
316705b261ecSmrg				prev = x;
316805b261ecSmrg				continue;
316905b261ecSmrg			}
317005b261ecSmrg			if (x->min <= xmax && xmin <= x->max) {
317105b261ecSmrg				if (oldx) {
317205b261ecSmrg					oldx->min = min (x->min, xmin);
317305b261ecSmrg					oldx->max = max (x->max, xmax);
317405b261ecSmrg					if (prev)
317505b261ecSmrg						prev->next = x->next;
317605b261ecSmrg					else
317705b261ecSmrg						*f = x->next;
317805b261ecSmrg					--nspans;
317905b261ecSmrg				} else {
318005b261ecSmrg					x->min = min (x->min, xmin);
318105b261ecSmrg					x->max = max (x->max, xmax);
318205b261ecSmrg					oldx = x;
318305b261ecSmrg				}
318405b261ecSmrg				xmin = oldx->min;
318505b261ecSmrg				xmax = oldx->max;
318605b261ecSmrg				break;
318705b261ecSmrg			}
318805b261ecSmrg			prev = x;
318905b261ecSmrg		}
319005b261ecSmrg		if (!x)
319105b261ecSmrg			break;
319205b261ecSmrg	}
319305b261ecSmrg	if (!oldx) {
319405b261ecSmrg		x = allocFinalSpan ();
319505b261ecSmrg		if (x)
319605b261ecSmrg		{
319705b261ecSmrg		    x->min = xmin;
319805b261ecSmrg		    x->max = xmax;
319905b261ecSmrg		    x->next = *f;
320005b261ecSmrg		    *f = x;
320105b261ecSmrg		    ++nspans;
320205b261ecSmrg		}
320305b261ecSmrg	}
320405b261ecSmrg}
320505b261ecSmrg
320605b261ecSmrgstatic void
320705b261ecSmrgmirrorSppPoint (
320805b261ecSmrg	int		quadrant,
320905b261ecSmrg	SppPointPtr	sppPoint)
321005b261ecSmrg{
321105b261ecSmrg	switch (quadrant) {
321205b261ecSmrg	case 0:
321305b261ecSmrg		break;
321405b261ecSmrg	case 1:
321505b261ecSmrg		sppPoint->x = -sppPoint->x;
321605b261ecSmrg		break;
321705b261ecSmrg	case 2:
321805b261ecSmrg		sppPoint->x = -sppPoint->x;
321905b261ecSmrg		sppPoint->y = -sppPoint->y;
322005b261ecSmrg		break;
322105b261ecSmrg	case 3:
322205b261ecSmrg		sppPoint->y = -sppPoint->y;
322305b261ecSmrg		break;
322405b261ecSmrg	}
322505b261ecSmrg	/*
322605b261ecSmrg	 * and translate to X coordinate system
322705b261ecSmrg	 */
322805b261ecSmrg	sppPoint->y = -sppPoint->y;
322905b261ecSmrg}
323005b261ecSmrg
323105b261ecSmrg/*
323205b261ecSmrg * split an arc into pieces which are scan-converted
323305b261ecSmrg * in the first-quadrant and mirrored into position.
323405b261ecSmrg * This is necessary as the scan-conversion code can
323505b261ecSmrg * only deal with arcs completely contained in the
323605b261ecSmrg * first quadrant.
323705b261ecSmrg */
323805b261ecSmrg
323905b261ecSmrgstatic void
324005b261ecSmrgdrawArc (
324105b261ecSmrg	xArc *tarc,
324205b261ecSmrg	int	l,
324305b261ecSmrg	int	a0,
324405b261ecSmrg	int	a1,
324505b261ecSmrg	miArcFacePtr	right,
324605b261ecSmrg	miArcFacePtr	left)	/* save end line points */
324705b261ecSmrg{
324805b261ecSmrg	struct arc_def		def;
324905b261ecSmrg	struct accelerators	acc;
325005b261ecSmrg	int			startq, endq, curq;
325105b261ecSmrg	int			rightq, leftq = 0, righta = 0, lefta = 0;
325205b261ecSmrg	miArcFacePtr		passRight, passLeft;
325305b261ecSmrg	int			q0 = 0, q1 = 0, mask;
325405b261ecSmrg	struct band {
325505b261ecSmrg		int	a0, a1;
325605b261ecSmrg		int	mask;
325705b261ecSmrg	}	band[5], sweep[20];
325805b261ecSmrg	int			bandno, sweepno;
325905b261ecSmrg	int			i, j;
326005b261ecSmrg	int			flipRight = 0, flipLeft = 0;
326105b261ecSmrg	int			copyEnd = 0;
326205b261ecSmrg	miArcSpanData		*spdata;
326305b261ecSmrg
32646747b715Smrg	spdata = miComputeWideEllipse(l, tarc);
326505b261ecSmrg	if (!spdata)
326605b261ecSmrg	    return;
326705b261ecSmrg
326805b261ecSmrg	if (a1 < a0)
326905b261ecSmrg		a1 += 360 * 64;
327005b261ecSmrg	startq = a0 / (90 * 64);
327105b261ecSmrg	if (a0 == a1)
327205b261ecSmrg	    endq = startq;
327305b261ecSmrg	else
327405b261ecSmrg	    endq = (a1-1) / (90 * 64);
327505b261ecSmrg	bandno = 0;
327605b261ecSmrg	curq = startq;
327705b261ecSmrg	rightq = -1;
327805b261ecSmrg	for (;;) {
327905b261ecSmrg		switch (curq) {
328005b261ecSmrg		case 0:
328105b261ecSmrg			if (a0 > 90 * 64)
328205b261ecSmrg				q0 = 0;
328305b261ecSmrg			else
328405b261ecSmrg				q0 = a0;
328505b261ecSmrg			if (a1 < 360 * 64)
328605b261ecSmrg				q1 = min (a1, 90 * 64);
328705b261ecSmrg			else
328805b261ecSmrg				q1 = 90 * 64;
328905b261ecSmrg			if (curq == startq && a0 == q0 && rightq < 0) {
329005b261ecSmrg				righta = q0;
329105b261ecSmrg				rightq = curq;
329205b261ecSmrg			}
329305b261ecSmrg			if (curq == endq && a1 == q1) {
329405b261ecSmrg				lefta = q1;
329505b261ecSmrg				leftq = curq;
329605b261ecSmrg			}
329705b261ecSmrg			break;
329805b261ecSmrg		case 1:
329905b261ecSmrg			if (a1 < 90 * 64)
330005b261ecSmrg				q0 = 0;
330105b261ecSmrg			else
330205b261ecSmrg				q0 = 180 * 64 - min (a1, 180 * 64);
330305b261ecSmrg			if (a0 > 180 * 64)
330405b261ecSmrg				q1 = 90 * 64;
330505b261ecSmrg			else
330605b261ecSmrg				q1 = 180 * 64 - max (a0, 90 * 64);
330705b261ecSmrg			if (curq == startq && 180 * 64 - a0 == q1) {
330805b261ecSmrg				righta = q1;
330905b261ecSmrg				rightq = curq;
331005b261ecSmrg			}
331105b261ecSmrg			if (curq == endq && 180 * 64 - a1 == q0) {
331205b261ecSmrg				lefta = q0;
331305b261ecSmrg				leftq = curq;
331405b261ecSmrg			}
331505b261ecSmrg			break;
331605b261ecSmrg		case 2:
331705b261ecSmrg			if (a0 > 270 * 64)
331805b261ecSmrg				q0 = 0;
331905b261ecSmrg			else
332005b261ecSmrg				q0 = max (a0, 180 * 64) - 180 * 64;
332105b261ecSmrg			if (a1 < 180 * 64)
332205b261ecSmrg				q1 = 90 * 64;
332305b261ecSmrg			else
332405b261ecSmrg				q1 = min (a1, 270 * 64) - 180 * 64;
332505b261ecSmrg			if (curq == startq && a0 - 180*64 == q0) {
332605b261ecSmrg				righta = q0;
332705b261ecSmrg				rightq = curq;
332805b261ecSmrg			}
332905b261ecSmrg			if (curq == endq && a1 - 180 * 64 == q1) {
333005b261ecSmrg				lefta = q1;
333105b261ecSmrg				leftq = curq;
333205b261ecSmrg			}
333305b261ecSmrg			break;
333405b261ecSmrg		case 3:
333505b261ecSmrg			if (a1 < 270 * 64)
333605b261ecSmrg				q0 = 0;
333705b261ecSmrg			else
333805b261ecSmrg				q0 = 360 * 64 - min (a1, 360 * 64);
333905b261ecSmrg			q1 = 360 * 64 - max (a0, 270 * 64);
334005b261ecSmrg			if (curq == startq && 360 * 64 - a0 == q1) {
334105b261ecSmrg				righta = q1;
334205b261ecSmrg				rightq = curq;
334305b261ecSmrg			}
334405b261ecSmrg			if (curq == endq && 360 * 64 - a1 == q0) {
334505b261ecSmrg				lefta = q0;
334605b261ecSmrg				leftq = curq;
334705b261ecSmrg			}
334805b261ecSmrg			break;
334905b261ecSmrg		}
335005b261ecSmrg		band[bandno].a0 = q0;
335105b261ecSmrg		band[bandno].a1 = q1;
335205b261ecSmrg		band[bandno].mask = 1 << curq;
335305b261ecSmrg		bandno++;
335405b261ecSmrg		if (curq == endq)
335505b261ecSmrg			break;
335605b261ecSmrg		curq++;
335705b261ecSmrg		if (curq == 4) {
335805b261ecSmrg			a0 = 0;
335905b261ecSmrg			a1 -= 360 * 64;
336005b261ecSmrg			curq = 0;
336105b261ecSmrg			endq -= 4;
336205b261ecSmrg		}
336305b261ecSmrg	}
336405b261ecSmrg	sweepno = 0;
336505b261ecSmrg	for (;;) {
336605b261ecSmrg		q0 = 90 * 64;
336705b261ecSmrg		mask = 0;
336805b261ecSmrg		/*
336905b261ecSmrg		 * find left-most point
337005b261ecSmrg		 */
337105b261ecSmrg		for (i = 0; i < bandno; i++)
337205b261ecSmrg			if (band[i].a0 <= q0) {
337305b261ecSmrg				q0 = band[i].a0;
337405b261ecSmrg				q1 = band[i].a1;
337505b261ecSmrg				mask = band[i].mask;
337605b261ecSmrg			}
337705b261ecSmrg		if (!mask)
337805b261ecSmrg			break;
337905b261ecSmrg		/*
338005b261ecSmrg		 * locate next point of change
338105b261ecSmrg		 */
338205b261ecSmrg		for (i = 0; i < bandno; i++)
338305b261ecSmrg			if (!(mask & band[i].mask)) {
338405b261ecSmrg				if (band[i].a0 == q0) {
338505b261ecSmrg					if (band[i].a1 < q1)
338605b261ecSmrg						q1 = band[i].a1;
338705b261ecSmrg					mask |= band[i].mask;
338805b261ecSmrg 				} else if (band[i].a0 < q1)
338905b261ecSmrg					q1 = band[i].a0;
339005b261ecSmrg			}
339105b261ecSmrg		/*
339205b261ecSmrg		 * create a new sweep
339305b261ecSmrg		 */
339405b261ecSmrg		sweep[sweepno].a0 = q0;
339505b261ecSmrg		sweep[sweepno].a1 = q1;
339605b261ecSmrg		sweep[sweepno].mask = mask;
339705b261ecSmrg		sweepno++;
339805b261ecSmrg		/*
339905b261ecSmrg		 * subtract the sweep from the affected bands
340005b261ecSmrg		 */
340105b261ecSmrg		for (i = 0; i < bandno; i++)
340205b261ecSmrg			if (band[i].a0 == q0) {
340305b261ecSmrg				band[i].a0 = q1;
340405b261ecSmrg				/*
340505b261ecSmrg				 * check if this band is empty
340605b261ecSmrg				 */
340705b261ecSmrg				if (band[i].a0 == band[i].a1)
340805b261ecSmrg					band[i].a1 = band[i].a0 = 90 * 64 + 1;
340905b261ecSmrg			}
341005b261ecSmrg	}
341105b261ecSmrg	computeAcc (tarc, l, &def, &acc);
341205b261ecSmrg	for (j = 0; j < sweepno; j++) {
341305b261ecSmrg		mask = sweep[j].mask;
341405b261ecSmrg		passRight = passLeft = 0;
341505b261ecSmrg 		if (mask & (1 << rightq)) {
341605b261ecSmrg			if (sweep[j].a0 == righta)
341705b261ecSmrg				passRight = right;
341805b261ecSmrg			else if (sweep[j].a1 == righta) {
341905b261ecSmrg				passLeft = right;
342005b261ecSmrg				flipRight = 1;
342105b261ecSmrg			}
342205b261ecSmrg		}
342305b261ecSmrg		if (mask & (1 << leftq)) {
342405b261ecSmrg			if (sweep[j].a1 == lefta)
342505b261ecSmrg			{
342605b261ecSmrg				if (passLeft)
342705b261ecSmrg					copyEnd = 1;
342805b261ecSmrg				passLeft = left;
342905b261ecSmrg			}
343005b261ecSmrg			else if (sweep[j].a0 == lefta) {
343105b261ecSmrg				if (passRight)
343205b261ecSmrg					copyEnd = 1;
343305b261ecSmrg				passRight = left;
343405b261ecSmrg				flipLeft = 1;
343505b261ecSmrg			}
343605b261ecSmrg		}
343705b261ecSmrg		drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask,
343805b261ecSmrg 			      passRight, passLeft, spdata);
343905b261ecSmrg	}
344005b261ecSmrg	/*
344105b261ecSmrg	 * when copyEnd is set, both ends of the arc were computed
344205b261ecSmrg	 * at the same time; drawQuadrant only takes one end though,
344305b261ecSmrg	 * so the left end will be the only one holding the data.  Copy
344405b261ecSmrg	 * it from there.
344505b261ecSmrg	 */
344605b261ecSmrg	if (copyEnd)
344705b261ecSmrg		*right = *left;
344805b261ecSmrg	/*
344905b261ecSmrg	 * mirror the coordinates generated for the
345005b261ecSmrg	 * faces of the arc
345105b261ecSmrg	 */
345205b261ecSmrg	if (right) {
345305b261ecSmrg		mirrorSppPoint (rightq, &right->clock);
345405b261ecSmrg		mirrorSppPoint (rightq, &right->center);
345505b261ecSmrg		mirrorSppPoint (rightq, &right->counterClock);
345605b261ecSmrg		if (flipRight) {
345705b261ecSmrg			SppPointRec	temp;
345805b261ecSmrg
345905b261ecSmrg			temp = right->clock;
346005b261ecSmrg			right->clock = right->counterClock;
346105b261ecSmrg			right->counterClock = temp;
346205b261ecSmrg		}
346305b261ecSmrg	}
346405b261ecSmrg	if (left) {
346505b261ecSmrg		mirrorSppPoint (leftq,  &left->counterClock);
346605b261ecSmrg		mirrorSppPoint (leftq,  &left->center);
346705b261ecSmrg		mirrorSppPoint (leftq,  &left->clock);
346805b261ecSmrg		if (flipLeft) {
346905b261ecSmrg			SppPointRec	temp;
347005b261ecSmrg
347105b261ecSmrg			temp = left->clock;
347205b261ecSmrg			left->clock = left->counterClock;
347305b261ecSmrg			left->counterClock = temp;
347405b261ecSmrg		}
347505b261ecSmrg	}
34766747b715Smrg	free(spdata);
347705b261ecSmrg}
347805b261ecSmrg
347905b261ecSmrgstatic void
348005b261ecSmrgdrawQuadrant (
348105b261ecSmrg	struct arc_def		*def,
348205b261ecSmrg	struct accelerators	*acc,
348305b261ecSmrg	int			a0,
348405b261ecSmrg	int			a1,
348505b261ecSmrg	int			mask,
348605b261ecSmrg	miArcFacePtr		right,
348705b261ecSmrg	miArcFacePtr		left,
348805b261ecSmrg	miArcSpanData		*spdata)
348905b261ecSmrg{
349005b261ecSmrg	struct arc_bound	bound;
349105b261ecSmrg	double			yy, x, xalt;
349205b261ecSmrg	int			y, miny, maxy;
349305b261ecSmrg	int			n;
349405b261ecSmrg	miArcSpan		*span;
349505b261ecSmrg
349605b261ecSmrg	def->a0 = ((double) a0) / 64.0;
349705b261ecSmrg	def->a1 = ((double) a1) / 64.0;
349805b261ecSmrg	computeBound (def, &bound, acc, right, left);
349905b261ecSmrg	yy = bound.inner.min;
350005b261ecSmrg	if (bound.outer.min < yy)
350105b261ecSmrg	    yy = bound.outer.min;
350205b261ecSmrg	miny = ICEIL(yy - acc->fromIntY);
350305b261ecSmrg	yy = bound.inner.max;
350405b261ecSmrg	if (bound.outer.max > yy)
350505b261ecSmrg	    yy = bound.outer.max;
350605b261ecSmrg	maxy = floor(yy - acc->fromIntY);
350705b261ecSmrg	y = spdata->k;
350805b261ecSmrg	span = spdata->spans;
350905b261ecSmrg	if (spdata->top)
351005b261ecSmrg	{
351105b261ecSmrg	    if (a1 == 90 * 64 && (mask & 1))
351205b261ecSmrg		newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
351305b261ecSmrg	    span++;
351405b261ecSmrg	}
351505b261ecSmrg	for (n = spdata->count1; --n >= 0; )
351605b261ecSmrg	{
351705b261ecSmrg	    if (y < miny)
351805b261ecSmrg		return;
351905b261ecSmrg	    if (y <= maxy) {
352005b261ecSmrg		arcSpan (y,
352105b261ecSmrg			 span->lx, -span->lx, 0, span->lx + span->lw,
352205b261ecSmrg			 def, &bound, acc, mask);
352305b261ecSmrg		if (span->rw + span->rx)
352405b261ecSmrg		    tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
352505b261ecSmrg	    }
352605b261ecSmrg	    y--;
352705b261ecSmrg	    span++;
352805b261ecSmrg	}
352905b261ecSmrg	if (y < miny)
353005b261ecSmrg	    return;
353105b261ecSmrg	if (spdata->hole)
353205b261ecSmrg	{
353305b261ecSmrg	    if (y <= maxy)
353405b261ecSmrg		arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
353505b261ecSmrg	}
353605b261ecSmrg	for (n = spdata->count2; --n >= 0; )
353705b261ecSmrg	{
353805b261ecSmrg	    if (y < miny)
353905b261ecSmrg		return;
354005b261ecSmrg	    if (y <= maxy)
354105b261ecSmrg		arcSpan (y, span->lx, span->lw, span->rx, span->rw,
354205b261ecSmrg			 def, &bound, acc, mask);
354305b261ecSmrg	    y--;
354405b261ecSmrg	    span++;
354505b261ecSmrg	}
354605b261ecSmrg	if (spdata->bot && miny <= y && y <= maxy)
354705b261ecSmrg	{
354805b261ecSmrg	    n = mask;
354905b261ecSmrg	    if (y == miny)
355005b261ecSmrg		n &= 0xc;
355105b261ecSmrg	    if (span->rw <= 0) {
355205b261ecSmrg		arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
355305b261ecSmrg			  def, &bound, acc, n);
355405b261ecSmrg		if (span->rw + span->rx)
355505b261ecSmrg		    tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
355605b261ecSmrg	    }
355705b261ecSmrg	    else
355805b261ecSmrg		arcSpan0 (span->lx, span->lw, span->rx, span->rw,
355905b261ecSmrg			  def, &bound, acc, n);
356005b261ecSmrg	    y--;
356105b261ecSmrg	}
356205b261ecSmrg	while (y >= miny) {
356305b261ecSmrg	    yy = y + acc->fromIntY;
356405b261ecSmrg	    if (def->w == def->h) {
356505b261ecSmrg		xalt = def->w - def->l;
356605b261ecSmrg		x = -sqrt(xalt * xalt - yy * yy);
356705b261ecSmrg	    } else {
356805b261ecSmrg		x = tailX(yy, def, &bound, acc);
356905b261ecSmrg		if (acc->left.valid && boundedLe (yy, bound.left)) {
357005b261ecSmrg		    xalt = intersectLine (yy, acc->left);
357105b261ecSmrg		    if (xalt < x)
357205b261ecSmrg			x = xalt;
357305b261ecSmrg		}
357405b261ecSmrg		if (acc->right.valid && boundedLe (yy, bound.right)) {
357505b261ecSmrg		    xalt = intersectLine (yy, acc->right);
357605b261ecSmrg		    if (xalt < x)
357705b261ecSmrg			x = xalt;
357805b261ecSmrg		}
357905b261ecSmrg	    }
358005b261ecSmrg	    arcSpan (y,
358105b261ecSmrg		     ICEIL(acc->fromIntX - x), 0,
358205b261ecSmrg		     ICEIL(acc->fromIntX + x), 0,
358305b261ecSmrg		     def, &bound, acc, mask);
358405b261ecSmrg	    y--;
358505b261ecSmrg	}
358605b261ecSmrg}
3587