1706f2543Smrg/***********************************************************
2706f2543Smrg
3706f2543SmrgCopyright 1987, 1998  The Open Group
4706f2543Smrg
5706f2543SmrgPermission to use, copy, modify, distribute, and sell this software and its
6706f2543Smrgdocumentation for any purpose is hereby granted without fee, provided that
7706f2543Smrgthe above copyright notice appear in all copies and that both that
8706f2543Smrgcopyright notice and this permission notice appear in supporting
9706f2543Smrgdocumentation.
10706f2543Smrg
11706f2543SmrgThe above copyright notice and this permission notice shall be included in
12706f2543Smrgall copies or substantial portions of the Software.
13706f2543Smrg
14706f2543SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15706f2543SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16706f2543SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17706f2543SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18706f2543SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19706f2543SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20706f2543Smrg
21706f2543SmrgExcept as contained in this notice, the name of The Open Group shall not be
22706f2543Smrgused in advertising or otherwise to promote the sale, use or other dealings
23706f2543Smrgin this Software without prior written authorization from The Open Group.
24706f2543Smrg
25706f2543Smrg
26706f2543SmrgCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
27706f2543Smrg
28706f2543Smrg                        All Rights Reserved
29706f2543Smrg
30706f2543SmrgPermission to use, copy, modify, and distribute this software and its
31706f2543Smrgdocumentation for any purpose and without fee is hereby granted,
32706f2543Smrgprovided that the above copyright notice appear in all copies and that
33706f2543Smrgboth that copyright notice and this permission notice appear in
34706f2543Smrgsupporting documentation, and that the name of Digital not be
35706f2543Smrgused in advertising or publicity pertaining to distribution of the
36706f2543Smrgsoftware without specific, written prior permission.
37706f2543Smrg
38706f2543SmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39706f2543SmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40706f2543SmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41706f2543SmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42706f2543SmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43706f2543SmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44706f2543SmrgSOFTWARE.
45706f2543Smrg
46706f2543Smrg******************************************************************/
47706f2543Smrg/* Author: Keith Packard and Bob Scheifler */
48706f2543Smrg/* Warning: this code is toxic, do not dally very long here. */
49706f2543Smrg
50706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
51706f2543Smrg#include <dix-config.h>
52706f2543Smrg#endif
53706f2543Smrg
54706f2543Smrg#include <math.h>
55706f2543Smrg#include <X11/X.h>
56706f2543Smrg#include <X11/Xprotostr.h>
57706f2543Smrg#include "misc.h"
58706f2543Smrg#include "gcstruct.h"
59706f2543Smrg#include "scrnintstr.h"
60706f2543Smrg#include "pixmapstr.h"
61706f2543Smrg#include "windowstr.h"
62706f2543Smrg#include "mifpoly.h"
63706f2543Smrg#include "mi.h"
64706f2543Smrg#include "mifillarc.h"
65706f2543Smrg#include <X11/Xfuncproto.h>
66706f2543Smrg
67706f2543Smrgstatic double miDsin(double a);
68706f2543Smrgstatic double miDcos(double a);
69706f2543Smrgstatic double miDasin(double v);
70706f2543Smrgstatic double miDatan2(double dy, double dx);
71706f2543Smrg
72706f2543Smrg#ifndef HAVE_CBRT
73706f2543Smrgstatic double
74706f2543Smrgcbrt(double x)
75706f2543Smrg{
76706f2543Smrg    if (x > 0.0)
77706f2543Smrg	return pow(x, 1.0/3.0);
78706f2543Smrg    else
79706f2543Smrg	return -pow(-x, 1.0/3.0);
80706f2543Smrg}
81706f2543Smrg#endif
82706f2543Smrg
83706f2543Smrg/*
84706f2543Smrg * some interesting sematic interpretation of the protocol:
85706f2543Smrg *
86706f2543Smrg * Self intersecting arcs (i.e. those spanning 360 degrees)
87706f2543Smrg *  never join with other arcs, and are drawn without caps
88706f2543Smrg *  (unless on/off dashed, in which case each dash segment
89706f2543Smrg *  is capped, except when the last segment meets the
90706f2543Smrg *  first segment, when no caps are drawn)
91706f2543Smrg *
92706f2543Smrg * double dash arcs are drawn in two parts, first the
93706f2543Smrg *  odd dashes (drawn in background) then the even dashes
94706f2543Smrg *  (drawn in foreground).  This means that overlapping
95706f2543Smrg *  sections of foreground/background are drawn twice,
96706f2543Smrg *  first in background then in foreground.  The double-draw
97706f2543Smrg *  occurs even when the function uses the destination values
98706f2543Smrg *  (e.g. xor mode).  This is the same way the wide-line
99706f2543Smrg *  code works and should be "fixed".
100706f2543Smrg *
101706f2543Smrg */
102706f2543Smrg
103706f2543Smrg#undef max
104706f2543Smrg#undef min
105706f2543Smrg
106706f2543Smrg_X_INLINE static int max (const int x, const int y)
107706f2543Smrg{
108706f2543Smrg	return x>y? x:y;
109706f2543Smrg}
110706f2543Smrg
111706f2543Smrg_X_INLINE static int min (const int x, const int y)
112706f2543Smrg{
113706f2543Smrg	return x<y? x:y;
114706f2543Smrg}
115706f2543Smrg
116706f2543Smrgstruct bound {
117706f2543Smrg	double	min, max;
118706f2543Smrg};
119706f2543Smrg
120706f2543Smrgstruct ibound {
121706f2543Smrg	int	min, max;
122706f2543Smrg};
123706f2543Smrg
124706f2543Smrg#define boundedLe(value, bounds)\
125706f2543Smrg	((bounds).min <= (value) && (value) <= (bounds).max)
126706f2543Smrg
127706f2543Smrgstruct line {
128706f2543Smrg	double	m, b;
129706f2543Smrg	int	valid;
130706f2543Smrg};
131706f2543Smrg
132706f2543Smrg#define intersectLine(y,line) (line.m * (y) + line.b)
133706f2543Smrg
134706f2543Smrg/*
135706f2543Smrg * these are all y value bounds
136706f2543Smrg */
137706f2543Smrg
138706f2543Smrgstruct arc_bound {
139706f2543Smrg	struct bound	ellipse;
140706f2543Smrg	struct bound	inner;
141706f2543Smrg	struct bound	outer;
142706f2543Smrg	struct bound	right;
143706f2543Smrg	struct bound	left;
144706f2543Smrg	struct ibound	inneri;
145706f2543Smrg	struct ibound	outeri;
146706f2543Smrg};
147706f2543Smrg
148706f2543Smrgstruct accelerators {
149706f2543Smrg	double		tail_y;
150706f2543Smrg	double		h2;
151706f2543Smrg	double		w2;
152706f2543Smrg	double		h4;
153706f2543Smrg	double		w4;
154706f2543Smrg	double		h2mw2;
155706f2543Smrg	double		h2l;
156706f2543Smrg	double		w2l;
157706f2543Smrg	double		fromIntX;
158706f2543Smrg	double		fromIntY;
159706f2543Smrg	struct line	left, right;
160706f2543Smrg	int		yorgu;
161706f2543Smrg	int		yorgl;
162706f2543Smrg	int		xorg;
163706f2543Smrg};
164706f2543Smrg
165706f2543Smrgstruct arc_def {
166706f2543Smrg	double	w, h, l;
167706f2543Smrg	double	a0, a1;
168706f2543Smrg};
169706f2543Smrg
170706f2543Smrg# define todeg(xAngle)	(((double) (xAngle)) / 64.0)
171706f2543Smrg
172706f2543Smrg# define RIGHT_END	0
173706f2543Smrg# define LEFT_END	1
174706f2543Smrg
175706f2543Smrgtypedef struct _miArcJoin {
176706f2543Smrg	int	arcIndex0, arcIndex1;
177706f2543Smrg	int	phase0, phase1;
178706f2543Smrg	int	end0, end1;
179706f2543Smrg} miArcJoinRec, *miArcJoinPtr;
180706f2543Smrg
181706f2543Smrgtypedef struct _miArcCap {
182706f2543Smrg	int		arcIndex;
183706f2543Smrg	int		end;
184706f2543Smrg} miArcCapRec, *miArcCapPtr;
185706f2543Smrg
186706f2543Smrgtypedef struct _miArcFace {
187706f2543Smrg	SppPointRec	clock;
188706f2543Smrg	SppPointRec	center;
189706f2543Smrg	SppPointRec	counterClock;
190706f2543Smrg} miArcFaceRec, *miArcFacePtr;
191706f2543Smrg
192706f2543Smrgtypedef struct _miArcData {
193706f2543Smrg	xArc		arc;
194706f2543Smrg	int		render;		/* non-zero means render after drawing */
195706f2543Smrg	int		join;		/* related join */
196706f2543Smrg	int		cap;		/* related cap */
197706f2543Smrg	int		selfJoin;	/* final dash meets first dash */
198706f2543Smrg	miArcFaceRec	bounds[2];
199706f2543Smrg	double		x0, y0, x1, y1;
200706f2543Smrg} miArcDataRec, *miArcDataPtr;
201706f2543Smrg
202706f2543Smrg/*
203706f2543Smrg * This is an entire sequence of arcs, computed and categorized according
204706f2543Smrg * to operation.  miDashArcs generates either one or two of these.
205706f2543Smrg */
206706f2543Smrg
207706f2543Smrgtypedef struct _miPolyArc {
208706f2543Smrg	int		narcs;
209706f2543Smrg	miArcDataPtr	arcs;
210706f2543Smrg	int		ncaps;
211706f2543Smrg	miArcCapPtr	caps;
212706f2543Smrg	int		njoins;
213706f2543Smrg	miArcJoinPtr	joins;
214706f2543Smrg} miPolyArcRec, *miPolyArcPtr;
215706f2543Smrg
216706f2543Smrgstatic void fillSpans(DrawablePtr pDrawable, GCPtr pGC);
217706f2543Smrgstatic void newFinalSpan(int y, int xmin, int xmax);
218706f2543Smrgstatic void drawArc(xArc *tarc, int l, int a0, int a1, miArcFacePtr right,
219706f2543Smrg		    miArcFacePtr left);
220706f2543Smrgstatic void drawZeroArc(DrawablePtr pDraw, GCPtr pGC, xArc *tarc, int lw,
221706f2543Smrg			miArcFacePtr left, miArcFacePtr right);
222706f2543Smrgstatic void miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
223706f2543Smrg		      miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
224706f2543Smrg		      double xFtransLeft, double yFtransLeft,
225706f2543Smrg		      int xOrgRight, int yOrgRight,
226706f2543Smrg		      double xFtransRight, double yFtransRight);
227706f2543Smrgstatic void miArcCap(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pFace,
228706f2543Smrg		     int end, int xOrg, int yOrg, double xFtrans,
229706f2543Smrg		     double yFtrans);
230706f2543Smrgstatic void miRoundCap(DrawablePtr pDraw, GCPtr pGC, SppPointRec pCenter,
231706f2543Smrg		       SppPointRec pEnd, SppPointRec pCorner,
232706f2543Smrg		       SppPointRec pOtherCorner, int fLineEnd,
233706f2543Smrg		       int xOrg, int yOrg, double xFtrans, double yFtrans);
234706f2543Smrgstatic void miFreeArcs(miPolyArcPtr arcs, GCPtr pGC);
235706f2543Smrgstatic miPolyArcPtr miComputeArcs(xArc *parcs, int narcs, GCPtr pGC);
236706f2543Smrgstatic int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts);
237706f2543Smrg
238706f2543Smrg# define CUBED_ROOT_2	1.2599210498948732038115849718451499938964
239706f2543Smrg# define CUBED_ROOT_4	1.5874010519681993173435330390930175781250
240706f2543Smrg
241706f2543Smrg/*
242706f2543Smrg * draw one segment of the arc using the arc spans generation routines
243706f2543Smrg */
244706f2543Smrg
245706f2543Smrgstatic void
246706f2543SmrgmiArcSegment(
247706f2543Smrg    DrawablePtr   pDraw,
248706f2543Smrg    GCPtr         pGC,
249706f2543Smrg    xArc          tarc,
250706f2543Smrg    miArcFacePtr	right,
251706f2543Smrg    miArcFacePtr	left)
252706f2543Smrg{
253706f2543Smrg    int l = pGC->lineWidth;
254706f2543Smrg    int a0, a1, startAngle, endAngle;
255706f2543Smrg    miArcFacePtr temp;
256706f2543Smrg
257706f2543Smrg    if (!l)
258706f2543Smrg	l = 1;
259706f2543Smrg
260706f2543Smrg    if (tarc.width == 0 || tarc.height == 0) {
261706f2543Smrg    	drawZeroArc (pDraw, pGC, &tarc, l, left, right);
262706f2543Smrg	return;
263706f2543Smrg    }
264706f2543Smrg
265706f2543Smrg    if (pGC->miTranslate) {
266706f2543Smrg	tarc.x += pDraw->x;
267706f2543Smrg	tarc.y += pDraw->y;
268706f2543Smrg    }
269706f2543Smrg
270706f2543Smrg    a0 = tarc.angle1;
271706f2543Smrg    a1 = tarc.angle2;
272706f2543Smrg    if (a1 > FULLCIRCLE)
273706f2543Smrg	a1 = FULLCIRCLE;
274706f2543Smrg    else if (a1 < -FULLCIRCLE)
275706f2543Smrg	a1 = -FULLCIRCLE;
276706f2543Smrg    if (a1 < 0) {
277706f2543Smrg    	startAngle = a0 + a1;
278706f2543Smrg	endAngle = a0;
279706f2543Smrg	temp = right;
280706f2543Smrg	right = left;
281706f2543Smrg	left = temp;
282706f2543Smrg    } else {
283706f2543Smrg	startAngle = a0;
284706f2543Smrg	endAngle = a0 + a1;
285706f2543Smrg    }
286706f2543Smrg    /*
287706f2543Smrg     * bounds check the two angles
288706f2543Smrg     */
289706f2543Smrg    if (startAngle < 0)
290706f2543Smrg	startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
291706f2543Smrg    if (startAngle >= FULLCIRCLE)
292706f2543Smrg	startAngle = startAngle % FULLCIRCLE;
293706f2543Smrg    if (endAngle < 0)
294706f2543Smrg	endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
295706f2543Smrg    if (endAngle > FULLCIRCLE)
296706f2543Smrg	endAngle = (endAngle-1) % FULLCIRCLE + 1;
297706f2543Smrg    if ((startAngle == endAngle) && a1) {
298706f2543Smrg	startAngle = 0;
299706f2543Smrg	endAngle = FULLCIRCLE;
300706f2543Smrg    }
301706f2543Smrg
302706f2543Smrg    drawArc (&tarc, l, startAngle, endAngle, right, left);
303706f2543Smrg}
304706f2543Smrg
305706f2543Smrg/*
306706f2543Smrg
307706f2543SmrgThree equations combine to describe the boundaries of the arc
308706f2543Smrg
309706f2543Smrgx^2/w^2 + y^2/h^2 = 1			ellipse itself
310706f2543Smrg(X-x)^2 + (Y-y)^2 = r^2			circle at (x, y) on the ellipse
311706f2543Smrg(Y-y) = (X-x)*w^2*y/(h^2*x)		normal at (x, y) on the ellipse
312706f2543Smrg
313706f2543SmrgThese lead to a quartic relating Y and y
314706f2543Smrg
315706f2543Smrgy^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
316706f2543Smrg    - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
317706f2543Smrg
318706f2543SmrgThe reducible cubic obtained from this quartic is
319706f2543Smrg
320706f2543Smrgz^3 - (3N)z^2 - 2V = 0
321706f2543Smrg
322706f2543Smrgwhere
323706f2543Smrg
324706f2543SmrgN = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
325706f2543SmrgV = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
326706f2543Smrg
327706f2543SmrgLet
328706f2543Smrg
329706f2543Smrgt = z - N
330706f2543Smrgp = -N^2
331706f2543Smrgq = -N^3 - V
332706f2543Smrg
333706f2543SmrgThen we get
334706f2543Smrg
335706f2543Smrgt^3 + 3pt + 2q = 0
336706f2543Smrg
337706f2543SmrgThe discriminant of this cubic is
338706f2543Smrg
339706f2543SmrgD = q^2 + p^3
340706f2543Smrg
341706f2543SmrgWhen D > 0, a real root is obtained as
342706f2543Smrg
343706f2543Smrgz = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
344706f2543Smrg
345706f2543SmrgWhen D < 0, a real root is obtained as
346706f2543Smrg
347706f2543Smrgz = N - 2m*cos(acos(-q/m^3)/3)
348706f2543Smrg
349706f2543Smrgwhere
350706f2543Smrg
351706f2543Smrgm = sqrt(|p|) * sign(q)
352706f2543Smrg
353706f2543SmrgGiven a real root Z of the cubic, the roots of the quartic are the roots
354706f2543Smrgof the two quadratics
355706f2543Smrg
356706f2543Smrgy^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
357706f2543Smrg
358706f2543Smrgwhere
359706f2543Smrg
360706f2543SmrgA = +/- sqrt(8Z + b^2 - 4c)
361706f2543Smrgb, c, d are the cubic, quadratic, and linear coefficients of the quartic
362706f2543Smrg
363706f2543SmrgSome experimentation is then required to determine which solutions
364706f2543Smrgcorrespond to the inner and outer boundaries.
365706f2543Smrg
366706f2543Smrg*/
367706f2543Smrg
368706f2543Smrgtypedef struct {
369706f2543Smrg    short lx, lw, rx, rw;
370706f2543Smrg} miArcSpan;
371706f2543Smrg
372706f2543Smrgtypedef struct {
373706f2543Smrg    miArcSpan *spans;
374706f2543Smrg    int count1, count2, k;
375706f2543Smrg    char top, bot, hole;
376706f2543Smrg} miArcSpanData;
377706f2543Smrg
378706f2543Smrgstatic void drawQuadrant(struct arc_def *def, struct accelerators *acc,
379706f2543Smrg			 int a0, int a1, int mask, miArcFacePtr right,
380706f2543Smrg			 miArcFacePtr left, miArcSpanData *spdata);
381706f2543Smrg
382706f2543Smrgstatic void
383706f2543SmrgmiComputeCircleSpans(
384706f2543Smrg    int lw,
385706f2543Smrg    xArc *parc,
386706f2543Smrg    miArcSpanData *spdata)
387706f2543Smrg{
388706f2543Smrg    miArcSpan *span;
389706f2543Smrg    int doinner;
390706f2543Smrg    int x, y, e;
391706f2543Smrg    int xk, yk, xm, ym, dx, dy;
392706f2543Smrg    int slw, inslw;
393706f2543Smrg    int inx = 0, iny, ine = 0;
394706f2543Smrg    int inxk = 0, inyk = 0, inxm = 0, inym = 0;
395706f2543Smrg
396706f2543Smrg    doinner = -lw;
397706f2543Smrg    slw = parc->width - doinner;
398706f2543Smrg    y = parc->height >> 1;
399706f2543Smrg    dy = parc->height & 1;
400706f2543Smrg    dx = 1 - dy;
401706f2543Smrg    MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
402706f2543Smrg    inslw = parc->width + doinner;
403706f2543Smrg    if (inslw > 0)
404706f2543Smrg    {
405706f2543Smrg	spdata->hole = spdata->top;
406706f2543Smrg	MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
407706f2543Smrg    }
408706f2543Smrg    else
409706f2543Smrg    {
410706f2543Smrg	spdata->hole = FALSE;
411706f2543Smrg	doinner = -y;
412706f2543Smrg    }
413706f2543Smrg    spdata->count1 = -doinner - spdata->top;
414706f2543Smrg    spdata->count2 = y + doinner;
415706f2543Smrg    span = spdata->spans;
416706f2543Smrg    while (y)
417706f2543Smrg    {
418706f2543Smrg	MIFILLARCSTEP(slw);
419706f2543Smrg	span->lx = dy - x;
420706f2543Smrg	if (++doinner <= 0)
421706f2543Smrg 	{
422706f2543Smrg	    span->lw = slw;
423706f2543Smrg	    span->rx = 0;
424706f2543Smrg	    span->rw = span->lx + slw;
425706f2543Smrg	}
426706f2543Smrg	else
427706f2543Smrg	{
428706f2543Smrg	    MIFILLINARCSTEP(inslw);
429706f2543Smrg	    span->lw = x - inx;
430706f2543Smrg	    span->rx = dy - inx + inslw;
431706f2543Smrg	    span->rw = inx - x + slw - inslw;
432706f2543Smrg	}
433706f2543Smrg	span++;
434706f2543Smrg    }
435706f2543Smrg    if (spdata->bot)
436706f2543Smrg    {
437706f2543Smrg	if (spdata->count2)
438706f2543Smrg	    spdata->count2--;
439706f2543Smrg	else
440706f2543Smrg	{
441706f2543Smrg	    if (lw > (int)parc->height)
442706f2543Smrg		span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1);
443706f2543Smrg	    else
444706f2543Smrg		span[-1].rw = 0;
445706f2543Smrg	    spdata->count1--;
446706f2543Smrg	}
447706f2543Smrg    }
448706f2543Smrg}
449706f2543Smrg
450706f2543Smrgstatic void
451706f2543SmrgmiComputeEllipseSpans(
452706f2543Smrg    int lw,
453706f2543Smrg    xArc *parc,
454706f2543Smrg    miArcSpanData *spdata)
455706f2543Smrg{
456706f2543Smrg    miArcSpan *span;
457706f2543Smrg    double w, h, r, xorg;
458706f2543Smrg    double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
459706f2543Smrg    double A, T, b, d, x, y, t, inx, outx = 0.0, hepp, hepm;
460706f2543Smrg    int flip, solution;
461706f2543Smrg
462706f2543Smrg    w = (double)parc->width / 2.0;
463706f2543Smrg    h = (double)parc->height / 2.0;
464706f2543Smrg    r = lw / 2.0;
465706f2543Smrg    rs = r * r;
466706f2543Smrg    Hs = h * h;
467706f2543Smrg    WH = w * w - Hs;
468706f2543Smrg    Nk = w * r;
469706f2543Smrg    Vk = (Nk * Hs) / (WH + WH);
470706f2543Smrg    Hf = Hs * Hs;
471706f2543Smrg    Nk = (Hf - Nk * Nk) / WH;
472706f2543Smrg    Fk = Hf / WH;
473706f2543Smrg    hepp = h + EPSILON;
474706f2543Smrg    hepm = h - EPSILON;
475706f2543Smrg    K = h + ((lw - 1) >> 1);
476706f2543Smrg    span = spdata->spans;
477706f2543Smrg    if (parc->width & 1)
478706f2543Smrg	xorg = .5;
479706f2543Smrg    else
480706f2543Smrg	xorg = 0.0;
481706f2543Smrg    if (spdata->top)
482706f2543Smrg    {
483706f2543Smrg	span->lx = 0;
484706f2543Smrg	span->lw = 1;
485706f2543Smrg	span++;
486706f2543Smrg    }
487706f2543Smrg    spdata->count1 = 0;
488706f2543Smrg    spdata->count2 = 0;
489706f2543Smrg    spdata->hole = (spdata->top &&
490706f2543Smrg		 (int)parc->height * lw <= (int)(parc->width * parc->width) &&
491706f2543Smrg		    lw < (int)parc->height);
492706f2543Smrg    for (; K > 0.0; K -= 1.0)
493706f2543Smrg    {
494706f2543Smrg	N = (K * K + Nk) / 6.0;
495706f2543Smrg	Nc = N * N * N;
496706f2543Smrg	Vr = Vk * K;
497706f2543Smrg	t = Nc + Vr * Vr;
498706f2543Smrg	d = Nc + t;
499706f2543Smrg	if (d < 0.0) {
500706f2543Smrg	    d = Nc;
501706f2543Smrg	    b = N;
502706f2543Smrg	    if ( (b < 0.0) == (t < 0.0) )
503706f2543Smrg	    {
504706f2543Smrg		b = -b;
505706f2543Smrg		d = -d;
506706f2543Smrg	    }
507706f2543Smrg	    Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
508706f2543Smrg	    if ( (Z < 0.0) == (Vr < 0.0) )
509706f2543Smrg		flip = 2;
510706f2543Smrg	    else
511706f2543Smrg		flip = 1;
512706f2543Smrg	}
513706f2543Smrg	else
514706f2543Smrg	{
515706f2543Smrg	    d = Vr * sqrt(d);
516706f2543Smrg	    Z = N + cbrt(t + d) + cbrt(t - d);
517706f2543Smrg	    flip = 0;
518706f2543Smrg	}
519706f2543Smrg	A = sqrt((Z + Z) - Nk);
520706f2543Smrg	T = (Fk - Z) * K / A;
521706f2543Smrg	inx = 0.0;
522706f2543Smrg	solution = FALSE;
523706f2543Smrg	b = -A + K;
524706f2543Smrg	d = b * b - 4 * (Z + T);
525706f2543Smrg	if (d >= 0)
526706f2543Smrg	{
527706f2543Smrg	    d = sqrt(d);
528706f2543Smrg	    y = (b + d) / 2;
529706f2543Smrg	    if ((y >= 0.0) && (y < hepp))
530706f2543Smrg	    {
531706f2543Smrg		solution = TRUE;
532706f2543Smrg		if (y > hepm)
533706f2543Smrg		    y = h;
534706f2543Smrg		t = y / h;
535706f2543Smrg		x = w * sqrt(1 - (t * t));
536706f2543Smrg		t = K - y;
537706f2543Smrg		if (rs - (t * t) >= 0)
538706f2543Smrg		   t = sqrt(rs - (t * t));
539706f2543Smrg		else
540706f2543Smrg		   t = 0;
541706f2543Smrg		if (flip == 2)
542706f2543Smrg		    inx = x - t;
543706f2543Smrg		else
544706f2543Smrg		    outx = x + t;
545706f2543Smrg	    }
546706f2543Smrg	}
547706f2543Smrg	b = A + K;
548706f2543Smrg	d = b * b - 4 * (Z - T);
549706f2543Smrg	/* Because of the large magnitudes involved, we lose enough precision
550706f2543Smrg	 * that sometimes we end up with a negative value near the axis, when
551706f2543Smrg	 * it should be positive.  This is a workaround.
552706f2543Smrg	 */
553706f2543Smrg	if (d < 0 && !solution)
554706f2543Smrg	    d = 0.0;
555706f2543Smrg	if (d >= 0) {
556706f2543Smrg	    d = sqrt(d);
557706f2543Smrg	    y = (b + d) / 2;
558706f2543Smrg	    if (y < hepp)
559706f2543Smrg	    {
560706f2543Smrg		if (y > hepm)
561706f2543Smrg		    y = h;
562706f2543Smrg		t = y / h;
563706f2543Smrg		x = w * sqrt(1 - (t * t));
564706f2543Smrg		t = K - y;
565706f2543Smrg		if (rs - (t * t) >= 0)
566706f2543Smrg		   inx = x - sqrt(rs - (t * t));
567706f2543Smrg		else
568706f2543Smrg		   inx = x;
569706f2543Smrg	    }
570706f2543Smrg	    y = (b - d) / 2;
571706f2543Smrg	    if (y >= 0.0)
572706f2543Smrg	    {
573706f2543Smrg		if (y > hepm)
574706f2543Smrg		    y = h;
575706f2543Smrg		t = y / h;
576706f2543Smrg		x = w * sqrt(1 - (t * t));
577706f2543Smrg		t = K - y;
578706f2543Smrg		if (rs - (t * t) >= 0)
579706f2543Smrg		   t = sqrt(rs - (t * t));
580706f2543Smrg		else
581706f2543Smrg		   t = 0;
582706f2543Smrg		if (flip == 1)
583706f2543Smrg		    inx = x - t;
584706f2543Smrg		else
585706f2543Smrg		    outx = x + t;
586706f2543Smrg	    }
587706f2543Smrg	}
588706f2543Smrg	span->lx = ICEIL(xorg - outx);
589706f2543Smrg	if (inx <= 0.0)
590706f2543Smrg	{
591706f2543Smrg	    spdata->count1++;
592706f2543Smrg	    span->lw = ICEIL(xorg + outx) - span->lx;
593706f2543Smrg	    span->rx = ICEIL(xorg + inx);
594706f2543Smrg	    span->rw = -ICEIL(xorg - inx);
595706f2543Smrg	}
596706f2543Smrg	else
597706f2543Smrg	{
598706f2543Smrg	    spdata->count2++;
599706f2543Smrg	    span->lw = ICEIL(xorg - inx) - span->lx;
600706f2543Smrg	    span->rx = ICEIL(xorg + inx);
601706f2543Smrg	    span->rw = ICEIL(xorg + outx) - span->rx;
602706f2543Smrg	}
603706f2543Smrg	span++;
604706f2543Smrg    }
605706f2543Smrg    if (spdata->bot)
606706f2543Smrg    {
607706f2543Smrg	outx = w + r;
608706f2543Smrg	if (r >= h && r <= w)
609706f2543Smrg	    inx = 0.0;
610706f2543Smrg	else if (Nk < 0.0 && -Nk < Hs)
611706f2543Smrg	{
612706f2543Smrg	    inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
613706f2543Smrg	    if (inx > w - r)
614706f2543Smrg		inx = w - r;
615706f2543Smrg	}
616706f2543Smrg	else
617706f2543Smrg	    inx = w - r;
618706f2543Smrg	span->lx = ICEIL(xorg - outx);
619706f2543Smrg	if (inx <= 0.0)
620706f2543Smrg	{
621706f2543Smrg	    span->lw = ICEIL(xorg + outx) - span->lx;
622706f2543Smrg	    span->rx = ICEIL(xorg + inx);
623706f2543Smrg	    span->rw = -ICEIL(xorg - inx);
624706f2543Smrg	}
625706f2543Smrg	else
626706f2543Smrg	{
627706f2543Smrg	    span->lw = ICEIL(xorg - inx) - span->lx;
628706f2543Smrg	    span->rx = ICEIL(xorg + inx);
629706f2543Smrg	    span->rw = ICEIL(xorg + outx) - span->rx;
630706f2543Smrg	}
631706f2543Smrg    }
632706f2543Smrg    if (spdata->hole)
633706f2543Smrg    {
634706f2543Smrg	span = &spdata->spans[spdata->count1];
635706f2543Smrg	span->lw = -span->lx;
636706f2543Smrg	span->rx = 1;
637706f2543Smrg	span->rw = span->lw;
638706f2543Smrg	spdata->count1--;
639706f2543Smrg	spdata->count2++;
640706f2543Smrg    }
641706f2543Smrg}
642706f2543Smrg
643706f2543Smrgstatic double
644706f2543SmrgtailX(
645706f2543Smrg    double K,
646706f2543Smrg    struct arc_def *def,
647706f2543Smrg    struct arc_bound *bounds,
648706f2543Smrg    struct accelerators *acc)
649706f2543Smrg{
650706f2543Smrg    double w, h, r;
651706f2543Smrg    double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
652706f2543Smrg    double A, T, b, d, x, y, t, hepp, hepm;
653706f2543Smrg    int flip, solution;
654706f2543Smrg    double xs[2];
655706f2543Smrg    double *xp;
656706f2543Smrg
657706f2543Smrg    w = def->w;
658706f2543Smrg    h = def->h;
659706f2543Smrg    r = def->l;
660706f2543Smrg    rs = r * r;
661706f2543Smrg    Hs = acc->h2;
662706f2543Smrg    WH = -acc->h2mw2;
663706f2543Smrg    Nk = def->w * r;
664706f2543Smrg    Vk = (Nk * Hs) / (WH + WH);
665706f2543Smrg    Hf = acc->h4;
666706f2543Smrg    Nk = (Hf - Nk * Nk) / WH;
667706f2543Smrg    if (K == 0.0) {
668706f2543Smrg	if (Nk < 0.0 && -Nk < Hs) {
669706f2543Smrg	    xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
670706f2543Smrg	    xs[1] = w - r;
671706f2543Smrg	    if (acc->left.valid && boundedLe(K, bounds->left) &&
672706f2543Smrg		!boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
673706f2543Smrg		return xs[1];
674706f2543Smrg	    if (acc->right.valid && boundedLe(K, bounds->right) &&
675706f2543Smrg		!boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
676706f2543Smrg		return xs[1];
677706f2543Smrg	    return xs[0];
678706f2543Smrg	}
679706f2543Smrg	return w - r;
680706f2543Smrg    }
681706f2543Smrg    Fk = Hf / WH;
682706f2543Smrg    hepp = h + EPSILON;
683706f2543Smrg    hepm = h - EPSILON;
684706f2543Smrg    N = (K * K + Nk) / 6.0;
685706f2543Smrg    Nc = N * N * N;
686706f2543Smrg    Vr = Vk * K;
687706f2543Smrg    xp = xs;
688706f2543Smrg    xs[0] = 0.0;
689706f2543Smrg    t = Nc + Vr * Vr;
690706f2543Smrg    d = Nc + t;
691706f2543Smrg    if (d < 0.0) {
692706f2543Smrg	d = Nc;
693706f2543Smrg	b = N;
694706f2543Smrg	if ( (b < 0.0) == (t < 0.0) )
695706f2543Smrg	{
696706f2543Smrg	    b = -b;
697706f2543Smrg	    d = -d;
698706f2543Smrg	}
699706f2543Smrg	Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
700706f2543Smrg	if ( (Z < 0.0) == (Vr < 0.0) )
701706f2543Smrg	    flip = 2;
702706f2543Smrg	else
703706f2543Smrg	    flip = 1;
704706f2543Smrg    }
705706f2543Smrg    else
706706f2543Smrg    {
707706f2543Smrg	d = Vr * sqrt(d);
708706f2543Smrg	Z = N + cbrt(t + d) + cbrt(t - d);
709706f2543Smrg	flip = 0;
710706f2543Smrg    }
711706f2543Smrg    A = sqrt((Z + Z) - Nk);
712706f2543Smrg    T = (Fk - Z) * K / A;
713706f2543Smrg    solution = FALSE;
714706f2543Smrg    b = -A + K;
715706f2543Smrg    d = b * b - 4 * (Z + T);
716706f2543Smrg    if (d >= 0 && flip == 2)
717706f2543Smrg    {
718706f2543Smrg	d = sqrt(d);
719706f2543Smrg	y = (b + d) / 2;
720706f2543Smrg	if ((y >= 0.0) && (y < hepp))
721706f2543Smrg	{
722706f2543Smrg	    solution = TRUE;
723706f2543Smrg	    if (y > hepm)
724706f2543Smrg		y = h;
725706f2543Smrg	    t = y / h;
726706f2543Smrg	    x = w * sqrt(1 - (t * t));
727706f2543Smrg	    t = K - y;
728706f2543Smrg	    if (rs - (t * t) >= 0)
729706f2543Smrg	       t = sqrt(rs - (t * t));
730706f2543Smrg	    else
731706f2543Smrg	       t = 0;
732706f2543Smrg	    *xp++ = x - t;
733706f2543Smrg	}
734706f2543Smrg    }
735706f2543Smrg    b = A + K;
736706f2543Smrg    d = b * b - 4 * (Z - T);
737706f2543Smrg    /* Because of the large magnitudes involved, we lose enough precision
738706f2543Smrg     * that sometimes we end up with a negative value near the axis, when
739706f2543Smrg     * it should be positive.  This is a workaround.
740706f2543Smrg     */
741706f2543Smrg    if (d < 0 && !solution)
742706f2543Smrg	d = 0.0;
743706f2543Smrg    if (d >= 0) {
744706f2543Smrg	d = sqrt(d);
745706f2543Smrg	y = (b + d) / 2;
746706f2543Smrg	if (y < hepp)
747706f2543Smrg	{
748706f2543Smrg	    if (y > hepm)
749706f2543Smrg		y = h;
750706f2543Smrg	    t = y / h;
751706f2543Smrg	    x = w * sqrt(1 - (t * t));
752706f2543Smrg	    t = K - y;
753706f2543Smrg	    if (rs - (t * t) >= 0)
754706f2543Smrg	       *xp++ = x - sqrt(rs - (t * t));
755706f2543Smrg	    else
756706f2543Smrg	       *xp++ = x;
757706f2543Smrg	}
758706f2543Smrg	y = (b - d) / 2;
759706f2543Smrg	if (y >= 0.0 && flip == 1)
760706f2543Smrg	{
761706f2543Smrg	    if (y > hepm)
762706f2543Smrg		y = h;
763706f2543Smrg	    t = y / h;
764706f2543Smrg	    x = w * sqrt(1 - (t * t));
765706f2543Smrg	    t = K - y;
766706f2543Smrg	    if (rs - (t * t) >= 0)
767706f2543Smrg	       t = sqrt(rs - (t * t));
768706f2543Smrg	    else
769706f2543Smrg	       t = 0;
770706f2543Smrg	    *xp++ = x - t;
771706f2543Smrg	}
772706f2543Smrg    }
773706f2543Smrg    if (xp > &xs[1]) {
774706f2543Smrg	if (acc->left.valid && boundedLe(K, bounds->left) &&
775706f2543Smrg	    !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
776706f2543Smrg	    return xs[1];
777706f2543Smrg	if (acc->right.valid && boundedLe(K, bounds->right) &&
778706f2543Smrg	    !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
779706f2543Smrg	    return xs[1];
780706f2543Smrg    }
781706f2543Smrg    return xs[0];
782706f2543Smrg}
783706f2543Smrg
784706f2543Smrgstatic miArcSpanData *
785706f2543SmrgmiComputeWideEllipse(int lw, xArc *parc)
786706f2543Smrg{
787706f2543Smrg    miArcSpanData *spdata = NULL;
788706f2543Smrg    int k;
789706f2543Smrg
790706f2543Smrg    if (!lw)
791706f2543Smrg	lw = 1;
792706f2543Smrg    k = (parc->height >> 1) + ((lw - 1) >> 1);
793706f2543Smrg    spdata = malloc(sizeof(miArcSpanData) + sizeof(miArcSpan) * (k + 2));
794706f2543Smrg    if (!spdata)
795706f2543Smrg	return NULL;
796706f2543Smrg    spdata->spans = (miArcSpan *)(spdata + 1);
797706f2543Smrg    spdata->k = k;
798706f2543Smrg    spdata->top = !(lw & 1) && !(parc->width & 1);
799706f2543Smrg    spdata->bot = !(parc->height & 1);
800706f2543Smrg    if (parc->width == parc->height)
801706f2543Smrg	miComputeCircleSpans(lw, parc, spdata);
802706f2543Smrg    else
803706f2543Smrg	miComputeEllipseSpans(lw, parc, spdata);
804706f2543Smrg    return spdata;
805706f2543Smrg}
806706f2543Smrg
807706f2543Smrgstatic void
808706f2543SmrgmiFillWideEllipse(
809706f2543Smrg    DrawablePtr	pDraw,
810706f2543Smrg    GCPtr	pGC,
811706f2543Smrg    xArc	*parc)
812706f2543Smrg{
813706f2543Smrg    DDXPointPtr points;
814706f2543Smrg    DDXPointPtr pts;
815706f2543Smrg    int *widths;
816706f2543Smrg    int *wids;
817706f2543Smrg    miArcSpanData *spdata;
818706f2543Smrg    miArcSpan *span;
819706f2543Smrg    int xorg, yorgu, yorgl;
820706f2543Smrg    int n;
821706f2543Smrg
822706f2543Smrg    yorgu = parc->height + pGC->lineWidth;
823706f2543Smrg    n = (sizeof(int) * 2) * yorgu;
824706f2543Smrg    widths = malloc(n + (sizeof(DDXPointRec) * 2) * yorgu);
825706f2543Smrg    if (!widths)
826706f2543Smrg	return;
827706f2543Smrg    points = (DDXPointPtr)((char *)widths + n);
828706f2543Smrg    spdata = miComputeWideEllipse((int)pGC->lineWidth, parc);
829706f2543Smrg    if (!spdata)
830706f2543Smrg    {
831706f2543Smrg	free(widths);
832706f2543Smrg	return;
833706f2543Smrg    }
834706f2543Smrg    pts = points;
835706f2543Smrg    wids = widths;
836706f2543Smrg    span = spdata->spans;
837706f2543Smrg    xorg = parc->x + (parc->width >> 1);
838706f2543Smrg    yorgu = parc->y + (parc->height >> 1);
839706f2543Smrg    yorgl = yorgu + (parc->height & 1);
840706f2543Smrg    if (pGC->miTranslate)
841706f2543Smrg    {
842706f2543Smrg	xorg += pDraw->x;
843706f2543Smrg	yorgu += pDraw->y;
844706f2543Smrg	yorgl += pDraw->y;
845706f2543Smrg    }
846706f2543Smrg    yorgu -= spdata->k;
847706f2543Smrg    yorgl += spdata->k;
848706f2543Smrg    if (spdata->top)
849706f2543Smrg    {
850706f2543Smrg	pts->x = xorg;
851706f2543Smrg	pts->y = yorgu - 1;
852706f2543Smrg	pts++;
853706f2543Smrg	*wids++ = 1;
854706f2543Smrg	span++;
855706f2543Smrg    }
856706f2543Smrg    for (n = spdata->count1; --n >= 0; )
857706f2543Smrg    {
858706f2543Smrg	pts[0].x = xorg + span->lx;
859706f2543Smrg	pts[0].y = yorgu;
860706f2543Smrg	wids[0] = span->lw;
861706f2543Smrg	pts[1].x = pts[0].x;
862706f2543Smrg	pts[1].y = yorgl;
863706f2543Smrg	wids[1] = wids[0];
864706f2543Smrg	yorgu++;
865706f2543Smrg	yorgl--;
866706f2543Smrg	pts += 2;
867706f2543Smrg	wids += 2;
868706f2543Smrg	span++;
869706f2543Smrg    }
870706f2543Smrg    if (spdata->hole)
871706f2543Smrg    {
872706f2543Smrg	pts[0].x = xorg;
873706f2543Smrg	pts[0].y = yorgl;
874706f2543Smrg	wids[0] = 1;
875706f2543Smrg	pts++;
876706f2543Smrg	wids++;
877706f2543Smrg    }
878706f2543Smrg    for (n = spdata->count2; --n >= 0; )
879706f2543Smrg    {
880706f2543Smrg	pts[0].x = xorg + span->lx;
881706f2543Smrg	pts[0].y = yorgu;
882706f2543Smrg	wids[0] = span->lw;
883706f2543Smrg	pts[1].x = xorg + span->rx;
884706f2543Smrg	pts[1].y = pts[0].y;
885706f2543Smrg	wids[1] = span->rw;
886706f2543Smrg	pts[2].x = pts[0].x;
887706f2543Smrg	pts[2].y = yorgl;
888706f2543Smrg	wids[2] = wids[0];
889706f2543Smrg	pts[3].x = pts[1].x;
890706f2543Smrg	pts[3].y = pts[2].y;
891706f2543Smrg	wids[3] = wids[1];
892706f2543Smrg	yorgu++;
893706f2543Smrg	yorgl--;
894706f2543Smrg	pts += 4;
895706f2543Smrg	wids += 4;
896706f2543Smrg	span++;
897706f2543Smrg    }
898706f2543Smrg    if (spdata->bot)
899706f2543Smrg    {
900706f2543Smrg	if (span->rw <= 0)
901706f2543Smrg	{
902706f2543Smrg	    pts[0].x = xorg + span->lx;
903706f2543Smrg	    pts[0].y = yorgu;
904706f2543Smrg	    wids[0] = span->lw;
905706f2543Smrg	    pts++;
906706f2543Smrg	    wids++;
907706f2543Smrg	}
908706f2543Smrg	else
909706f2543Smrg	{
910706f2543Smrg	    pts[0].x = xorg + span->lx;
911706f2543Smrg	    pts[0].y = yorgu;
912706f2543Smrg	    wids[0] = span->lw;
913706f2543Smrg	    pts[1].x = xorg + span->rx;
914706f2543Smrg	    pts[1].y = pts[0].y;
915706f2543Smrg	    wids[1] = span->rw;
916706f2543Smrg	    pts += 2;
917706f2543Smrg	    wids += 2;
918706f2543Smrg	}
919706f2543Smrg    }
920706f2543Smrg    free(spdata);
921706f2543Smrg    (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE);
922706f2543Smrg
923706f2543Smrg    free(widths);
924706f2543Smrg}
925706f2543Smrg
926706f2543Smrg/*
927706f2543Smrg * miPolyArc strategy:
928706f2543Smrg *
929706f2543Smrg * If arc is zero width and solid, we don't have to worry about the rasterop
930706f2543Smrg * or join styles.  For wide solid circles, we use a fast integer algorithm.
931706f2543Smrg * For wide solid ellipses, we use special case floating point code.
932706f2543Smrg * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
933706f2543Smrg * draw using pGCTo and pDrawTo.  If the raster-op was "tricky," that is,
934706f2543Smrg * if it involves the destination, then we use PushPixels to move the bits
935706f2543Smrg * from the scratch drawable to pDraw. (See the wide line code for a
936706f2543Smrg * fuller explanation of this.)
937706f2543Smrg */
938706f2543Smrg
939706f2543Smrgvoid
940706f2543SmrgmiPolyArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc *parcs)
941706f2543Smrg{
942706f2543Smrg    int		i;
943706f2543Smrg    xArc	*parc;
944706f2543Smrg    int		xMin, xMax, yMin, yMax;
945706f2543Smrg    int		pixmapWidth = 0, pixmapHeight = 0;
946706f2543Smrg    int		xOrg = 0, yOrg = 0;
947706f2543Smrg    int		width;
948706f2543Smrg    Bool	fTricky;
949706f2543Smrg    DrawablePtr	pDrawTo;
950706f2543Smrg    CARD32	fg, bg;
951706f2543Smrg    GCPtr	pGCTo;
952706f2543Smrg    miPolyArcPtr polyArcs;
953706f2543Smrg    int		cap[2], join[2];
954706f2543Smrg    int		iphase;
955706f2543Smrg    int		halfWidth;
956706f2543Smrg
957706f2543Smrg    width = pGC->lineWidth;
958706f2543Smrg    if(width == 0 && pGC->lineStyle == LineSolid)
959706f2543Smrg    {
960706f2543Smrg	for(i = narcs, parc = parcs; --i >= 0; parc++)
961706f2543Smrg	    miArcSegment( pDraw, pGC, *parc,
962706f2543Smrg 	    (miArcFacePtr) 0, (miArcFacePtr) 0 );
963706f2543Smrg	fillSpans (pDraw, pGC);
964706f2543Smrg    }
965706f2543Smrg    else
966706f2543Smrg    {
967706f2543Smrg	if ((pGC->lineStyle == LineSolid) && narcs)
968706f2543Smrg	{
969706f2543Smrg	    while (parcs->width && parcs->height &&
970706f2543Smrg		   (parcs->angle2 >= FULLCIRCLE ||
971706f2543Smrg		    parcs->angle2 <= -FULLCIRCLE))
972706f2543Smrg	    {
973706f2543Smrg		miFillWideEllipse(pDraw, pGC, parcs);
974706f2543Smrg		if (!--narcs)
975706f2543Smrg		    return;
976706f2543Smrg		parcs++;
977706f2543Smrg	    }
978706f2543Smrg	}
979706f2543Smrg
980706f2543Smrg	/* Set up pDrawTo and pGCTo based on the rasterop */
981706f2543Smrg	switch(pGC->alu)
982706f2543Smrg	{
983706f2543Smrg	  case GXclear:		/* 0 */
984706f2543Smrg	  case GXcopy:		/* src */
985706f2543Smrg	  case GXcopyInverted:	/* NOT src */
986706f2543Smrg	  case GXset:		/* 1 */
987706f2543Smrg	    fTricky = FALSE;
988706f2543Smrg	    pDrawTo = pDraw;
989706f2543Smrg	    pGCTo = pGC;
990706f2543Smrg	    break;
991706f2543Smrg	  default:
992706f2543Smrg	    fTricky = TRUE;
993706f2543Smrg
994706f2543Smrg	    /* find bounding box around arcs */
995706f2543Smrg	    xMin = yMin = MAXSHORT;
996706f2543Smrg	    xMax = yMax = MINSHORT;
997706f2543Smrg
998706f2543Smrg	    for(i = narcs, parc = parcs; --i >= 0; parc++)
999706f2543Smrg	    {
1000706f2543Smrg		xMin = min (xMin, parc->x);
1001706f2543Smrg		yMin = min (yMin, parc->y);
1002706f2543Smrg		xMax = max (xMax, (parc->x + (int) parc->width));
1003706f2543Smrg		yMax = max (yMax, (parc->y + (int) parc->height));
1004706f2543Smrg	    }
1005706f2543Smrg
1006706f2543Smrg	    /* expand box to deal with line widths */
1007706f2543Smrg	    halfWidth = (width + 1)/2;
1008706f2543Smrg	    xMin -= halfWidth;
1009706f2543Smrg	    yMin -= halfWidth;
1010706f2543Smrg	    xMax += halfWidth;
1011706f2543Smrg	    yMax += halfWidth;
1012706f2543Smrg
1013706f2543Smrg	    /* compute pixmap size; limit it to size of drawable */
1014706f2543Smrg	    xOrg = max(xMin, 0);
1015706f2543Smrg	    yOrg = max(yMin, 0);
1016706f2543Smrg	    pixmapWidth = min(xMax, pDraw->width) - xOrg;
1017706f2543Smrg	    pixmapHeight = min(yMax, pDraw->height) - yOrg;
1018706f2543Smrg
1019706f2543Smrg	    /* if nothing left, return */
1020706f2543Smrg	    if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return;
1021706f2543Smrg
1022706f2543Smrg	    for(i = narcs, parc = parcs; --i >= 0; parc++)
1023706f2543Smrg	    {
1024706f2543Smrg		parc->x -= xOrg;
1025706f2543Smrg		parc->y -= yOrg;
1026706f2543Smrg	    }
1027706f2543Smrg	    if (pGC->miTranslate)
1028706f2543Smrg	    {
1029706f2543Smrg		xOrg += pDraw->x;
1030706f2543Smrg		yOrg += pDraw->y;
1031706f2543Smrg	    }
1032706f2543Smrg
1033706f2543Smrg	    /* set up scratch GC */
1034706f2543Smrg
1035706f2543Smrg	    pGCTo = GetScratchGC(1, pDraw->pScreen);
1036706f2543Smrg	    if (!pGCTo)
1037706f2543Smrg		return;
1038706f2543Smrg	    {
1039706f2543Smrg		ChangeGCVal gcvals[6];
1040706f2543Smrg		gcvals[0].val = GXcopy;
1041706f2543Smrg		gcvals[1].val = 1;
1042706f2543Smrg		gcvals[2].val = 0;
1043706f2543Smrg		gcvals[3].val = pGC->lineWidth;
1044706f2543Smrg		gcvals[4].val = pGC->capStyle;
1045706f2543Smrg		gcvals[5].val = pGC->joinStyle;
1046706f2543Smrg		ChangeGC(NullClient, pGCTo, GCFunction |
1047706f2543Smrg			GCForeground | GCBackground | GCLineWidth |
1048706f2543Smrg			GCCapStyle | GCJoinStyle, gcvals);
1049706f2543Smrg	    }
1050706f2543Smrg
1051706f2543Smrg	    /* allocate a 1 bit deep pixmap of the appropriate size, and
1052706f2543Smrg	     * validate it */
1053706f2543Smrg	    pDrawTo = (DrawablePtr)(*pDraw->pScreen->CreatePixmap)
1054706f2543Smrg				(pDraw->pScreen, pixmapWidth, pixmapHeight, 1,
1055706f2543Smrg				 CREATE_PIXMAP_USAGE_SCRATCH);
1056706f2543Smrg	    if (!pDrawTo)
1057706f2543Smrg	    {
1058706f2543Smrg		FreeScratchGC(pGCTo);
1059706f2543Smrg		return;
1060706f2543Smrg	    }
1061706f2543Smrg	    ValidateGC(pDrawTo, pGCTo);
1062706f2543Smrg	    miClearDrawable(pDrawTo, pGCTo);
1063706f2543Smrg	}
1064706f2543Smrg
1065706f2543Smrg	fg = pGC->fgPixel;
1066706f2543Smrg	bg = pGC->bgPixel;
1067706f2543Smrg	if ((pGC->fillStyle == FillTiled) ||
1068706f2543Smrg	    (pGC->fillStyle == FillOpaqueStippled))
1069706f2543Smrg	    bg = fg; /* the protocol sez these don't cause color changes */
1070706f2543Smrg
1071706f2543Smrg	polyArcs = miComputeArcs (parcs, narcs, pGC);
1072706f2543Smrg
1073706f2543Smrg	if (!polyArcs)
1074706f2543Smrg	{
1075706f2543Smrg	    if (fTricky) {
1076706f2543Smrg		(*pDraw->pScreen->DestroyPixmap) ((PixmapPtr)pDrawTo);
1077706f2543Smrg		FreeScratchGC (pGCTo);
1078706f2543Smrg	    }
1079706f2543Smrg	    return;
1080706f2543Smrg	}
1081706f2543Smrg
1082706f2543Smrg	cap[0] = cap[1] = 0;
1083706f2543Smrg	join[0] = join[1] = 0;
1084706f2543Smrg	for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
1085706f2543Smrg 	     iphase >= 0;
1086706f2543Smrg	     iphase--)
1087706f2543Smrg	{
1088706f2543Smrg	    ChangeGCVal gcval;
1089706f2543Smrg	    if (iphase == 1) {
1090706f2543Smrg		gcval.val = bg;
1091706f2543Smrg		ChangeGC (NullClient, pGC, GCForeground, &gcval);
1092706f2543Smrg		ValidateGC (pDraw, pGC);
1093706f2543Smrg	    } else if (pGC->lineStyle == LineDoubleDash) {
1094706f2543Smrg		gcval.val = fg;
1095706f2543Smrg		ChangeGC (NullClient, pGC, GCForeground, &gcval);
1096706f2543Smrg		ValidateGC (pDraw, pGC);
1097706f2543Smrg	    }
1098706f2543Smrg	    for (i = 0; i < polyArcs[iphase].narcs; i++) {
1099706f2543Smrg		miArcDataPtr	arcData;
1100706f2543Smrg
1101706f2543Smrg		arcData = &polyArcs[iphase].arcs[i];
1102706f2543Smrg		miArcSegment(pDrawTo, pGCTo, arcData->arc,
1103706f2543Smrg			     &arcData->bounds[RIGHT_END],
1104706f2543Smrg			     &arcData->bounds[LEFT_END]);
1105706f2543Smrg		if (polyArcs[iphase].arcs[i].render) {
1106706f2543Smrg		    fillSpans (pDrawTo, pGCTo);
1107706f2543Smrg		    /*
1108706f2543Smrg		     * don't cap self-joining arcs
1109706f2543Smrg		     */
1110706f2543Smrg		    if (polyArcs[iphase].arcs[i].selfJoin &&
1111706f2543Smrg		        cap[iphase] < polyArcs[iphase].arcs[i].cap)
1112706f2543Smrg		    	cap[iphase]++;
1113706f2543Smrg		    while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
1114706f2543Smrg			int	arcIndex, end;
1115706f2543Smrg			miArcDataPtr	arcData0;
1116706f2543Smrg
1117706f2543Smrg			arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
1118706f2543Smrg			end = polyArcs[iphase].caps[cap[iphase]].end;
1119706f2543Smrg			arcData0 = &polyArcs[iphase].arcs[arcIndex];
1120706f2543Smrg			miArcCap (pDrawTo, pGCTo,
1121706f2543Smrg 				  &arcData0->bounds[end], end,
1122706f2543Smrg				  arcData0->arc.x, arcData0->arc.y,
1123706f2543Smrg				  (double) arcData0->arc.width / 2.0,
1124706f2543Smrg 				  (double) arcData0->arc.height / 2.0);
1125706f2543Smrg			++cap[iphase];
1126706f2543Smrg		    }
1127706f2543Smrg		    while (join[iphase] < polyArcs[iphase].arcs[i].join) {
1128706f2543Smrg			int	arcIndex0, arcIndex1, end0, end1;
1129706f2543Smrg			int	phase0, phase1;
1130706f2543Smrg			miArcDataPtr	arcData0, arcData1;
1131706f2543Smrg			miArcJoinPtr	joinp;
1132706f2543Smrg
1133706f2543Smrg			joinp = &polyArcs[iphase].joins[join[iphase]];
1134706f2543Smrg			arcIndex0 = joinp->arcIndex0;
1135706f2543Smrg			end0 = joinp->end0;
1136706f2543Smrg			arcIndex1 = joinp->arcIndex1;
1137706f2543Smrg			end1 = joinp->end1;
1138706f2543Smrg			phase0 = joinp->phase0;
1139706f2543Smrg			phase1 = joinp->phase1;
1140706f2543Smrg			arcData0 = &polyArcs[phase0].arcs[arcIndex0];
1141706f2543Smrg			arcData1 = &polyArcs[phase1].arcs[arcIndex1];
1142706f2543Smrg			miArcJoin (pDrawTo, pGCTo,
1143706f2543Smrg				  &arcData0->bounds[end0],
1144706f2543Smrg 				  &arcData1->bounds[end1],
1145706f2543Smrg				  arcData0->arc.x, arcData0->arc.y,
1146706f2543Smrg				  (double) arcData0->arc.width / 2.0,
1147706f2543Smrg 				  (double) arcData0->arc.height / 2.0,
1148706f2543Smrg				  arcData1->arc.x, arcData1->arc.y,
1149706f2543Smrg				  (double) arcData1->arc.width / 2.0,
1150706f2543Smrg 				  (double) arcData1->arc.height / 2.0);
1151706f2543Smrg			++join[iphase];
1152706f2543Smrg		    }
1153706f2543Smrg		    if (fTricky) {
1154706f2543Smrg			if (pGC->serialNumber != pDraw->serialNumber)
1155706f2543Smrg			    ValidateGC (pDraw, pGC);
1156706f2543Smrg		    	(*pGC->ops->PushPixels) (pGC, (PixmapPtr)pDrawTo,
1157706f2543Smrg				 pDraw, pixmapWidth, pixmapHeight, xOrg, yOrg);
1158706f2543Smrg			miClearDrawable ((DrawablePtr) pDrawTo, pGCTo);
1159706f2543Smrg		    }
1160706f2543Smrg		}
1161706f2543Smrg	    }
1162706f2543Smrg	}
1163706f2543Smrg	miFreeArcs(polyArcs, pGC);
1164706f2543Smrg
1165706f2543Smrg	if(fTricky)
1166706f2543Smrg	{
1167706f2543Smrg	    (*pGCTo->pScreen->DestroyPixmap)((PixmapPtr)pDrawTo);
1168706f2543Smrg	    FreeScratchGC(pGCTo);
1169706f2543Smrg	}
1170706f2543Smrg    }
1171706f2543Smrg}
1172706f2543Smrg
1173706f2543Smrgstatic double
1174706f2543SmrgangleBetween (SppPointRec center, SppPointRec point1, SppPointRec point2)
1175706f2543Smrg{
1176706f2543Smrg	double	a1, a2, a;
1177706f2543Smrg
1178706f2543Smrg	/*
1179706f2543Smrg	 * reflect from X coordinates back to ellipse
1180706f2543Smrg	 * coordinates -- y increasing upwards
1181706f2543Smrg	 */
1182706f2543Smrg	a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
1183706f2543Smrg	a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
1184706f2543Smrg	a = a2 - a1;
1185706f2543Smrg	if (a <= -180.0)
1186706f2543Smrg		a += 360.0;
1187706f2543Smrg	else if (a > 180.0)
1188706f2543Smrg		a -= 360.0;
1189706f2543Smrg	return a;
1190706f2543Smrg}
1191706f2543Smrg
1192706f2543Smrgstatic void
1193706f2543SmrgtranslateBounds (
1194706f2543Smrg	miArcFacePtr	b,
1195706f2543Smrg	int		x,
1196706f2543Smrg	int		y,
1197706f2543Smrg	double		fx,
1198706f2543Smrg	double		fy)
1199706f2543Smrg{
1200706f2543Smrg	fx += x;
1201706f2543Smrg	fy += y;
1202706f2543Smrg	b->clock.x -= fx;
1203706f2543Smrg	b->clock.y -= fy;
1204706f2543Smrg	b->center.x -= fx;
1205706f2543Smrg	b->center.y -= fy;
1206706f2543Smrg	b->counterClock.x -= fx;
1207706f2543Smrg	b->counterClock.y -= fy;
1208706f2543Smrg}
1209706f2543Smrg
1210706f2543Smrgstatic void
1211706f2543SmrgmiArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
1212706f2543Smrg	  miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
1213706f2543Smrg	  double xFtransLeft, double yFtransLeft,
1214706f2543Smrg	  int xOrgRight, int yOrgRight,
1215706f2543Smrg	  double xFtransRight, double yFtransRight)
1216706f2543Smrg{
1217706f2543Smrg	SppPointRec	center, corner, otherCorner;
1218706f2543Smrg	SppPointRec	poly[5], e;
1219706f2543Smrg	SppPointPtr	pArcPts;
1220706f2543Smrg	int		cpt;
1221706f2543Smrg	SppArcRec	arc;
1222706f2543Smrg	miArcFaceRec	Right, Left;
1223706f2543Smrg	int		polyLen = 0;
1224706f2543Smrg	int		xOrg, yOrg;
1225706f2543Smrg	double		xFtrans, yFtrans;
1226706f2543Smrg	double		a;
1227706f2543Smrg	double		ae, ac2, ec2, bc2, de;
1228706f2543Smrg	double		width;
1229706f2543Smrg
1230706f2543Smrg	xOrg = (xOrgRight + xOrgLeft) / 2;
1231706f2543Smrg	yOrg = (yOrgRight + yOrgLeft) / 2;
1232706f2543Smrg	xFtrans = (xFtransLeft + xFtransRight) / 2;
1233706f2543Smrg	yFtrans = (yFtransLeft + yFtransRight) / 2;
1234706f2543Smrg	Right = *pRight;
1235706f2543Smrg	translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
1236706f2543Smrg				 xFtrans - xFtransRight, yFtrans - yFtransRight);
1237706f2543Smrg	Left = *pLeft;
1238706f2543Smrg	translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
1239706f2543Smrg				 xFtrans - xFtransLeft, yFtrans - yFtransLeft);
1240706f2543Smrg	pRight = &Right;
1241706f2543Smrg	pLeft = &Left;
1242706f2543Smrg
1243706f2543Smrg	if (pRight->clock.x == pLeft->counterClock.x &&
1244706f2543Smrg	    pRight->clock.y == pLeft->counterClock.y)
1245706f2543Smrg		return;
1246706f2543Smrg	center = pRight->center;
1247706f2543Smrg	if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
1248706f2543Smrg 	    && a <= 180.0)
1249706f2543Smrg 	{
1250706f2543Smrg		corner = pRight->clock;
1251706f2543Smrg		otherCorner = pLeft->counterClock;
1252706f2543Smrg	} else {
1253706f2543Smrg		a = angleBetween (center, pLeft->clock, pRight->counterClock);
1254706f2543Smrg		corner = pLeft->clock;
1255706f2543Smrg		otherCorner = pRight->counterClock;
1256706f2543Smrg	}
1257706f2543Smrg	switch (pGC->joinStyle) {
1258706f2543Smrg	case JoinRound:
1259706f2543Smrg		width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
1260706f2543Smrg
1261706f2543Smrg		arc.x = center.x - width/2;
1262706f2543Smrg		arc.y = center.y - width/2;
1263706f2543Smrg		arc.width = width;
1264706f2543Smrg		arc.height = width;
1265706f2543Smrg		arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
1266706f2543Smrg		arc.angle2 = a;
1267706f2543Smrg		pArcPts = malloc(3 * sizeof (SppPointRec));
1268706f2543Smrg		if (!pArcPts)
1269706f2543Smrg		    return;
1270706f2543Smrg		pArcPts[0].x = otherCorner.x;
1271706f2543Smrg		pArcPts[0].y = otherCorner.y;
1272706f2543Smrg		pArcPts[1].x = center.x;
1273706f2543Smrg		pArcPts[1].y = center.y;
1274706f2543Smrg		pArcPts[2].x = corner.x;
1275706f2543Smrg		pArcPts[2].y = corner.y;
1276706f2543Smrg		if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
1277706f2543Smrg		{
1278706f2543Smrg			/* by drawing with miFillSppPoly and setting the endpoints of the arc
1279706f2543Smrg			 * to be the corners, we assure that the cap will meet up with the
1280706f2543Smrg			 * rest of the line */
1281706f2543Smrg			miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
1282706f2543Smrg		}
1283706f2543Smrg		free(pArcPts);
1284706f2543Smrg		return;
1285706f2543Smrg	case JoinMiter:
1286706f2543Smrg		/*
1287706f2543Smrg		 * don't miter arcs with less than 11 degrees between them
1288706f2543Smrg		 */
1289706f2543Smrg		if (a < 169.0) {
1290706f2543Smrg			poly[0] = corner;
1291706f2543Smrg			poly[1] = center;
1292706f2543Smrg			poly[2] = otherCorner;
1293706f2543Smrg			bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
1294706f2543Smrg			      (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
1295706f2543Smrg			ec2 = bc2 / 4;
1296706f2543Smrg			ac2 = (corner.x - center.x) * (corner.x - center.x) +
1297706f2543Smrg			      (corner.y - center.y) * (corner.y - center.y);
1298706f2543Smrg			ae = sqrt (ac2 - ec2);
1299706f2543Smrg			de = ec2 / ae;
1300706f2543Smrg			e.x = (corner.x + otherCorner.x) / 2;
1301706f2543Smrg			e.y = (corner.y + otherCorner.y) / 2;
1302706f2543Smrg			poly[3].x = e.x + de * (e.x - center.x) / ae;
1303706f2543Smrg			poly[3].y = e.y + de * (e.y - center.y) / ae;
1304706f2543Smrg			poly[4] = corner;
1305706f2543Smrg			polyLen = 5;
1306706f2543Smrg			break;
1307706f2543Smrg		}
1308706f2543Smrg	case JoinBevel:
1309706f2543Smrg		poly[0] = corner;
1310706f2543Smrg		poly[1] = center;
1311706f2543Smrg		poly[2] = otherCorner;
1312706f2543Smrg		poly[3] = corner;
1313706f2543Smrg		polyLen = 4;
1314706f2543Smrg		break;
1315706f2543Smrg	}
1316706f2543Smrg	miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
1317706f2543Smrg}
1318706f2543Smrg
1319706f2543Smrg/*ARGSUSED*/
1320706f2543Smrgstatic void
1321706f2543SmrgmiArcCap (
1322706f2543Smrg	DrawablePtr	pDraw,
1323706f2543Smrg	GCPtr		pGC,
1324706f2543Smrg	miArcFacePtr	pFace,
1325706f2543Smrg	int		end,
1326706f2543Smrg	int		xOrg,
1327706f2543Smrg	int		yOrg,
1328706f2543Smrg	double		xFtrans,
1329706f2543Smrg	double		yFtrans)
1330706f2543Smrg{
1331706f2543Smrg	SppPointRec	corner, otherCorner, center, endPoint, poly[5];
1332706f2543Smrg
1333706f2543Smrg	corner = pFace->clock;
1334706f2543Smrg	otherCorner = pFace->counterClock;
1335706f2543Smrg	center = pFace->center;
1336706f2543Smrg	switch (pGC->capStyle) {
1337706f2543Smrg	case CapProjecting:
1338706f2543Smrg		poly[0].x = otherCorner.x;
1339706f2543Smrg		poly[0].y = otherCorner.y;
1340706f2543Smrg		poly[1].x = corner.x;
1341706f2543Smrg		poly[1].y = corner.y;
1342706f2543Smrg		poly[2].x = corner.x -
1343706f2543Smrg 				(center.y - corner.y);
1344706f2543Smrg		poly[2].y = corner.y +
1345706f2543Smrg 				(center.x - corner.x);
1346706f2543Smrg		poly[3].x = otherCorner.x -
1347706f2543Smrg 				(otherCorner.y - center.y);
1348706f2543Smrg		poly[3].y = otherCorner.y +
1349706f2543Smrg 				(otherCorner.x - center.x);
1350706f2543Smrg		poly[4].x = otherCorner.x;
1351706f2543Smrg		poly[4].y = otherCorner.y;
1352706f2543Smrg		miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
1353706f2543Smrg		break;
1354706f2543Smrg	case CapRound:
1355706f2543Smrg		/*
1356706f2543Smrg		 * miRoundCap just needs these to be unequal.
1357706f2543Smrg		 */
1358706f2543Smrg		endPoint = center;
1359706f2543Smrg		endPoint.x = endPoint.x + 100;
1360706f2543Smrg		miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
1361706f2543Smrg			    -xOrg, -yOrg, xFtrans, yFtrans);
1362706f2543Smrg		break;
1363706f2543Smrg	}
1364706f2543Smrg}
1365706f2543Smrg
1366706f2543Smrg/* MIROUNDCAP -- a private helper function
1367706f2543Smrg * Put Rounded cap on end. pCenter is the center of this end of the line
1368706f2543Smrg * pEnd is the center of the other end of the line. pCorner is one of the
1369706f2543Smrg * two corners at this end of the line.
1370706f2543Smrg * NOTE:  pOtherCorner must be counter-clockwise from pCorner.
1371706f2543Smrg */
1372706f2543Smrg/*ARGSUSED*/
1373706f2543Smrgstatic void
1374706f2543SmrgmiRoundCap(
1375706f2543Smrg    DrawablePtr	pDraw,
1376706f2543Smrg    GCPtr	pGC,
1377706f2543Smrg    SppPointRec	pCenter,
1378706f2543Smrg    SppPointRec	pEnd,
1379706f2543Smrg    SppPointRec	pCorner,
1380706f2543Smrg    SppPointRec	pOtherCorner,
1381706f2543Smrg    int		fLineEnd,
1382706f2543Smrg    int		xOrg,
1383706f2543Smrg    int		yOrg,
1384706f2543Smrg    double	xFtrans,
1385706f2543Smrg    double	yFtrans)
1386706f2543Smrg{
1387706f2543Smrg    int		cpt;
1388706f2543Smrg    double	width;
1389706f2543Smrg    SppArcRec	arc;
1390706f2543Smrg    SppPointPtr	pArcPts;
1391706f2543Smrg
1392706f2543Smrg    width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
1393706f2543Smrg
1394706f2543Smrg    arc.x = pCenter.x - width/2;
1395706f2543Smrg    arc.y = pCenter.y - width/2;
1396706f2543Smrg    arc.width = width;
1397706f2543Smrg    arc.height = width;
1398706f2543Smrg    arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x);
1399706f2543Smrg    if(PTISEQUAL(pCenter, pEnd))
1400706f2543Smrg	arc.angle2 = - 180.0;
1401706f2543Smrg    else {
1402706f2543Smrg	arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
1403706f2543Smrg	if (arc.angle2 < 0)
1404706f2543Smrg	    arc.angle2 += 360.0;
1405706f2543Smrg    }
1406706f2543Smrg    pArcPts = (SppPointPtr) NULL;
1407706f2543Smrg    if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
1408706f2543Smrg    {
1409706f2543Smrg	/* by drawing with miFillSppPoly and setting the endpoints of the arc
1410706f2543Smrg	 * to be the corners, we assure that the cap will meet up with the
1411706f2543Smrg	 * rest of the line */
1412706f2543Smrg	miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
1413706f2543Smrg    }
1414706f2543Smrg    free(pArcPts);
1415706f2543Smrg}
1416706f2543Smrg
1417706f2543Smrg/*
1418706f2543Smrg * To avoid inaccuracy at the cardinal points, use trig functions
1419706f2543Smrg * which are exact for those angles
1420706f2543Smrg */
1421706f2543Smrg
1422706f2543Smrg#ifndef M_PI
1423706f2543Smrg#define M_PI	3.14159265358979323846
1424706f2543Smrg#endif
1425706f2543Smrg#ifndef M_PI_2
1426706f2543Smrg#define M_PI_2	1.57079632679489661923
1427706f2543Smrg#endif
1428706f2543Smrg
1429706f2543Smrg# define Dsin(d)	((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
1430706f2543Smrg# define Dcos(d)	((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
1431706f2543Smrg# define mod(a,b)	((a) >= 0 ? (a) % (b) : (b) - (-(a)) % (b))
1432706f2543Smrg
1433706f2543Smrgstatic double
1434706f2543SmrgmiDcos (double a)
1435706f2543Smrg{
1436706f2543Smrg	int	i;
1437706f2543Smrg
1438706f2543Smrg	if (floor (a/90) == a/90) {
1439706f2543Smrg		i = (int) (a/90.0);
1440706f2543Smrg		switch (mod (i, 4)) {
1441706f2543Smrg		case 0: return 1;
1442706f2543Smrg		case 1: return 0;
1443706f2543Smrg		case 2: return -1;
1444706f2543Smrg		case 3: return 0;
1445706f2543Smrg		}
1446706f2543Smrg	}
1447706f2543Smrg	return cos (a * M_PI / 180.0);
1448706f2543Smrg}
1449706f2543Smrg
1450706f2543Smrgstatic double
1451706f2543SmrgmiDsin (double a)
1452706f2543Smrg{
1453706f2543Smrg	int	i;
1454706f2543Smrg
1455706f2543Smrg	if (floor (a/90) == a/90) {
1456706f2543Smrg		i = (int) (a/90.0);
1457706f2543Smrg		switch (mod (i, 4)) {
1458706f2543Smrg		case 0: return 0;
1459706f2543Smrg		case 1: return 1;
1460706f2543Smrg		case 2: return 0;
1461706f2543Smrg		case 3: return -1;
1462706f2543Smrg		}
1463706f2543Smrg	}
1464706f2543Smrg	return sin (a * M_PI / 180.0);
1465706f2543Smrg}
1466706f2543Smrg
1467706f2543Smrgstatic double
1468706f2543SmrgmiDasin (double v)
1469706f2543Smrg{
1470706f2543Smrg    if (v == 0)
1471706f2543Smrg	return 0.0;
1472706f2543Smrg    if (v == 1.0)
1473706f2543Smrg	return 90.0;
1474706f2543Smrg    if (v == -1.0)
1475706f2543Smrg	return -90.0;
1476706f2543Smrg    return asin(v) * (180.0 / M_PI);
1477706f2543Smrg}
1478706f2543Smrg
1479706f2543Smrgstatic double
1480706f2543SmrgmiDatan2 (double dy, double dx)
1481706f2543Smrg{
1482706f2543Smrg    if (dy == 0) {
1483706f2543Smrg	if (dx >= 0)
1484706f2543Smrg	    return 0.0;
1485706f2543Smrg	return 180.0;
1486706f2543Smrg    } else if (dx == 0) {
1487706f2543Smrg	if (dy > 0)
1488706f2543Smrg	    return 90.0;
1489706f2543Smrg	return -90.0;
1490706f2543Smrg    } else if (Fabs (dy) == Fabs (dx)) {
1491706f2543Smrg	if (dy > 0) {
1492706f2543Smrg	    if (dx > 0)
1493706f2543Smrg		return 45.0;
1494706f2543Smrg	    return 135.0;
1495706f2543Smrg	} else {
1496706f2543Smrg	    if (dx > 0)
1497706f2543Smrg		return 315.0;
1498706f2543Smrg	    return 225.0;
1499706f2543Smrg	}
1500706f2543Smrg    } else {
1501706f2543Smrg	return atan2 (dy, dx) * (180.0 / M_PI);
1502706f2543Smrg    }
1503706f2543Smrg}
1504706f2543Smrg
1505706f2543Smrg/* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
1506706f2543Smrg * routine for filled arc and line (round cap) code.
1507706f2543Smrg * Returns the number of points in the arc.  Note that it takes a pointer
1508706f2543Smrg * to a pointer to where it should put the points and an index (cpt).
1509706f2543Smrg * This procedure allocates the space necessary to fit the arc points.
1510706f2543Smrg * Sometimes it's convenient for those points to be at the end of an existing
1511706f2543Smrg * array. (For example, if we want to leave a spare point to make sectors
1512706f2543Smrg * instead of segments.)  So we pass in the malloc()ed chunk that contains the
1513706f2543Smrg * array and an index saying where we should start stashing the points.
1514706f2543Smrg * If there isn't an array already, we just pass in a null pointer and
1515706f2543Smrg * count on realloc() to handle the null pointer correctly.
1516706f2543Smrg */
1517706f2543Smrgstatic int
1518706f2543SmrgmiGetArcPts(
1519706f2543Smrg    SppArcPtr	parc,	/* points to an arc */
1520706f2543Smrg    int		cpt,	/* number of points already in arc list */
1521706f2543Smrg    SppPointPtr	*ppPts) /* pointer to pointer to arc-list -- modified */
1522706f2543Smrg{
1523706f2543Smrg    double 	st,	/* Start Theta, start angle */
1524706f2543Smrg                et,	/* End Theta, offset from start theta */
1525706f2543Smrg		dt,	/* Delta Theta, angle to sweep ellipse */
1526706f2543Smrg		cdt,	/* Cos Delta Theta, actually 2 cos(dt) */
1527706f2543Smrg    		x0, y0,	/* the recurrence formula needs two points to start */
1528706f2543Smrg		x1, y1,
1529706f2543Smrg		x2, y2, /* this will be the new point generated */
1530706f2543Smrg		xc, yc; /* the center point */
1531706f2543Smrg    int		count, i;
1532706f2543Smrg    SppPointPtr	poly;
1533706f2543Smrg
1534706f2543Smrg    /* The spec says that positive angles indicate counterclockwise motion.
1535706f2543Smrg     * Given our coordinate system (with 0,0 in the upper left corner),
1536706f2543Smrg     * the screen appears flipped in Y.  The easiest fix is to negate the
1537706f2543Smrg     * angles given */
1538706f2543Smrg
1539706f2543Smrg    st = - parc->angle1;
1540706f2543Smrg
1541706f2543Smrg    et = - parc->angle2;
1542706f2543Smrg
1543706f2543Smrg    /* Try to get a delta theta that is within 1/2 pixel.  Then adjust it
1544706f2543Smrg     * so that it divides evenly into the total.
1545706f2543Smrg     * I'm just using cdt 'cause I'm lazy.
1546706f2543Smrg     */
1547706f2543Smrg    cdt = parc->width;
1548706f2543Smrg    if (parc->height > cdt)
1549706f2543Smrg	cdt = parc->height;
1550706f2543Smrg    cdt /= 2.0;
1551706f2543Smrg    if(cdt <= 0)
1552706f2543Smrg	return 0;
1553706f2543Smrg    if (cdt < 1.0)
1554706f2543Smrg	cdt = 1.0;
1555706f2543Smrg    dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
1556706f2543Smrg    count = et/dt;
1557706f2543Smrg    count = abs(count) + 1;
1558706f2543Smrg    dt = et/count;
1559706f2543Smrg    count++;
1560706f2543Smrg
1561706f2543Smrg    cdt = 2 * miDcos(dt);
1562706f2543Smrg    if (!(poly = (SppPointPtr) realloc((pointer)*ppPts,
1563706f2543Smrg					(cpt + count) * sizeof(SppPointRec))))
1564706f2543Smrg	return 0;
1565706f2543Smrg    *ppPts = poly;
1566706f2543Smrg
1567706f2543Smrg    xc = parc->width/2.0;		/* store half width and half height */
1568706f2543Smrg    yc = parc->height/2.0;
1569706f2543Smrg
1570706f2543Smrg    x0 = xc * miDcos(st);
1571706f2543Smrg    y0 = yc * miDsin(st);
1572706f2543Smrg    x1 = xc * miDcos(st + dt);
1573706f2543Smrg    y1 = yc * miDsin(st + dt);
1574706f2543Smrg    xc += parc->x;		/* by adding initial point, these become */
1575706f2543Smrg    yc += parc->y;		/* the center point */
1576706f2543Smrg
1577706f2543Smrg    poly[cpt].x = (xc + x0);
1578706f2543Smrg    poly[cpt].y = (yc + y0);
1579706f2543Smrg    poly[cpt + 1].x = (xc + x1);
1580706f2543Smrg    poly[cpt + 1].y = (yc + y1);
1581706f2543Smrg
1582706f2543Smrg    for(i = 2; i < count; i++)
1583706f2543Smrg    {
1584706f2543Smrg	x2 = cdt * x1 - x0;
1585706f2543Smrg	y2 = cdt * y1 - y0;
1586706f2543Smrg
1587706f2543Smrg 	poly[cpt + i].x = (xc + x2);
1588706f2543Smrg 	poly[cpt + i].y = (yc + y2);
1589706f2543Smrg
1590706f2543Smrg	x0 = x1; y0 = y1;
1591706f2543Smrg	x1 = x2; y1 = y2;
1592706f2543Smrg    }
1593706f2543Smrg    /* adjust the last point */
1594706f2543Smrg    if (fabs(parc->angle2) >= 360.0)
1595706f2543Smrg	poly[cpt +i -1] = poly[0];
1596706f2543Smrg    else {
1597706f2543Smrg	poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
1598706f2543Smrg	poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
1599706f2543Smrg    }
1600706f2543Smrg
1601706f2543Smrg    return count;
1602706f2543Smrg}
1603706f2543Smrg
1604706f2543Smrgstruct arcData {
1605706f2543Smrg	double	x0, y0, x1, y1;
1606706f2543Smrg	int	selfJoin;
1607706f2543Smrg};
1608706f2543Smrg
1609706f2543Smrg# define ADD_REALLOC_STEP	20
1610706f2543Smrg
1611706f2543Smrgstatic void
1612706f2543SmrgaddCap (
1613706f2543Smrg	miArcCapPtr	*capsp,
1614706f2543Smrg	int		*ncapsp,
1615706f2543Smrg	int		*sizep,
1616706f2543Smrg	int		end,
1617706f2543Smrg	int		arcIndex)
1618706f2543Smrg{
1619706f2543Smrg	int newsize;
1620706f2543Smrg	miArcCapPtr	cap;
1621706f2543Smrg
1622706f2543Smrg	if (*ncapsp == *sizep)
1623706f2543Smrg	{
1624706f2543Smrg	    newsize = *sizep + ADD_REALLOC_STEP;
1625706f2543Smrg	    cap = (miArcCapPtr) realloc(*capsp,
1626706f2543Smrg					  newsize * sizeof (**capsp));
1627706f2543Smrg	    if (!cap)
1628706f2543Smrg		return;
1629706f2543Smrg	    *sizep = newsize;
1630706f2543Smrg	    *capsp = cap;
1631706f2543Smrg	}
1632706f2543Smrg	cap = &(*capsp)[*ncapsp];
1633706f2543Smrg	cap->end = end;
1634706f2543Smrg	cap->arcIndex = arcIndex;
1635706f2543Smrg	++*ncapsp;
1636706f2543Smrg}
1637706f2543Smrg
1638706f2543Smrgstatic void
1639706f2543SmrgaddJoin (
1640706f2543Smrg	miArcJoinPtr	*joinsp,
1641706f2543Smrg	int		*njoinsp,
1642706f2543Smrg	int		*sizep,
1643706f2543Smrg	int		end0,
1644706f2543Smrg	int		index0,
1645706f2543Smrg	int		phase0,
1646706f2543Smrg	int		end1,
1647706f2543Smrg	int		index1,
1648706f2543Smrg	int		phase1)
1649706f2543Smrg{
1650706f2543Smrg	int newsize;
1651706f2543Smrg	miArcJoinPtr	join;
1652706f2543Smrg
1653706f2543Smrg	if (*njoinsp == *sizep)
1654706f2543Smrg	{
1655706f2543Smrg	    newsize = *sizep + ADD_REALLOC_STEP;
1656706f2543Smrg	    join = (miArcJoinPtr) realloc(*joinsp,
1657706f2543Smrg					    newsize * sizeof (**joinsp));
1658706f2543Smrg	    if (!join)
1659706f2543Smrg		return;
1660706f2543Smrg	    *sizep = newsize;
1661706f2543Smrg	    *joinsp = join;
1662706f2543Smrg	}
1663706f2543Smrg	join = &(*joinsp)[*njoinsp];
1664706f2543Smrg	join->end0 = end0;
1665706f2543Smrg	join->arcIndex0 = index0;
1666706f2543Smrg	join->phase0 = phase0;
1667706f2543Smrg	join->end1 = end1;
1668706f2543Smrg	join->arcIndex1 = index1;
1669706f2543Smrg	join->phase1 = phase1;
1670706f2543Smrg	++*njoinsp;
1671706f2543Smrg}
1672706f2543Smrg
1673706f2543Smrgstatic miArcDataPtr
1674706f2543SmrgaddArc (
1675706f2543Smrg	miArcDataPtr	*arcsp,
1676706f2543Smrg	int		*narcsp,
1677706f2543Smrg	int		*sizep,
1678706f2543Smrg	xArc		*xarc)
1679706f2543Smrg{
1680706f2543Smrg	int newsize;
1681706f2543Smrg	miArcDataPtr	arc;
1682706f2543Smrg
1683706f2543Smrg	if (*narcsp == *sizep)
1684706f2543Smrg	{
1685706f2543Smrg	    newsize = *sizep + ADD_REALLOC_STEP;
1686706f2543Smrg	    arc = (miArcDataPtr) realloc(*arcsp,
1687706f2543Smrg					   newsize * sizeof (**arcsp));
1688706f2543Smrg	    if (!arc)
1689706f2543Smrg		return NULL;
1690706f2543Smrg	    *sizep = newsize;
1691706f2543Smrg	    *arcsp = arc;
1692706f2543Smrg	}
1693706f2543Smrg	arc = &(*arcsp)[*narcsp];
1694706f2543Smrg	arc->arc = *xarc;
1695706f2543Smrg	++*narcsp;
1696706f2543Smrg	return arc;
1697706f2543Smrg}
1698706f2543Smrg
1699706f2543Smrgstatic void
1700706f2543SmrgmiFreeArcs(
1701706f2543Smrg    miPolyArcPtr arcs,
1702706f2543Smrg    GCPtr pGC)
1703706f2543Smrg{
1704706f2543Smrg	int iphase;
1705706f2543Smrg
1706706f2543Smrg	for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
1707706f2543Smrg 	     iphase >= 0;
1708706f2543Smrg	     iphase--)
1709706f2543Smrg	{
1710706f2543Smrg	    if (arcs[iphase].narcs > 0)
1711706f2543Smrg		free(arcs[iphase].arcs);
1712706f2543Smrg	    if (arcs[iphase].njoins > 0)
1713706f2543Smrg		free(arcs[iphase].joins);
1714706f2543Smrg	    if (arcs[iphase].ncaps > 0)
1715706f2543Smrg		free(arcs[iphase].caps);
1716706f2543Smrg	}
1717706f2543Smrg	free(arcs);
1718706f2543Smrg}
1719706f2543Smrg
1720706f2543Smrg/*
1721706f2543Smrg * map angles to radial distance.  This only deals with the first quadrant
1722706f2543Smrg */
1723706f2543Smrg
1724706f2543Smrg/*
1725706f2543Smrg * a polygonal approximation to the arc for computing arc lengths
1726706f2543Smrg */
1727706f2543Smrg
1728706f2543Smrg# define DASH_MAP_SIZE	91
1729706f2543Smrg
1730706f2543Smrg# define dashIndexToAngle(di)	((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
1731706f2543Smrg# define xAngleToDashIndex(xa)	((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
1732706f2543Smrg# define dashIndexToXAngle(di)	((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
1733706f2543Smrg# define dashXAngleStep	(((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
1734706f2543Smrg
1735706f2543Smrgtypedef struct {
1736706f2543Smrg	double	map[DASH_MAP_SIZE];
1737706f2543Smrg} dashMap;
1738706f2543Smrg
1739706f2543Smrgstatic int computeAngleFromPath(int startAngle, int endAngle, dashMap *map,
1740706f2543Smrg				int *lenp, int backwards);
1741706f2543Smrg
1742706f2543Smrgstatic void
1743706f2543SmrgcomputeDashMap (
1744706f2543Smrg	xArc	*arcp,
1745706f2543Smrg	dashMap	*map)
1746706f2543Smrg{
1747706f2543Smrg	int	di;
1748706f2543Smrg	double	a, x, y, prevx = 0.0, prevy = 0.0, dist;
1749706f2543Smrg
1750706f2543Smrg	for (di = 0; di < DASH_MAP_SIZE; di++) {
1751706f2543Smrg		a = dashIndexToAngle (di);
1752706f2543Smrg		x = ((double) arcp->width / 2.0) * miDcos (a);
1753706f2543Smrg		y = ((double) arcp->height / 2.0) * miDsin (a);
1754706f2543Smrg		if (di == 0) {
1755706f2543Smrg			map->map[di] = 0.0;
1756706f2543Smrg		} else {
1757706f2543Smrg			dist = hypot (x - prevx, y - prevy);
1758706f2543Smrg			map->map[di] = map->map[di - 1] + dist;
1759706f2543Smrg		}
1760706f2543Smrg		prevx = x;
1761706f2543Smrg		prevy = y;
1762706f2543Smrg	}
1763706f2543Smrg}
1764706f2543Smrg
1765706f2543Smrgtypedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
1766706f2543Smrg
1767706f2543Smrg/* this routine is a bit gory */
1768706f2543Smrg
1769706f2543Smrgstatic miPolyArcPtr
1770706f2543SmrgmiComputeArcs (
1771706f2543Smrg	xArc	*parcs,
1772706f2543Smrg	int	narcs,
1773706f2543Smrg	GCPtr	pGC)
1774706f2543Smrg{
1775706f2543Smrg	int		isDashed, isDoubleDash;
1776706f2543Smrg	int		dashOffset;
1777706f2543Smrg	miPolyArcPtr	arcs;
1778706f2543Smrg	int		start, i, j, k = 0, nexti, nextk = 0;
1779706f2543Smrg	int		joinSize[2];
1780706f2543Smrg	int		capSize[2];
1781706f2543Smrg	int		arcSize[2];
1782706f2543Smrg	int		angle2;
1783706f2543Smrg	double		a0, a1;
1784706f2543Smrg	struct arcData	*data;
1785706f2543Smrg	miArcDataPtr	arc;
1786706f2543Smrg	xArc		xarc;
1787706f2543Smrg	int		iphase, prevphase = 0, joinphase;
1788706f2543Smrg	int		arcsJoin;
1789706f2543Smrg	int		selfJoin;
1790706f2543Smrg
1791706f2543Smrg	int		iDash = 0, dashRemaining = 0;
1792706f2543Smrg	int		iDashStart = 0, dashRemainingStart = 0, iphaseStart;
1793706f2543Smrg	int		startAngle, spanAngle, endAngle, backwards = 0;
1794706f2543Smrg	int		prevDashAngle, dashAngle;
1795706f2543Smrg	dashMap		map;
1796706f2543Smrg
1797706f2543Smrg	isDashed = !(pGC->lineStyle == LineSolid);
1798706f2543Smrg	isDoubleDash = (pGC->lineStyle == LineDoubleDash);
1799706f2543Smrg	dashOffset = pGC->dashOffset;
1800706f2543Smrg
1801706f2543Smrg	data = malloc(narcs * sizeof (struct arcData));
1802706f2543Smrg	if (!data)
1803706f2543Smrg	    return NULL;
1804706f2543Smrg	arcs = malloc(sizeof (*arcs) * (isDoubleDash ? 2 : 1));
1805706f2543Smrg	if (!arcs)
1806706f2543Smrg	{
1807706f2543Smrg	    free(data);
1808706f2543Smrg	    return NULL;
1809706f2543Smrg	}
1810706f2543Smrg	for (i = 0; i < narcs; i++) {
1811706f2543Smrg		a0 = todeg (parcs[i].angle1);
1812706f2543Smrg		angle2 = parcs[i].angle2;
1813706f2543Smrg		if (angle2 > FULLCIRCLE)
1814706f2543Smrg			angle2 = FULLCIRCLE;
1815706f2543Smrg		else if (angle2 < -FULLCIRCLE)
1816706f2543Smrg			angle2 = -FULLCIRCLE;
1817706f2543Smrg		data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
1818706f2543Smrg		a1 = todeg (parcs[i].angle1 + angle2);
1819706f2543Smrg		data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
1820706f2543Smrg		data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
1821706f2543Smrg		data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
1822706f2543Smrg		data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
1823706f2543Smrg	}
1824706f2543Smrg
1825706f2543Smrg	for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
1826706f2543Smrg		arcs[iphase].njoins = 0;
1827706f2543Smrg		arcs[iphase].joins = 0;
1828706f2543Smrg		joinSize[iphase] = 0;
1829706f2543Smrg
1830706f2543Smrg		arcs[iphase].ncaps = 0;
1831706f2543Smrg		arcs[iphase].caps = 0;
1832706f2543Smrg		capSize[iphase] = 0;
1833706f2543Smrg
1834706f2543Smrg		arcs[iphase].narcs = 0;
1835706f2543Smrg		arcs[iphase].arcs = 0;
1836706f2543Smrg		arcSize[iphase] = 0;
1837706f2543Smrg	}
1838706f2543Smrg
1839706f2543Smrg	iphase = 0;
1840706f2543Smrg	if (isDashed) {
1841706f2543Smrg		iDash = 0;
1842706f2543Smrg		dashRemaining = pGC->dash[0];
1843706f2543Smrg	 	while (dashOffset > 0) {
1844706f2543Smrg			if (dashOffset >= dashRemaining) {
1845706f2543Smrg				dashOffset -= dashRemaining;
1846706f2543Smrg				iphase = iphase ? 0 : 1;
1847706f2543Smrg				iDash++;
1848706f2543Smrg				if (iDash == pGC->numInDashList)
1849706f2543Smrg				    iDash = 0;
1850706f2543Smrg				dashRemaining = pGC->dash[iDash];
1851706f2543Smrg			} else {
1852706f2543Smrg				dashRemaining -= dashOffset;
1853706f2543Smrg				dashOffset = 0;
1854706f2543Smrg			}
1855706f2543Smrg		}
1856706f2543Smrg		iDashStart = iDash;
1857706f2543Smrg		dashRemainingStart = dashRemaining;
1858706f2543Smrg	}
1859706f2543Smrg	iphaseStart = iphase;
1860706f2543Smrg
1861706f2543Smrg	for (i = narcs - 1; i >= 0; i--) {
1862706f2543Smrg		j = i + 1;
1863706f2543Smrg		if (j == narcs)
1864706f2543Smrg			j = 0;
1865706f2543Smrg		if (data[i].selfJoin || i == j ||
1866706f2543Smrg		     (UNEQUAL (data[i].x1, data[j].x0) ||
1867706f2543Smrg		      UNEQUAL (data[i].y1, data[j].y0)))
1868706f2543Smrg 		{
1869706f2543Smrg			if (iphase == 0 || isDoubleDash)
1870706f2543Smrg				addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
1871706f2543Smrg	 				&capSize[iphase], RIGHT_END, 0);
1872706f2543Smrg			break;
1873706f2543Smrg		}
1874706f2543Smrg	}
1875706f2543Smrg	start = i + 1;
1876706f2543Smrg	if (start == narcs)
1877706f2543Smrg		start = 0;
1878706f2543Smrg	i = start;
1879706f2543Smrg	for (;;) {
1880706f2543Smrg		j = i + 1;
1881706f2543Smrg		if (j == narcs)
1882706f2543Smrg			j = 0;
1883706f2543Smrg		nexti = i+1;
1884706f2543Smrg		if (nexti == narcs)
1885706f2543Smrg			nexti = 0;
1886706f2543Smrg		if (isDashed) {
1887706f2543Smrg			/*
1888706f2543Smrg			** deal with dashed arcs.  Use special rules for certain 0 area arcs.
1889706f2543Smrg			** Presumably, the other 0 area arcs still aren't done right.
1890706f2543Smrg			*/
1891706f2543Smrg			arcTypes	arcType = OTHER;
1892706f2543Smrg			CARD16		thisLength;
1893706f2543Smrg
1894706f2543Smrg			if (parcs[i].height == 0
1895706f2543Smrg			    && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
1896706f2543Smrg			    && parcs[i].angle2 == 0x2d00)
1897706f2543Smrg				arcType = HORIZONTAL;
1898706f2543Smrg			else if (parcs[i].width == 0
1899706f2543Smrg			    && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
1900706f2543Smrg			    && parcs[i].angle2 == 0x2d00)
1901706f2543Smrg				arcType = VERTICAL;
1902706f2543Smrg			if (arcType == OTHER) {
1903706f2543Smrg				/*
1904706f2543Smrg				 * precompute an approximation map
1905706f2543Smrg				 */
1906706f2543Smrg				computeDashMap (&parcs[i], &map);
1907706f2543Smrg				/*
1908706f2543Smrg				 * compute each individual dash segment using the path
1909706f2543Smrg				 * length function
1910706f2543Smrg				 */
1911706f2543Smrg				startAngle = parcs[i].angle1;
1912706f2543Smrg				spanAngle = parcs[i].angle2;
1913706f2543Smrg				if (spanAngle > FULLCIRCLE)
1914706f2543Smrg					spanAngle = FULLCIRCLE;
1915706f2543Smrg				else if (spanAngle < -FULLCIRCLE)
1916706f2543Smrg					spanAngle = -FULLCIRCLE;
1917706f2543Smrg				if (startAngle < 0)
1918706f2543Smrg					startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
1919706f2543Smrg				if (startAngle >= FULLCIRCLE)
1920706f2543Smrg					startAngle = startAngle % FULLCIRCLE;
1921706f2543Smrg				endAngle = startAngle + spanAngle;
1922706f2543Smrg				backwards = spanAngle < 0;
1923706f2543Smrg			} else {
1924706f2543Smrg				xarc = parcs[i];
1925706f2543Smrg				if (arcType == VERTICAL) {
1926706f2543Smrg					xarc.angle1 = 0x1680;
1927706f2543Smrg					startAngle = parcs[i].y;
1928706f2543Smrg					endAngle = startAngle + parcs[i].height;
1929706f2543Smrg				} else {
1930706f2543Smrg					xarc.angle1 = 0x2d00;
1931706f2543Smrg					startAngle = parcs[i].x;
1932706f2543Smrg					endAngle = startAngle + parcs[i].width;
1933706f2543Smrg				}
1934706f2543Smrg			}
1935706f2543Smrg			dashAngle = startAngle;
1936706f2543Smrg			selfJoin = data[i].selfJoin &&
1937706f2543Smrg 				    (iphase == 0 || isDoubleDash);
1938706f2543Smrg			/*
1939706f2543Smrg			 * add dashed arcs to each bucket
1940706f2543Smrg			 */
1941706f2543Smrg			arc = 0;
1942706f2543Smrg			while (dashAngle != endAngle) {
1943706f2543Smrg				prevDashAngle = dashAngle;
1944706f2543Smrg				if (arcType == OTHER) {
1945706f2543Smrg					dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
1946706f2543Smrg								&map, &dashRemaining, backwards);
1947706f2543Smrg					/* avoid troubles with huge arcs and small dashes */
1948706f2543Smrg					if (dashAngle == prevDashAngle) {
1949706f2543Smrg						if (backwards)
1950706f2543Smrg							dashAngle--;
1951706f2543Smrg						else
1952706f2543Smrg							dashAngle++;
1953706f2543Smrg					}
1954706f2543Smrg				} else {
1955706f2543Smrg					thisLength = (dashAngle + dashRemaining <= endAngle) ?
1956706f2543Smrg					    dashRemaining : endAngle - dashAngle;
1957706f2543Smrg					if (arcType == VERTICAL) {
1958706f2543Smrg						xarc.y = dashAngle;
1959706f2543Smrg						xarc.height = thisLength;
1960706f2543Smrg					} else {
1961706f2543Smrg						xarc.x = dashAngle;
1962706f2543Smrg						xarc.width = thisLength;
1963706f2543Smrg					}
1964706f2543Smrg					dashAngle += thisLength;
1965706f2543Smrg					dashRemaining -= thisLength;
1966706f2543Smrg				}
1967706f2543Smrg				if (iphase == 0 || isDoubleDash) {
1968706f2543Smrg					if (arcType == OTHER) {
1969706f2543Smrg						xarc = parcs[i];
1970706f2543Smrg						spanAngle = prevDashAngle;
1971706f2543Smrg						if (spanAngle < 0)
1972706f2543Smrg						    spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
1973706f2543Smrg						if (spanAngle >= FULLCIRCLE)
1974706f2543Smrg						    spanAngle = spanAngle % FULLCIRCLE;
1975706f2543Smrg						xarc.angle1 = spanAngle;
1976706f2543Smrg						spanAngle = dashAngle - prevDashAngle;
1977706f2543Smrg						if (backwards) {
1978706f2543Smrg							if (dashAngle > prevDashAngle)
1979706f2543Smrg								spanAngle = - FULLCIRCLE + spanAngle;
1980706f2543Smrg						} else {
1981706f2543Smrg							if (dashAngle < prevDashAngle)
1982706f2543Smrg								spanAngle = FULLCIRCLE + spanAngle;
1983706f2543Smrg						}
1984706f2543Smrg						if (spanAngle > FULLCIRCLE)
1985706f2543Smrg						    spanAngle = FULLCIRCLE;
1986706f2543Smrg						if (spanAngle < -FULLCIRCLE)
1987706f2543Smrg						    spanAngle = -FULLCIRCLE;
1988706f2543Smrg						xarc.angle2 = spanAngle;
1989706f2543Smrg					}
1990706f2543Smrg					arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
1991706f2543Smrg 							&arcSize[iphase], &xarc);
1992706f2543Smrg					if (!arc)
1993706f2543Smrg					    goto arcfail;
1994706f2543Smrg					/*
1995706f2543Smrg					 * cap each end of an on/off dash
1996706f2543Smrg					 */
1997706f2543Smrg					if (!isDoubleDash) {
1998706f2543Smrg						if (prevDashAngle != startAngle) {
1999706f2543Smrg							addCap (&arcs[iphase].caps,
2000706f2543Smrg 								&arcs[iphase].ncaps,
2001706f2543Smrg 								&capSize[iphase], RIGHT_END,
2002706f2543Smrg 								arc - arcs[iphase].arcs);
2003706f2543Smrg
2004706f2543Smrg						}
2005706f2543Smrg						if (dashAngle != endAngle) {
2006706f2543Smrg							addCap (&arcs[iphase].caps,
2007706f2543Smrg 								&arcs[iphase].ncaps,
2008706f2543Smrg 								&capSize[iphase], LEFT_END,
2009706f2543Smrg 								arc - arcs[iphase].arcs);
2010706f2543Smrg						}
2011706f2543Smrg					}
2012706f2543Smrg					arc->cap = arcs[iphase].ncaps;
2013706f2543Smrg					arc->join = arcs[iphase].njoins;
2014706f2543Smrg					arc->render = 0;
2015706f2543Smrg					arc->selfJoin = 0;
2016706f2543Smrg					if (dashAngle == endAngle)
2017706f2543Smrg						arc->selfJoin = selfJoin;
2018706f2543Smrg				}
2019706f2543Smrg				prevphase = iphase;
2020706f2543Smrg				if (dashRemaining <= 0) {
2021706f2543Smrg					++iDash;
2022706f2543Smrg					if (iDash == pGC->numInDashList)
2023706f2543Smrg						iDash = 0;
2024706f2543Smrg					iphase = iphase ? 0:1;
2025706f2543Smrg					dashRemaining = pGC->dash[iDash];
2026706f2543Smrg				}
2027706f2543Smrg			}
2028706f2543Smrg			/*
2029706f2543Smrg			 * make sure a place exists for the position data when
2030706f2543Smrg			 * drawing a zero-length arc
2031706f2543Smrg			 */
2032706f2543Smrg			if (startAngle == endAngle) {
2033706f2543Smrg				prevphase = iphase;
2034706f2543Smrg				if (!isDoubleDash && iphase == 1)
2035706f2543Smrg					prevphase = 0;
2036706f2543Smrg				arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
2037706f2543Smrg					      &arcSize[prevphase], &parcs[i]);
2038706f2543Smrg				if (!arc)
2039706f2543Smrg				    goto arcfail;
2040706f2543Smrg				arc->join = arcs[prevphase].njoins;
2041706f2543Smrg				arc->cap = arcs[prevphase].ncaps;
2042706f2543Smrg				arc->selfJoin = data[i].selfJoin;
2043706f2543Smrg			}
2044706f2543Smrg		} else {
2045706f2543Smrg			arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
2046706f2543Smrg 				      &arcSize[iphase], &parcs[i]);
2047706f2543Smrg			if (!arc)
2048706f2543Smrg			    goto arcfail;
2049706f2543Smrg			arc->join = arcs[iphase].njoins;
2050706f2543Smrg			arc->cap = arcs[iphase].ncaps;
2051706f2543Smrg			arc->selfJoin = data[i].selfJoin;
2052706f2543Smrg			prevphase = iphase;
2053706f2543Smrg		}
2054706f2543Smrg		if (prevphase == 0 || isDoubleDash)
2055706f2543Smrg			k = arcs[prevphase].narcs - 1;
2056706f2543Smrg		if (iphase == 0 || isDoubleDash)
2057706f2543Smrg			nextk = arcs[iphase].narcs;
2058706f2543Smrg		if (nexti == start) {
2059706f2543Smrg			nextk = 0;
2060706f2543Smrg			if (isDashed) {
2061706f2543Smrg				iDash = iDashStart;
2062706f2543Smrg				iphase = iphaseStart;
2063706f2543Smrg				dashRemaining = dashRemainingStart;
2064706f2543Smrg			}
2065706f2543Smrg		}
2066706f2543Smrg		arcsJoin = narcs > 1 && i != j &&
2067706f2543Smrg	 		    ISEQUAL (data[i].x1, data[j].x0) &&
2068706f2543Smrg			    ISEQUAL (data[i].y1, data[j].y0) &&
2069706f2543Smrg			    !data[i].selfJoin && !data[j].selfJoin;
2070706f2543Smrg		if (arc)
2071706f2543Smrg		{
2072706f2543Smrg			if (arcsJoin)
2073706f2543Smrg				arc->render = 0;
2074706f2543Smrg			else
2075706f2543Smrg				arc->render = 1;
2076706f2543Smrg		}
2077706f2543Smrg		if (arcsJoin &&
2078706f2543Smrg		    (prevphase == 0 || isDoubleDash) &&
2079706f2543Smrg		    (iphase == 0 || isDoubleDash))
2080706f2543Smrg 		{
2081706f2543Smrg			joinphase = iphase;
2082706f2543Smrg			if (isDoubleDash) {
2083706f2543Smrg				if (nexti == start)
2084706f2543Smrg					joinphase = iphaseStart;
2085706f2543Smrg				/*
2086706f2543Smrg				 * if the join is right at the dash,
2087706f2543Smrg				 * draw the join in foreground
2088706f2543Smrg				 * This is because the foreground
2089706f2543Smrg				 * arcs are computed second, the results
2090706f2543Smrg				 * of which are needed to draw the join
2091706f2543Smrg				 */
2092706f2543Smrg				if (joinphase != prevphase)
2093706f2543Smrg					joinphase = 0;
2094706f2543Smrg			}
2095706f2543Smrg			if (joinphase == 0 || isDoubleDash) {
2096706f2543Smrg				addJoin (&arcs[joinphase].joins,
2097706f2543Smrg 					 &arcs[joinphase].njoins,
2098706f2543Smrg 					 &joinSize[joinphase],
2099706f2543Smrg	 				 LEFT_END, k, prevphase,
2100706f2543Smrg	 				 RIGHT_END, nextk, iphase);
2101706f2543Smrg				arc->join = arcs[prevphase].njoins;
2102706f2543Smrg			}
2103706f2543Smrg		} else {
2104706f2543Smrg			/*
2105706f2543Smrg			 * cap the left end of this arc
2106706f2543Smrg			 * unless it joins itself
2107706f2543Smrg			 */
2108706f2543Smrg			if ((prevphase == 0 || isDoubleDash) &&
2109706f2543Smrg			    !arc->selfJoin)
2110706f2543Smrg			{
2111706f2543Smrg				addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
2112706f2543Smrg 					&capSize[prevphase], LEFT_END, k);
2113706f2543Smrg				arc->cap = arcs[prevphase].ncaps;
2114706f2543Smrg			}
2115706f2543Smrg			if (isDashed && !arcsJoin) {
2116706f2543Smrg				iDash = iDashStart;
2117706f2543Smrg				iphase = iphaseStart;
2118706f2543Smrg				dashRemaining = dashRemainingStart;
2119706f2543Smrg			}
2120706f2543Smrg			nextk = arcs[iphase].narcs;
2121706f2543Smrg			if (nexti == start) {
2122706f2543Smrg				nextk = 0;
2123706f2543Smrg				iDash = iDashStart;
2124706f2543Smrg				iphase = iphaseStart;
2125706f2543Smrg				dashRemaining = dashRemainingStart;
2126706f2543Smrg			}
2127706f2543Smrg			/*
2128706f2543Smrg			 * cap the right end of the next arc.  If the
2129706f2543Smrg			 * next arc is actually the first arc, only
2130706f2543Smrg			 * cap it if it joins with this arc.  This
2131706f2543Smrg			 * case will occur when the final dash segment
2132706f2543Smrg			 * of an on/off dash is off.  Of course, this
2133706f2543Smrg			 * cap will be drawn at a strange time, but that
2134706f2543Smrg			 * hardly matters...
2135706f2543Smrg			 */
2136706f2543Smrg			if ((iphase == 0 || isDoubleDash) &&
2137706f2543Smrg			    (nexti != start || (arcsJoin && isDashed)))
2138706f2543Smrg				addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
2139706f2543Smrg 					&capSize[iphase], RIGHT_END, nextk);
2140706f2543Smrg		}
2141706f2543Smrg		i = nexti;
2142706f2543Smrg		if (i == start)
2143706f2543Smrg			break;
2144706f2543Smrg	}
2145706f2543Smrg	/*
2146706f2543Smrg	 * make sure the last section is rendered
2147706f2543Smrg	 */
2148706f2543Smrg	for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
2149706f2543Smrg		if (arcs[iphase].narcs > 0) {
2150706f2543Smrg			arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
2151706f2543Smrg			arcs[iphase].arcs[arcs[iphase].narcs-1].join =
2152706f2543Smrg			         arcs[iphase].njoins;
2153706f2543Smrg			arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
2154706f2543Smrg			         arcs[iphase].ncaps;
2155706f2543Smrg		}
2156706f2543Smrg	free(data);
2157706f2543Smrg	return arcs;
2158706f2543Smrgarcfail:
2159706f2543Smrg	miFreeArcs(arcs, pGC);
2160706f2543Smrg	free(data);
2161706f2543Smrg	return NULL;
2162706f2543Smrg}
2163706f2543Smrg
2164706f2543Smrgstatic double
2165706f2543SmrgangleToLength (
2166706f2543Smrg	int	angle,
2167706f2543Smrg	dashMap	*map)
2168706f2543Smrg{
2169706f2543Smrg	double	len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
2170706f2543Smrg	int	di;
2171706f2543Smrg	int	excess;
2172706f2543Smrg	Bool	oddSide = FALSE;
2173706f2543Smrg
2174706f2543Smrg	totallen = 0;
2175706f2543Smrg	if (angle >= 0) {
2176706f2543Smrg		while (angle >= 90 * 64) {
2177706f2543Smrg			angle -= 90 * 64;
2178706f2543Smrg			totallen += sidelen;
2179706f2543Smrg			oddSide = !oddSide;
2180706f2543Smrg		}
2181706f2543Smrg	} else {
2182706f2543Smrg		while (angle < 0) {
2183706f2543Smrg			angle += 90 * 64;
2184706f2543Smrg			totallen -= sidelen;
2185706f2543Smrg			oddSide = !oddSide;
2186706f2543Smrg		}
2187706f2543Smrg	}
2188706f2543Smrg	if (oddSide)
2189706f2543Smrg		angle = 90 * 64 - angle;
2190706f2543Smrg
2191706f2543Smrg	di = xAngleToDashIndex (angle);
2192706f2543Smrg	excess = angle - dashIndexToXAngle (di);
2193706f2543Smrg
2194706f2543Smrg	len = map->map[di];
2195706f2543Smrg	/*
2196706f2543Smrg	 * linearly interpolate between this point and the next
2197706f2543Smrg	 */
2198706f2543Smrg	if (excess > 0) {
2199706f2543Smrg		excesslen = (map->map[di + 1] - map->map[di]) *
2200706f2543Smrg				((double) excess) / dashXAngleStep;
2201706f2543Smrg		len += excesslen;
2202706f2543Smrg	}
2203706f2543Smrg	if (oddSide)
2204706f2543Smrg		totallen += (sidelen - len);
2205706f2543Smrg	else
2206706f2543Smrg		totallen += len;
2207706f2543Smrg	return totallen;
2208706f2543Smrg}
2209706f2543Smrg
2210706f2543Smrg/*
2211706f2543Smrg * len is along the arc, but may be more than one rotation
2212706f2543Smrg */
2213706f2543Smrg
2214706f2543Smrgstatic int
2215706f2543SmrglengthToAngle (
2216706f2543Smrg	double	len,
2217706f2543Smrg	dashMap	*map)
2218706f2543Smrg{
2219706f2543Smrg	double	sidelen = map->map[DASH_MAP_SIZE - 1];
2220706f2543Smrg	int	angle, angleexcess;
2221706f2543Smrg	Bool	oddSide = FALSE;
2222706f2543Smrg	int	a0, a1, a;
2223706f2543Smrg
2224706f2543Smrg	angle = 0;
2225706f2543Smrg	/*
2226706f2543Smrg	 * step around the ellipse, subtracting sidelens and
2227706f2543Smrg	 * adding 90 degrees.  oddSide will tell if the
2228706f2543Smrg	 * map should be interpolated in reverse
2229706f2543Smrg	 */
2230706f2543Smrg	if (len >= 0) {
2231706f2543Smrg		if (sidelen == 0)
2232706f2543Smrg			return 2 * FULLCIRCLE;	/* infinity */
2233706f2543Smrg		while (len >= sidelen) {
2234706f2543Smrg			angle += 90 * 64;
2235706f2543Smrg			len -= sidelen;
2236706f2543Smrg			oddSide = !oddSide;
2237706f2543Smrg		}
2238706f2543Smrg	} else {
2239706f2543Smrg		if (sidelen == 0)
2240706f2543Smrg			return -2 * FULLCIRCLE;	/* infinity */
2241706f2543Smrg		while (len < 0) {
2242706f2543Smrg			angle -= 90 * 64;
2243706f2543Smrg			len += sidelen;
2244706f2543Smrg			oddSide = !oddSide;
2245706f2543Smrg		}
2246706f2543Smrg	}
2247706f2543Smrg	if (oddSide)
2248706f2543Smrg		len = sidelen - len;
2249706f2543Smrg	a0 = 0;
2250706f2543Smrg	a1 = DASH_MAP_SIZE - 1;
2251706f2543Smrg	/*
2252706f2543Smrg	 * binary search for the closest pre-computed length
2253706f2543Smrg	 */
2254706f2543Smrg	while (a1 - a0 > 1) {
2255706f2543Smrg		a = (a0 + a1) / 2;
2256706f2543Smrg		if (len > map->map[a])
2257706f2543Smrg			a0 = a;
2258706f2543Smrg		else
2259706f2543Smrg			a1 = a;
2260706f2543Smrg	}
2261706f2543Smrg	angleexcess = dashIndexToXAngle (a0);
2262706f2543Smrg	/*
2263706f2543Smrg	 * linearly interpolate to the next point
2264706f2543Smrg	 */
2265706f2543Smrg	angleexcess += (len - map->map[a0]) /
2266706f2543Smrg			(map->map[a0+1] - map->map[a0]) * dashXAngleStep;
2267706f2543Smrg	if (oddSide)
2268706f2543Smrg		angle += (90 * 64) - angleexcess;
2269706f2543Smrg	else
2270706f2543Smrg		angle += angleexcess;
2271706f2543Smrg	return angle;
2272706f2543Smrg}
2273706f2543Smrg
2274706f2543Smrg/*
2275706f2543Smrg * compute the angle of an ellipse which cooresponds to
2276706f2543Smrg * the given path length.  Note that the correct solution
2277706f2543Smrg * to this problem is an eliptic integral, we'll punt and
2278706f2543Smrg * approximate (it's only for dashes anyway).  This
2279706f2543Smrg * approximation uses a polygon.
2280706f2543Smrg *
2281706f2543Smrg * The remaining portion of len is stored in *lenp -
2282706f2543Smrg * this will be negative if the arc extends beyond
2283706f2543Smrg * len and positive if len extends beyond the arc.
2284706f2543Smrg */
2285706f2543Smrg
2286706f2543Smrgstatic int
2287706f2543SmrgcomputeAngleFromPath (
2288706f2543Smrg	int	startAngle,
2289706f2543Smrg	int	endAngle,	/* normalized absolute angles in *64 degrees */
2290706f2543Smrg	dashMap	*map,
2291706f2543Smrg	int	*lenp,
2292706f2543Smrg	int	backwards)
2293706f2543Smrg{
2294706f2543Smrg	int	a0, a1, a;
2295706f2543Smrg	double	len0;
2296706f2543Smrg	int	len;
2297706f2543Smrg
2298706f2543Smrg	a0 = startAngle;
2299706f2543Smrg	a1 = endAngle;
2300706f2543Smrg	len = *lenp;
2301706f2543Smrg	if (backwards) {
2302706f2543Smrg		/*
2303706f2543Smrg		 * flip the problem around to always be
2304706f2543Smrg		 * forwards
2305706f2543Smrg		 */
2306706f2543Smrg		a0 = FULLCIRCLE - a0;
2307706f2543Smrg		a1 = FULLCIRCLE - a1;
2308706f2543Smrg	}
2309706f2543Smrg	if (a1 < a0)
2310706f2543Smrg		a1 += FULLCIRCLE;
2311706f2543Smrg	len0 = angleToLength (a0, map);
2312706f2543Smrg	a = lengthToAngle (len0 + len, map);
2313706f2543Smrg	if (a > a1) {
2314706f2543Smrg		a = a1;
2315706f2543Smrg		len -= angleToLength (a1, map) - len0;
2316706f2543Smrg	} else
2317706f2543Smrg		len = 0;
2318706f2543Smrg	if (backwards)
2319706f2543Smrg		a = FULLCIRCLE - a;
2320706f2543Smrg	*lenp = len;
2321706f2543Smrg	return a;
2322706f2543Smrg}
2323706f2543Smrg
2324706f2543Smrg/*
2325706f2543Smrg * scan convert wide arcs.
2326706f2543Smrg */
2327706f2543Smrg
2328706f2543Smrg/*
2329706f2543Smrg * draw zero width/height arcs
2330706f2543Smrg */
2331706f2543Smrg
2332706f2543Smrgstatic void
2333706f2543SmrgdrawZeroArc (
2334706f2543Smrg    DrawablePtr   pDraw,
2335706f2543Smrg    GCPtr         pGC,
2336706f2543Smrg    xArc          *tarc,
2337706f2543Smrg    int		  lw,
2338706f2543Smrg    miArcFacePtr	left,
2339706f2543Smrg    miArcFacePtr	right)
2340706f2543Smrg{
2341706f2543Smrg	double	x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
2342706f2543Smrg	double	xmax, ymax, xmin, ymin;
2343706f2543Smrg	int	a0, a1;
2344706f2543Smrg	double	a, startAngle, endAngle;
2345706f2543Smrg	double	l, lx, ly;
2346706f2543Smrg
2347706f2543Smrg	l = lw / 2.0;
2348706f2543Smrg	a0 = tarc->angle1;
2349706f2543Smrg	a1 = tarc->angle2;
2350706f2543Smrg	if (a1 > FULLCIRCLE)
2351706f2543Smrg		a1 = FULLCIRCLE;
2352706f2543Smrg	else if (a1 < -FULLCIRCLE)
2353706f2543Smrg		a1 = -FULLCIRCLE;
2354706f2543Smrg	w = (double)tarc->width / 2.0;
2355706f2543Smrg	h = (double)tarc->height / 2.0;
2356706f2543Smrg	/*
2357706f2543Smrg	 * play in X coordinates right away
2358706f2543Smrg	 */
2359706f2543Smrg	startAngle = - ((double) a0 / 64.0);
2360706f2543Smrg	endAngle = - ((double) (a0 + a1) / 64.0);
2361706f2543Smrg
2362706f2543Smrg	xmax = -w;
2363706f2543Smrg	xmin = w;
2364706f2543Smrg	ymax = -h;
2365706f2543Smrg	ymin = h;
2366706f2543Smrg	a = startAngle;
2367706f2543Smrg	for (;;)
2368706f2543Smrg	{
2369706f2543Smrg		x = w * miDcos(a);
2370706f2543Smrg		y = h * miDsin(a);
2371706f2543Smrg		if (a == startAngle)
2372706f2543Smrg		{
2373706f2543Smrg			x0 = x;
2374706f2543Smrg			y0 = y;
2375706f2543Smrg		}
2376706f2543Smrg		if (a == endAngle)
2377706f2543Smrg		{
2378706f2543Smrg			x1 = x;
2379706f2543Smrg			y1 = y;
2380706f2543Smrg		}
2381706f2543Smrg		if (x > xmax)
2382706f2543Smrg			xmax = x;
2383706f2543Smrg		if (x < xmin)
2384706f2543Smrg			xmin = x;
2385706f2543Smrg		if (y > ymax)
2386706f2543Smrg			ymax = y;
2387706f2543Smrg		if (y < ymin)
2388706f2543Smrg			ymin = y;
2389706f2543Smrg		if (a == endAngle)
2390706f2543Smrg			break;
2391706f2543Smrg		if (a1 < 0)	/* clockwise */
2392706f2543Smrg		{
2393706f2543Smrg			if (floor (a / 90.0) == floor (endAngle / 90.0))
2394706f2543Smrg				a = endAngle;
2395706f2543Smrg			else
2396706f2543Smrg				a = 90 * (floor (a/90.0) + 1);
2397706f2543Smrg		}
2398706f2543Smrg		else
2399706f2543Smrg		{
2400706f2543Smrg			if (ceil (a / 90.0) == ceil (endAngle / 90.0))
2401706f2543Smrg				a = endAngle;
2402706f2543Smrg			else
2403706f2543Smrg				a = 90 * (ceil (a/90.0) - 1);
2404706f2543Smrg		}
2405706f2543Smrg	}
2406706f2543Smrg	lx = ly = l;
2407706f2543Smrg	if ((x1 - x0) + (y1 - y0) < 0)
2408706f2543Smrg	    lx = ly = -l;
2409706f2543Smrg	if (h)
2410706f2543Smrg	{
2411706f2543Smrg	    ly = 0.0;
2412706f2543Smrg	    lx = -lx;
2413706f2543Smrg	}
2414706f2543Smrg	else
2415706f2543Smrg	    lx = 0.0;
2416706f2543Smrg	if (right)
2417706f2543Smrg	{
2418706f2543Smrg	    right->center.x = x0;
2419706f2543Smrg	    right->center.y = y0;
2420706f2543Smrg	    right->clock.x = x0 - lx;
2421706f2543Smrg	    right->clock.y = y0 - ly;
2422706f2543Smrg	    right->counterClock.x = x0 + lx;
2423706f2543Smrg	    right->counterClock.y = y0 + ly;
2424706f2543Smrg	}
2425706f2543Smrg	if (left)
2426706f2543Smrg 	{
2427706f2543Smrg	    left->center.x = x1;
2428706f2543Smrg	    left->center.y = y1;
2429706f2543Smrg	    left->clock.x = x1 + lx;
2430706f2543Smrg	    left->clock.y = y1 + ly;
2431706f2543Smrg	    left->counterClock.x = x1 - lx;
2432706f2543Smrg	    left->counterClock.y = y1 - ly;
2433706f2543Smrg	}
2434706f2543Smrg
2435706f2543Smrg	x0 = xmin;
2436706f2543Smrg	x1 = xmax;
2437706f2543Smrg	y0 = ymin;
2438706f2543Smrg	y1 = ymax;
2439706f2543Smrg	if (ymin != y1) {
2440706f2543Smrg		xmin = -l;
2441706f2543Smrg		xmax = l;
2442706f2543Smrg	} else {
2443706f2543Smrg		ymin = -l;
2444706f2543Smrg		ymax = l;
2445706f2543Smrg	}
2446706f2543Smrg	if (xmax != xmin && ymax != ymin) {
2447706f2543Smrg		int	minx, maxx, miny, maxy;
2448706f2543Smrg		xRectangle  rect;
2449706f2543Smrg
2450706f2543Smrg		minx = ICEIL (xmin + w) + tarc->x;
2451706f2543Smrg		maxx = ICEIL (xmax + w) + tarc->x;
2452706f2543Smrg		miny = ICEIL (ymin + h) + tarc->y;
2453706f2543Smrg		maxy = ICEIL (ymax + h) + tarc->y;
2454706f2543Smrg		rect.x = minx;
2455706f2543Smrg		rect.y = miny;
2456706f2543Smrg		rect.width = maxx - minx;
2457706f2543Smrg		rect.height = maxy - miny;
2458706f2543Smrg		(*pGC->ops->PolyFillRect) (pDraw, pGC, 1, &rect);
2459706f2543Smrg	}
2460706f2543Smrg}
2461706f2543Smrg
2462706f2543Smrg/*
2463706f2543Smrg * this computes the ellipse y value associated with the
2464706f2543Smrg * bottom of the tail.
2465706f2543Smrg */
2466706f2543Smrg
2467706f2543Smrgstatic void
2468706f2543SmrgtailEllipseY (
2469706f2543Smrg	struct arc_def		*def,
2470706f2543Smrg	struct accelerators	*acc)
2471706f2543Smrg{
2472706f2543Smrg	double		t;
2473706f2543Smrg
2474706f2543Smrg	acc->tail_y = 0.0;
2475706f2543Smrg	if (def->w == def->h)
2476706f2543Smrg	    return;
2477706f2543Smrg	t = def->l * def->w;
2478706f2543Smrg	if (def->w > def->h) {
2479706f2543Smrg	    if (t < acc->h2)
2480706f2543Smrg		return;
2481706f2543Smrg	} else {
2482706f2543Smrg	    if (t > acc->h2)
2483706f2543Smrg		return;
2484706f2543Smrg	}
2485706f2543Smrg	t = 2.0 * def->h * t;
2486706f2543Smrg	t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
2487706f2543Smrg	if (t > 0.0)
2488706f2543Smrg	    acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
2489706f2543Smrg}
2490706f2543Smrg
2491706f2543Smrg/*
2492706f2543Smrg * inverse functions -- compute edge coordinates
2493706f2543Smrg * from the ellipse
2494706f2543Smrg */
2495706f2543Smrg
2496706f2543Smrgstatic double
2497706f2543SmrgouterXfromXY (
2498706f2543Smrg	double			x,
2499706f2543Smrg	double			y,
2500706f2543Smrg	struct arc_def		*def,
2501706f2543Smrg	struct accelerators	*acc)
2502706f2543Smrg{
2503706f2543Smrg	return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2504706f2543Smrg}
2505706f2543Smrg
2506706f2543Smrgstatic double
2507706f2543SmrgouterYfromXY (
2508706f2543Smrg	double		x,
2509706f2543Smrg	double		y,
2510706f2543Smrg	struct arc_def		*def,
2511706f2543Smrg	struct accelerators	*acc)
2512706f2543Smrg{
2513706f2543Smrg	return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2514706f2543Smrg}
2515706f2543Smrg
2516706f2543Smrgstatic double
2517706f2543SmrginnerXfromXY (
2518706f2543Smrg	double			x,
2519706f2543Smrg	double			y,
2520706f2543Smrg	struct arc_def		*def,
2521706f2543Smrg	struct accelerators	*acc)
2522706f2543Smrg{
2523706f2543Smrg	return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2524706f2543Smrg}
2525706f2543Smrg
2526706f2543Smrgstatic double
2527706f2543SmrginnerYfromXY (
2528706f2543Smrg	double			x,
2529706f2543Smrg	double			y,
2530706f2543Smrg	struct arc_def		*def,
2531706f2543Smrg	struct accelerators	*acc)
2532706f2543Smrg{
2533706f2543Smrg	return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2534706f2543Smrg}
2535706f2543Smrg
2536706f2543Smrgstatic double
2537706f2543SmrginnerYfromY (
2538706f2543Smrg	double	y,
2539706f2543Smrg	struct arc_def		*def,
2540706f2543Smrg	struct accelerators	*acc)
2541706f2543Smrg{
2542706f2543Smrg	double	x;
2543706f2543Smrg
2544706f2543Smrg	x = (def->w / def->h) * sqrt (acc->h2 - y*y);
2545706f2543Smrg
2546706f2543Smrg	return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2547706f2543Smrg}
2548706f2543Smrg
2549706f2543Smrgstatic void
2550706f2543SmrgcomputeLine (
2551706f2543Smrg	double		x1,
2552706f2543Smrg	double		y1,
2553706f2543Smrg	double		x2,
2554706f2543Smrg	double		y2,
2555706f2543Smrg	struct line	*line)
2556706f2543Smrg{
2557706f2543Smrg	if (y1 == y2)
2558706f2543Smrg		line->valid = 0;
2559706f2543Smrg	else {
2560706f2543Smrg		line->m = (x1 - x2) / (y1 - y2);
2561706f2543Smrg		line->b = x1  - y1 * line->m;
2562706f2543Smrg		line->valid = 1;
2563706f2543Smrg	}
2564706f2543Smrg}
2565706f2543Smrg
2566706f2543Smrg/*
2567706f2543Smrg * compute various accelerators for an ellipse.  These
2568706f2543Smrg * are simply values that are used repeatedly in
2569706f2543Smrg * the computations
2570706f2543Smrg */
2571706f2543Smrg
2572706f2543Smrgstatic void
2573706f2543SmrgcomputeAcc (
2574706f2543Smrg	xArc			*tarc,
2575706f2543Smrg	int			lw,
2576706f2543Smrg	struct arc_def		*def,
2577706f2543Smrg	struct accelerators	*acc)
2578706f2543Smrg{
2579706f2543Smrg	def->w = ((double) tarc->width) / 2.0;
2580706f2543Smrg	def->h = ((double) tarc->height) / 2.0;
2581706f2543Smrg	def->l = ((double) lw) / 2.0;
2582706f2543Smrg	acc->h2 = def->h * def->h;
2583706f2543Smrg	acc->w2 = def->w * def->w;
2584706f2543Smrg	acc->h4 = acc->h2 * acc->h2;
2585706f2543Smrg	acc->w4 = acc->w2 * acc->w2;
2586706f2543Smrg	acc->h2l = acc->h2 * def->l;
2587706f2543Smrg	acc->w2l = acc->w2 * def->l;
2588706f2543Smrg	acc->h2mw2 = acc->h2 - acc->w2;
2589706f2543Smrg	acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
2590706f2543Smrg	acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
2591706f2543Smrg	acc->xorg = tarc->x + (tarc->width >> 1);
2592706f2543Smrg	acc->yorgu = tarc->y + (tarc->height >> 1);
2593706f2543Smrg	acc->yorgl = acc->yorgu + (tarc->height & 1);
2594706f2543Smrg	tailEllipseY (def, acc);
2595706f2543Smrg}
2596706f2543Smrg
2597706f2543Smrg/*
2598706f2543Smrg * compute y value bounds of various portions of the arc,
2599706f2543Smrg * the outer edge, the ellipse and the inner edge.
2600706f2543Smrg */
2601706f2543Smrg
2602706f2543Smrgstatic void
2603706f2543SmrgcomputeBound (
2604706f2543Smrg	struct arc_def		*def,
2605706f2543Smrg	struct arc_bound	*bound,
2606706f2543Smrg	struct accelerators	*acc,
2607706f2543Smrg	miArcFacePtr		right,
2608706f2543Smrg	miArcFacePtr		left)
2609706f2543Smrg{
2610706f2543Smrg	double		t;
2611706f2543Smrg	double		innerTaily;
2612706f2543Smrg	double		tail_y;
2613706f2543Smrg	struct bound	innerx, outerx;
2614706f2543Smrg	struct bound	ellipsex;
2615706f2543Smrg
2616706f2543Smrg	bound->ellipse.min = Dsin (def->a0) * def->h;
2617706f2543Smrg	bound->ellipse.max = Dsin (def->a1) * def->h;
2618706f2543Smrg	if (def->a0 == 45 && def->w == def->h)
2619706f2543Smrg		ellipsex.min = bound->ellipse.min;
2620706f2543Smrg	else
2621706f2543Smrg		ellipsex.min = Dcos (def->a0) * def->w;
2622706f2543Smrg	if (def->a1 == 45 && def->w == def->h)
2623706f2543Smrg		ellipsex.max = bound->ellipse.max;
2624706f2543Smrg	else
2625706f2543Smrg		ellipsex.max = Dcos (def->a1) * def->w;
2626706f2543Smrg	bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2627706f2543Smrg	bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2628706f2543Smrg	bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2629706f2543Smrg	bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2630706f2543Smrg
2631706f2543Smrg	outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2632706f2543Smrg	outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2633706f2543Smrg	innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2634706f2543Smrg	innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2635706f2543Smrg
2636706f2543Smrg	/*
2637706f2543Smrg	 * save the line end points for the
2638706f2543Smrg	 * cap code to use.  Careful here, these are
2639706f2543Smrg	 * in cartesean coordinates (y increasing upwards)
2640706f2543Smrg	 * while the cap code uses inverted coordinates
2641706f2543Smrg	 * (y increasing downwards)
2642706f2543Smrg	 */
2643706f2543Smrg
2644706f2543Smrg	if (right) {
2645706f2543Smrg		right->counterClock.y = bound->outer.min;
2646706f2543Smrg		right->counterClock.x = outerx.min;
2647706f2543Smrg		right->center.y = bound->ellipse.min;
2648706f2543Smrg		right->center.x = ellipsex.min;
2649706f2543Smrg		right->clock.y = bound->inner.min;
2650706f2543Smrg		right->clock.x = innerx.min;
2651706f2543Smrg	}
2652706f2543Smrg
2653706f2543Smrg	if (left) {
2654706f2543Smrg		left->clock.y = bound->outer.max;
2655706f2543Smrg		left->clock.x = outerx.max;
2656706f2543Smrg		left->center.y = bound->ellipse.max;
2657706f2543Smrg		left->center.x = ellipsex.max;
2658706f2543Smrg		left->counterClock.y = bound->inner.max;
2659706f2543Smrg		left->counterClock.x = innerx.max;
2660706f2543Smrg	}
2661706f2543Smrg
2662706f2543Smrg	bound->left.min = bound->inner.max;
2663706f2543Smrg	bound->left.max = bound->outer.max;
2664706f2543Smrg	bound->right.min = bound->inner.min;
2665706f2543Smrg	bound->right.max = bound->outer.min;
2666706f2543Smrg
2667706f2543Smrg	computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
2668706f2543Smrg		      &acc->right);
2669706f2543Smrg	computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
2670706f2543Smrg		     &acc->left);
2671706f2543Smrg
2672706f2543Smrg	if (bound->inner.min > bound->inner.max) {
2673706f2543Smrg		t = bound->inner.min;
2674706f2543Smrg		bound->inner.min = bound->inner.max;
2675706f2543Smrg		bound->inner.max = t;
2676706f2543Smrg	}
2677706f2543Smrg	tail_y = acc->tail_y;
2678706f2543Smrg	if (tail_y > bound->ellipse.max)
2679706f2543Smrg		tail_y = bound->ellipse.max;
2680706f2543Smrg	else if (tail_y < bound->ellipse.min)
2681706f2543Smrg		tail_y = bound->ellipse.min;
2682706f2543Smrg	innerTaily = innerYfromY (tail_y, def, acc);
2683706f2543Smrg	if (bound->inner.min > innerTaily)
2684706f2543Smrg		bound->inner.min = innerTaily;
2685706f2543Smrg	if (bound->inner.max < innerTaily)
2686706f2543Smrg		bound->inner.max = innerTaily;
2687706f2543Smrg	bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
2688706f2543Smrg	bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
2689706f2543Smrg	bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
2690706f2543Smrg	bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
2691706f2543Smrg}
2692706f2543Smrg
2693706f2543Smrg/*
2694706f2543Smrg * this section computes the x value of the span at y
2695706f2543Smrg * intersected with the specified face of the ellipse.
2696706f2543Smrg *
2697706f2543Smrg * this is the min/max X value over the set of normal
2698706f2543Smrg * lines to the entire ellipse,  the equation of the
2699706f2543Smrg * normal lines is:
2700706f2543Smrg *
2701706f2543Smrg *     ellipse_x h^2                   h^2
2702706f2543Smrg * x = ------------ y + ellipse_x (1 - --- )
2703706f2543Smrg *     ellipse_y w^2                   w^2
2704706f2543Smrg *
2705706f2543Smrg * compute the derivative with-respect-to ellipse_y and solve
2706706f2543Smrg * for zero:
2707706f2543Smrg *
2708706f2543Smrg *       (w^2 - h^2) ellipse_y^3 + h^4 y
2709706f2543Smrg * 0 = - ----------------------------------
2710706f2543Smrg *       h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
2711706f2543Smrg *
2712706f2543Smrg *             (   h^4 y     )
2713706f2543Smrg * ellipse_y = ( ----------  ) ^ (1/3)
2714706f2543Smrg *             ( (h^2 - w^2) )
2715706f2543Smrg *
2716706f2543Smrg * The other two solutions to the equation are imaginary.
2717706f2543Smrg *
2718706f2543Smrg * This gives the position on the ellipse which generates
2719706f2543Smrg * the normal with the largest/smallest x intersection point.
2720706f2543Smrg *
2721706f2543Smrg * Now compute the second derivative to check whether
2722706f2543Smrg * the intersection is a minimum or maximum:
2723706f2543Smrg *
2724706f2543Smrg *    h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2725706f2543Smrg * -  -------------------------------------------
2726706f2543Smrg *          w y0^3 (sqrt (h^2 - y^2)) ^ 3
2727706f2543Smrg *
2728706f2543Smrg * as we only care about the sign,
2729706f2543Smrg *
2730706f2543Smrg * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2731706f2543Smrg *
2732706f2543Smrg * or (to use accelerators),
2733706f2543Smrg *
2734706f2543Smrg * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
2735706f2543Smrg *
2736706f2543Smrg */
2737706f2543Smrg
2738706f2543Smrg/*
2739706f2543Smrg * computes the position on the ellipse whose normal line
2740706f2543Smrg * intersects the given scan line maximally
2741706f2543Smrg */
2742706f2543Smrg
2743706f2543Smrgstatic double
2744706f2543SmrghookEllipseY (
2745706f2543Smrg	double			scan_y,
2746706f2543Smrg	struct arc_bound	*bound,
2747706f2543Smrg	struct accelerators	*acc,
2748706f2543Smrg	int			left)
2749706f2543Smrg{
2750706f2543Smrg	double	ret;
2751706f2543Smrg
2752706f2543Smrg	if (acc->h2mw2 == 0) {
2753706f2543Smrg		if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
2754706f2543Smrg			return bound->ellipse.min;
2755706f2543Smrg		return bound->ellipse.max;
2756706f2543Smrg	}
2757706f2543Smrg	ret = (acc->h4 * scan_y) / (acc->h2mw2);
2758706f2543Smrg	if (ret >= 0)
2759706f2543Smrg		return cbrt (ret);
2760706f2543Smrg	else
2761706f2543Smrg		return -cbrt (-ret);
2762706f2543Smrg}
2763706f2543Smrg
2764706f2543Smrg/*
2765706f2543Smrg * computes the X value of the intersection of the
2766706f2543Smrg * given scan line with the right side of the lower hook
2767706f2543Smrg */
2768706f2543Smrg
2769706f2543Smrgstatic double
2770706f2543SmrghookX (
2771706f2543Smrg	double			scan_y,
2772706f2543Smrg	struct arc_def		*def,
2773706f2543Smrg	struct arc_bound	*bound,
2774706f2543Smrg	struct accelerators	*acc,
2775706f2543Smrg	int			left)
2776706f2543Smrg{
2777706f2543Smrg	double	ellipse_y, x;
2778706f2543Smrg	double	maxMin;
2779706f2543Smrg
2780706f2543Smrg	if (def->w != def->h) {
2781706f2543Smrg		ellipse_y = hookEllipseY (scan_y, bound, acc, left);
2782706f2543Smrg		if (boundedLe (ellipse_y, bound->ellipse)) {
2783706f2543Smrg			/*
2784706f2543Smrg		 	 * compute the value of the second
2785706f2543Smrg		 	 * derivative
2786706f2543Smrg		 	 */
2787706f2543Smrg			maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
2788706f2543Smrg		 	 acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
2789706f2543Smrg			if ((left && maxMin > 0) || (!left && maxMin < 0)) {
2790706f2543Smrg				if (ellipse_y == 0)
2791706f2543Smrg					return def->w + left ? -def->l : def->l;
2792706f2543Smrg				x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
2793706f2543Smrg					sqrt (acc->h2 - ellipse_y * ellipse_y) /
2794706f2543Smrg			 		(def->h * def->w * ellipse_y);
2795706f2543Smrg				return x;
2796706f2543Smrg			}
2797706f2543Smrg		}
2798706f2543Smrg	}
2799706f2543Smrg	if (left) {
2800706f2543Smrg		if (acc->left.valid && boundedLe (scan_y, bound->left)) {
2801706f2543Smrg			x = intersectLine (scan_y, acc->left);
2802706f2543Smrg		} else {
2803706f2543Smrg			if (acc->right.valid)
2804706f2543Smrg				x = intersectLine (scan_y, acc->right);
2805706f2543Smrg			else
2806706f2543Smrg				x = def->w - def->l;
2807706f2543Smrg		}
2808706f2543Smrg	} else {
2809706f2543Smrg		if (acc->right.valid && boundedLe (scan_y, bound->right)) {
2810706f2543Smrg			x = intersectLine (scan_y, acc->right);
2811706f2543Smrg		} else {
2812706f2543Smrg			if (acc->left.valid)
2813706f2543Smrg				x = intersectLine (scan_y, acc->left);
2814706f2543Smrg			else
2815706f2543Smrg				x = def->w - def->l;
2816706f2543Smrg		}
2817706f2543Smrg	}
2818706f2543Smrg	return x;
2819706f2543Smrg}
2820706f2543Smrg
2821706f2543Smrg/*
2822706f2543Smrg * generate the set of spans with
2823706f2543Smrg * the given y coordinate
2824706f2543Smrg */
2825706f2543Smrg
2826706f2543Smrgstatic void
2827706f2543SmrgarcSpan (
2828706f2543Smrg	int			y,
2829706f2543Smrg	int			lx,
2830706f2543Smrg	int			lw,
2831706f2543Smrg	int			rx,
2832706f2543Smrg	int			rw,
2833706f2543Smrg	struct arc_def		*def,
2834706f2543Smrg	struct arc_bound	*bounds,
2835706f2543Smrg	struct accelerators	*acc,
2836706f2543Smrg	int			mask)
2837706f2543Smrg{
2838706f2543Smrg	int linx, loutx, rinx, routx;
2839706f2543Smrg	double x, altx;
2840706f2543Smrg
2841706f2543Smrg	if (boundedLe (y, bounds->inneri)) {
2842706f2543Smrg	    linx = -(lx + lw);
2843706f2543Smrg	    rinx = rx;
2844706f2543Smrg	} else {
2845706f2543Smrg	    /*
2846706f2543Smrg	     * intersection with left face
2847706f2543Smrg	     */
2848706f2543Smrg	    x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
2849706f2543Smrg	    if (acc->right.valid &&
2850706f2543Smrg		boundedLe (y + acc->fromIntY, bounds->right))
2851706f2543Smrg	    {
2852706f2543Smrg		altx = intersectLine (y + acc->fromIntY, acc->right);
2853706f2543Smrg		if (altx < x)
2854706f2543Smrg		    x = altx;
2855706f2543Smrg	    }
2856706f2543Smrg	    linx = -ICEIL(acc->fromIntX - x);
2857706f2543Smrg	    rinx = ICEIL(acc->fromIntX + x);
2858706f2543Smrg	}
2859706f2543Smrg	if (boundedLe (y, bounds->outeri)) {
2860706f2543Smrg	    loutx = -lx;
2861706f2543Smrg	    routx = rx + rw;
2862706f2543Smrg	} else {
2863706f2543Smrg	    /*
2864706f2543Smrg	     * intersection with right face
2865706f2543Smrg	     */
2866706f2543Smrg	    x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
2867706f2543Smrg	    if (acc->left.valid &&
2868706f2543Smrg		boundedLe (y + acc->fromIntY, bounds->left))
2869706f2543Smrg	    {
2870706f2543Smrg		altx = x;
2871706f2543Smrg		x = intersectLine (y + acc->fromIntY, acc->left);
2872706f2543Smrg		if (x < altx)
2873706f2543Smrg		    x = altx;
2874706f2543Smrg	    }
2875706f2543Smrg	    loutx = -ICEIL(acc->fromIntX - x);
2876706f2543Smrg	    routx = ICEIL(acc->fromIntX + x);
2877706f2543Smrg	}
2878706f2543Smrg	if (routx > rinx) {
2879706f2543Smrg	    if (mask & 1)
2880706f2543Smrg		newFinalSpan (acc->yorgu - y,
2881706f2543Smrg			      acc->xorg + rinx, acc->xorg + routx);
2882706f2543Smrg	    if (mask & 8)
2883706f2543Smrg		newFinalSpan (acc->yorgl + y,
2884706f2543Smrg			      acc->xorg + rinx, acc->xorg + routx);
2885706f2543Smrg	}
2886706f2543Smrg	if (loutx > linx) {
2887706f2543Smrg	    if (mask & 2)
2888706f2543Smrg		newFinalSpan (acc->yorgu - y,
2889706f2543Smrg			      acc->xorg - loutx, acc->xorg - linx);
2890706f2543Smrg	    if (mask & 4)
2891706f2543Smrg		newFinalSpan (acc->yorgl + y,
2892706f2543Smrg			      acc->xorg - loutx, acc->xorg - linx);
2893706f2543Smrg	}
2894706f2543Smrg}
2895706f2543Smrg
2896706f2543Smrgstatic void
2897706f2543SmrgarcSpan0 (
2898706f2543Smrg	int			lx,
2899706f2543Smrg	int			lw,
2900706f2543Smrg	int			rx,
2901706f2543Smrg	int			rw,
2902706f2543Smrg	struct arc_def		*def,
2903706f2543Smrg	struct arc_bound	*bounds,
2904706f2543Smrg	struct accelerators	*acc,
2905706f2543Smrg	int			mask)
2906706f2543Smrg{
2907706f2543Smrg    double x;
2908706f2543Smrg
2909706f2543Smrg    if (boundedLe (0, bounds->inneri) &&
2910706f2543Smrg	acc->left.valid && boundedLe (0, bounds->left) &&
2911706f2543Smrg	acc->left.b > 0)
2912706f2543Smrg    {
2913706f2543Smrg	x = def->w - def->l;
2914706f2543Smrg	if (acc->left.b < x)
2915706f2543Smrg	    x = acc->left.b;
2916706f2543Smrg	lw = ICEIL(acc->fromIntX - x) - lx;
2917706f2543Smrg	rw += rx;
2918706f2543Smrg	rx = ICEIL(acc->fromIntX + x);
2919706f2543Smrg	rw -= rx;
2920706f2543Smrg    }
2921706f2543Smrg    arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
2922706f2543Smrg}
2923706f2543Smrg
2924706f2543Smrgstatic void
2925706f2543SmrgtailSpan (
2926706f2543Smrg	int			y,
2927706f2543Smrg	int			lw,
2928706f2543Smrg	int			rw,
2929706f2543Smrg	struct arc_def		*def,
2930706f2543Smrg	struct arc_bound	*bounds,
2931706f2543Smrg	struct accelerators	*acc,
2932706f2543Smrg	int			mask)
2933706f2543Smrg{
2934706f2543Smrg    double yy, xalt, x, lx, rx;
2935706f2543Smrg    int n;
2936706f2543Smrg
2937706f2543Smrg    if (boundedLe(y, bounds->outeri))
2938706f2543Smrg	arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
2939706f2543Smrg    else if (def->w != def->h) {
2940706f2543Smrg	yy = y + acc->fromIntY;
2941706f2543Smrg	x = tailX(yy, def, bounds, acc);
2942706f2543Smrg	if (yy == 0.0 && x == -rw - acc->fromIntX)
2943706f2543Smrg	    return;
2944706f2543Smrg	if (acc->right.valid && boundedLe (yy, bounds->right)) {
2945706f2543Smrg	    rx = x;
2946706f2543Smrg	    lx = -x;
2947706f2543Smrg	    xalt = intersectLine (yy, acc->right);
2948706f2543Smrg	    if (xalt >= -rw - acc->fromIntX && xalt <= rx)
2949706f2543Smrg		rx = xalt;
2950706f2543Smrg	    n = ICEIL(acc->fromIntX + lx);
2951706f2543Smrg	    if (lw > n) {
2952706f2543Smrg		if (mask & 2)
2953706f2543Smrg		    newFinalSpan (acc->yorgu - y,
2954706f2543Smrg				  acc->xorg + n, acc->xorg + lw);
2955706f2543Smrg		if (mask & 4)
2956706f2543Smrg		    newFinalSpan (acc->yorgl + y,
2957706f2543Smrg				  acc->xorg + n, acc->xorg + lw);
2958706f2543Smrg	    }
2959706f2543Smrg	    n = ICEIL(acc->fromIntX + rx);
2960706f2543Smrg	    if (n > -rw) {
2961706f2543Smrg		if (mask & 1)
2962706f2543Smrg		    newFinalSpan (acc->yorgu - y,
2963706f2543Smrg				  acc->xorg - rw, acc->xorg + n);
2964706f2543Smrg		if (mask & 8)
2965706f2543Smrg		    newFinalSpan (acc->yorgl + y,
2966706f2543Smrg				  acc->xorg - rw, acc->xorg + n);
2967706f2543Smrg	    }
2968706f2543Smrg	}
2969706f2543Smrg	arcSpan (y,
2970706f2543Smrg		 ICEIL(acc->fromIntX - x), 0,
2971706f2543Smrg		 ICEIL(acc->fromIntX + x), 0,
2972706f2543Smrg		 def, bounds, acc, mask);
2973706f2543Smrg    }
2974706f2543Smrg}
2975706f2543Smrg
2976706f2543Smrg/*
2977706f2543Smrg * create whole arcs out of pieces.  This code is
2978706f2543Smrg * very bad.
2979706f2543Smrg */
2980706f2543Smrg
2981706f2543Smrgstatic struct finalSpan	**finalSpans = NULL;
2982706f2543Smrgstatic int		finalMiny = 0, finalMaxy = -1;
2983706f2543Smrgstatic int		finalSize = 0;
2984706f2543Smrg
2985706f2543Smrgstatic int		nspans = 0;	/* total spans, not just y coords */
2986706f2543Smrg
2987706f2543Smrgstruct finalSpan {
2988706f2543Smrg	struct finalSpan	*next;
2989706f2543Smrg	int			min, max;	/* x values */
2990706f2543Smrg};
2991706f2543Smrg
2992706f2543Smrgstatic struct finalSpan    *freeFinalSpans, *tmpFinalSpan;
2993706f2543Smrg
2994706f2543Smrg# define allocFinalSpan()   (freeFinalSpans ?\
2995706f2543Smrg				((tmpFinalSpan = freeFinalSpans), \
2996706f2543Smrg				 (freeFinalSpans = freeFinalSpans->next), \
2997706f2543Smrg				 (tmpFinalSpan->next = 0), \
2998706f2543Smrg				 tmpFinalSpan) : \
2999706f2543Smrg			     realAllocSpan ())
3000706f2543Smrg
3001706f2543Smrg# define SPAN_CHUNK_SIZE    128
3002706f2543Smrg
3003706f2543Smrgstruct finalSpanChunk {
3004706f2543Smrg	struct finalSpan	data[SPAN_CHUNK_SIZE];
3005706f2543Smrg	struct finalSpanChunk	*next;
3006706f2543Smrg};
3007706f2543Smrg
3008706f2543Smrgstatic struct finalSpanChunk	*chunks;
3009706f2543Smrg
3010706f2543Smrgstatic struct finalSpan *
3011706f2543SmrgrealAllocSpan (void)
3012706f2543Smrg{
3013706f2543Smrg	struct finalSpanChunk	*newChunk;
3014706f2543Smrg	struct finalSpan	*span;
3015706f2543Smrg	int			i;
3016706f2543Smrg
3017706f2543Smrg	newChunk = malloc(sizeof (struct finalSpanChunk));
3018706f2543Smrg	if (!newChunk)
3019706f2543Smrg		return (struct finalSpan *) NULL;
3020706f2543Smrg	newChunk->next = chunks;
3021706f2543Smrg	chunks = newChunk;
3022706f2543Smrg	freeFinalSpans = span = newChunk->data + 1;
3023706f2543Smrg	for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
3024706f2543Smrg		span->next = span+1;
3025706f2543Smrg		span++;
3026706f2543Smrg	}
3027706f2543Smrg	span->next = 0;
3028706f2543Smrg	span = newChunk->data;
3029706f2543Smrg	span->next = 0;
3030706f2543Smrg	return span;
3031706f2543Smrg}
3032706f2543Smrg
3033706f2543Smrgstatic void
3034706f2543SmrgdisposeFinalSpans (void)
3035706f2543Smrg{
3036706f2543Smrg	struct finalSpanChunk	*chunk, *next;
3037706f2543Smrg
3038706f2543Smrg	for (chunk = chunks; chunk; chunk = next) {
3039706f2543Smrg		next = chunk->next;
3040706f2543Smrg		free(chunk);
3041706f2543Smrg	}
3042706f2543Smrg	chunks = 0;
3043706f2543Smrg	freeFinalSpans = 0;
3044706f2543Smrg	free(finalSpans);
3045706f2543Smrg	finalSpans = 0;
3046706f2543Smrg}
3047706f2543Smrg
3048706f2543Smrgstatic void
3049706f2543SmrgfillSpans (
3050706f2543Smrg    DrawablePtr	pDrawable,
3051706f2543Smrg    GCPtr	pGC)
3052706f2543Smrg{
3053706f2543Smrg	struct finalSpan	*span;
3054706f2543Smrg	DDXPointPtr		xSpan;
3055706f2543Smrg	int			*xWidth;
3056706f2543Smrg	int			i;
3057706f2543Smrg	struct finalSpan	**f;
3058706f2543Smrg	int			spany;
3059706f2543Smrg	DDXPointPtr		xSpans;
3060706f2543Smrg	int			*xWidths;
3061706f2543Smrg
3062706f2543Smrg	if (nspans == 0)
3063706f2543Smrg		return;
3064706f2543Smrg	xSpan = xSpans = malloc(nspans * sizeof (DDXPointRec));
3065706f2543Smrg	xWidth = xWidths = malloc(nspans * sizeof (int));
3066706f2543Smrg	if (xSpans && xWidths)
3067706f2543Smrg	{
3068706f2543Smrg	    i = 0;
3069706f2543Smrg	    f = finalSpans;
3070706f2543Smrg	    for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
3071706f2543Smrg		    for (span = *f; span; span=span->next) {
3072706f2543Smrg			    if (span->max <= span->min)
3073706f2543Smrg				    continue;
3074706f2543Smrg			    xSpan->x = span->min;
3075706f2543Smrg			    xSpan->y = spany;
3076706f2543Smrg			    ++xSpan;
3077706f2543Smrg			    *xWidth++ = span->max - span->min;
3078706f2543Smrg			    ++i;
3079706f2543Smrg		    }
3080706f2543Smrg	    }
3081706f2543Smrg	    (*pGC->ops->FillSpans) (pDrawable, pGC, i, xSpans, xWidths, TRUE);
3082706f2543Smrg	}
3083706f2543Smrg	disposeFinalSpans ();
3084706f2543Smrg	free(xSpans);
3085706f2543Smrg	free(xWidths);
3086706f2543Smrg	finalMiny = 0;
3087706f2543Smrg	finalMaxy = -1;
3088706f2543Smrg	finalSize = 0;
3089706f2543Smrg	nspans = 0;
3090706f2543Smrg}
3091706f2543Smrg
3092706f2543Smrg# define SPAN_REALLOC	100
3093706f2543Smrg
3094706f2543Smrg# define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
3095706f2543Smrg			  &finalSpans[(y) - finalMiny] : \
3096706f2543Smrg			  realFindSpan (y))
3097706f2543Smrg
3098706f2543Smrgstatic struct finalSpan **
3099706f2543SmrgrealFindSpan (int y)
3100706f2543Smrg{
3101706f2543Smrg	struct finalSpan	**newSpans;
3102706f2543Smrg	int			newSize, newMiny, newMaxy;
3103706f2543Smrg	int			change;
3104706f2543Smrg	int			i;
3105706f2543Smrg
3106706f2543Smrg	if (y < finalMiny || y > finalMaxy) {
3107706f2543Smrg		if (!finalSize) {
3108706f2543Smrg			finalMiny = y;
3109706f2543Smrg			finalMaxy = y - 1;
3110706f2543Smrg		}
3111706f2543Smrg		if (y < finalMiny)
3112706f2543Smrg			change = finalMiny - y;
3113706f2543Smrg		else
3114706f2543Smrg			change = y - finalMaxy;
3115706f2543Smrg		if (change >= SPAN_REALLOC)
3116706f2543Smrg			change += SPAN_REALLOC;
3117706f2543Smrg		else
3118706f2543Smrg			change = SPAN_REALLOC;
3119706f2543Smrg		newSize = finalSize + change;
3120706f2543Smrg		newSpans = malloc(newSize * sizeof (struct finalSpan *));
3121706f2543Smrg		if (!newSpans)
3122706f2543Smrg		    return NULL;
3123706f2543Smrg		newMiny = finalMiny;
3124706f2543Smrg		newMaxy = finalMaxy;
3125706f2543Smrg		if (y < finalMiny)
3126706f2543Smrg			newMiny = finalMiny - change;
3127706f2543Smrg		else
3128706f2543Smrg			newMaxy = finalMaxy + change;
3129706f2543Smrg		if (finalSpans) {
3130706f2543Smrg			memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
3131706f2543Smrg				(char *) finalSpans,
3132706f2543Smrg			       finalSize * sizeof (struct finalSpan *));
3133706f2543Smrg			free(finalSpans);
3134706f2543Smrg		}
3135706f2543Smrg		if ((i = finalMiny - newMiny) > 0)
3136706f2543Smrg			memset((char *)newSpans, 0, i * sizeof (struct finalSpan *));
3137706f2543Smrg		if ((i = newMaxy - finalMaxy) > 0)
3138706f2543Smrg			memset((char *)(newSpans + newSize - i), 0,
3139706f2543Smrg			       i * sizeof (struct finalSpan *));
3140706f2543Smrg		finalSpans = newSpans;
3141706f2543Smrg		finalMaxy = newMaxy;
3142706f2543Smrg		finalMiny = newMiny;
3143706f2543Smrg		finalSize = newSize;
3144706f2543Smrg	}
3145706f2543Smrg	return &finalSpans[y - finalMiny];
3146706f2543Smrg}
3147706f2543Smrg
3148706f2543Smrgstatic void
3149706f2543SmrgnewFinalSpan (
3150706f2543Smrg    int		y,
3151706f2543Smrg    int	xmin,
3152706f2543Smrg    int	xmax)
3153706f2543Smrg{
3154706f2543Smrg	struct finalSpan	*x;
3155706f2543Smrg	struct finalSpan	**f;
3156706f2543Smrg	struct finalSpan	*oldx;
3157706f2543Smrg	struct finalSpan	*prev;
3158706f2543Smrg
3159706f2543Smrg	f = findSpan (y);
3160706f2543Smrg	if (!f)
3161706f2543Smrg	    return;
3162706f2543Smrg	oldx = 0;
3163706f2543Smrg	for (;;) {
3164706f2543Smrg		prev = 0;
3165706f2543Smrg		for (x = *f; x; x=x->next) {
3166706f2543Smrg			if (x == oldx) {
3167706f2543Smrg				prev = x;
3168706f2543Smrg				continue;
3169706f2543Smrg			}
3170706f2543Smrg			if (x->min <= xmax && xmin <= x->max) {
3171706f2543Smrg				if (oldx) {
3172706f2543Smrg					oldx->min = min (x->min, xmin);
3173706f2543Smrg					oldx->max = max (x->max, xmax);
3174706f2543Smrg					if (prev)
3175706f2543Smrg						prev->next = x->next;
3176706f2543Smrg					else
3177706f2543Smrg						*f = x->next;
3178706f2543Smrg					--nspans;
3179706f2543Smrg				} else {
3180706f2543Smrg					x->min = min (x->min, xmin);
3181706f2543Smrg					x->max = max (x->max, xmax);
3182706f2543Smrg					oldx = x;
3183706f2543Smrg				}
3184706f2543Smrg				xmin = oldx->min;
3185706f2543Smrg				xmax = oldx->max;
3186706f2543Smrg				break;
3187706f2543Smrg			}
3188706f2543Smrg			prev = x;
3189706f2543Smrg		}
3190706f2543Smrg		if (!x)
3191706f2543Smrg			break;
3192706f2543Smrg	}
3193706f2543Smrg	if (!oldx) {
3194706f2543Smrg		x = allocFinalSpan ();
3195706f2543Smrg		if (x)
3196706f2543Smrg		{
3197706f2543Smrg		    x->min = xmin;
3198706f2543Smrg		    x->max = xmax;
3199706f2543Smrg		    x->next = *f;
3200706f2543Smrg		    *f = x;
3201706f2543Smrg		    ++nspans;
3202706f2543Smrg		}
3203706f2543Smrg	}
3204706f2543Smrg}
3205706f2543Smrg
3206706f2543Smrgstatic void
3207706f2543SmrgmirrorSppPoint (
3208706f2543Smrg	int		quadrant,
3209706f2543Smrg	SppPointPtr	sppPoint)
3210706f2543Smrg{
3211706f2543Smrg	switch (quadrant) {
3212706f2543Smrg	case 0:
3213706f2543Smrg		break;
3214706f2543Smrg	case 1:
3215706f2543Smrg		sppPoint->x = -sppPoint->x;
3216706f2543Smrg		break;
3217706f2543Smrg	case 2:
3218706f2543Smrg		sppPoint->x = -sppPoint->x;
3219706f2543Smrg		sppPoint->y = -sppPoint->y;
3220706f2543Smrg		break;
3221706f2543Smrg	case 3:
3222706f2543Smrg		sppPoint->y = -sppPoint->y;
3223706f2543Smrg		break;
3224706f2543Smrg	}
3225706f2543Smrg	/*
3226706f2543Smrg	 * and translate to X coordinate system
3227706f2543Smrg	 */
3228706f2543Smrg	sppPoint->y = -sppPoint->y;
3229706f2543Smrg}
3230706f2543Smrg
3231706f2543Smrg/*
3232706f2543Smrg * split an arc into pieces which are scan-converted
3233706f2543Smrg * in the first-quadrant and mirrored into position.
3234706f2543Smrg * This is necessary as the scan-conversion code can
3235706f2543Smrg * only deal with arcs completely contained in the
3236706f2543Smrg * first quadrant.
3237706f2543Smrg */
3238706f2543Smrg
3239706f2543Smrgstatic void
3240706f2543SmrgdrawArc (
3241706f2543Smrg	xArc *tarc,
3242706f2543Smrg	int	l,
3243706f2543Smrg	int	a0,
3244706f2543Smrg	int	a1,
3245706f2543Smrg	miArcFacePtr	right,
3246706f2543Smrg	miArcFacePtr	left)	/* save end line points */
3247706f2543Smrg{
3248706f2543Smrg	struct arc_def		def;
3249706f2543Smrg	struct accelerators	acc;
3250706f2543Smrg	int			startq, endq, curq;
3251706f2543Smrg	int			rightq, leftq = 0, righta = 0, lefta = 0;
3252706f2543Smrg	miArcFacePtr		passRight, passLeft;
3253706f2543Smrg	int			q0 = 0, q1 = 0, mask;
3254706f2543Smrg	struct band {
3255706f2543Smrg		int	a0, a1;
3256706f2543Smrg		int	mask;
3257706f2543Smrg	}	band[5], sweep[20];
3258706f2543Smrg	int			bandno, sweepno;
3259706f2543Smrg	int			i, j;
3260706f2543Smrg	int			flipRight = 0, flipLeft = 0;
3261706f2543Smrg	int			copyEnd = 0;
3262706f2543Smrg	miArcSpanData		*spdata;
3263706f2543Smrg
3264706f2543Smrg	spdata = miComputeWideEllipse(l, tarc);
3265706f2543Smrg	if (!spdata)
3266706f2543Smrg	    return;
3267706f2543Smrg
3268706f2543Smrg	if (a1 < a0)
3269706f2543Smrg		a1 += 360 * 64;
3270706f2543Smrg	startq = a0 / (90 * 64);
3271706f2543Smrg	if (a0 == a1)
3272706f2543Smrg	    endq = startq;
3273706f2543Smrg	else
3274706f2543Smrg	    endq = (a1-1) / (90 * 64);
3275706f2543Smrg	bandno = 0;
3276706f2543Smrg	curq = startq;
3277706f2543Smrg	rightq = -1;
3278706f2543Smrg	for (;;) {
3279706f2543Smrg		switch (curq) {
3280706f2543Smrg		case 0:
3281706f2543Smrg			if (a0 > 90 * 64)
3282706f2543Smrg				q0 = 0;
3283706f2543Smrg			else
3284706f2543Smrg				q0 = a0;
3285706f2543Smrg			if (a1 < 360 * 64)
3286706f2543Smrg				q1 = min (a1, 90 * 64);
3287706f2543Smrg			else
3288706f2543Smrg				q1 = 90 * 64;
3289706f2543Smrg			if (curq == startq && a0 == q0 && rightq < 0) {
3290706f2543Smrg				righta = q0;
3291706f2543Smrg				rightq = curq;
3292706f2543Smrg			}
3293706f2543Smrg			if (curq == endq && a1 == q1) {
3294706f2543Smrg				lefta = q1;
3295706f2543Smrg				leftq = curq;
3296706f2543Smrg			}
3297706f2543Smrg			break;
3298706f2543Smrg		case 1:
3299706f2543Smrg			if (a1 < 90 * 64)
3300706f2543Smrg				q0 = 0;
3301706f2543Smrg			else
3302706f2543Smrg				q0 = 180 * 64 - min (a1, 180 * 64);
3303706f2543Smrg			if (a0 > 180 * 64)
3304706f2543Smrg				q1 = 90 * 64;
3305706f2543Smrg			else
3306706f2543Smrg				q1 = 180 * 64 - max (a0, 90 * 64);
3307706f2543Smrg			if (curq == startq && 180 * 64 - a0 == q1) {
3308706f2543Smrg				righta = q1;
3309706f2543Smrg				rightq = curq;
3310706f2543Smrg			}
3311706f2543Smrg			if (curq == endq && 180 * 64 - a1 == q0) {
3312706f2543Smrg				lefta = q0;
3313706f2543Smrg				leftq = curq;
3314706f2543Smrg			}
3315706f2543Smrg			break;
3316706f2543Smrg		case 2:
3317706f2543Smrg			if (a0 > 270 * 64)
3318706f2543Smrg				q0 = 0;
3319706f2543Smrg			else
3320706f2543Smrg				q0 = max (a0, 180 * 64) - 180 * 64;
3321706f2543Smrg			if (a1 < 180 * 64)
3322706f2543Smrg				q1 = 90 * 64;
3323706f2543Smrg			else
3324706f2543Smrg				q1 = min (a1, 270 * 64) - 180 * 64;
3325706f2543Smrg			if (curq == startq && a0 - 180*64 == q0) {
3326706f2543Smrg				righta = q0;
3327706f2543Smrg				rightq = curq;
3328706f2543Smrg			}
3329706f2543Smrg			if (curq == endq && a1 - 180 * 64 == q1) {
3330706f2543Smrg				lefta = q1;
3331706f2543Smrg				leftq = curq;
3332706f2543Smrg			}
3333706f2543Smrg			break;
3334706f2543Smrg		case 3:
3335706f2543Smrg			if (a1 < 270 * 64)
3336706f2543Smrg				q0 = 0;
3337706f2543Smrg			else
3338706f2543Smrg				q0 = 360 * 64 - min (a1, 360 * 64);
3339706f2543Smrg			q1 = 360 * 64 - max (a0, 270 * 64);
3340706f2543Smrg			if (curq == startq && 360 * 64 - a0 == q1) {
3341706f2543Smrg				righta = q1;
3342706f2543Smrg				rightq = curq;
3343706f2543Smrg			}
3344706f2543Smrg			if (curq == endq && 360 * 64 - a1 == q0) {
3345706f2543Smrg				lefta = q0;
3346706f2543Smrg				leftq = curq;
3347706f2543Smrg			}
3348706f2543Smrg			break;
3349706f2543Smrg		}
3350706f2543Smrg		band[bandno].a0 = q0;
3351706f2543Smrg		band[bandno].a1 = q1;
3352706f2543Smrg		band[bandno].mask = 1 << curq;
3353706f2543Smrg		bandno++;
3354706f2543Smrg		if (curq == endq)
3355706f2543Smrg			break;
3356706f2543Smrg		curq++;
3357706f2543Smrg		if (curq == 4) {
3358706f2543Smrg			a0 = 0;
3359706f2543Smrg			a1 -= 360 * 64;
3360706f2543Smrg			curq = 0;
3361706f2543Smrg			endq -= 4;
3362706f2543Smrg		}
3363706f2543Smrg	}
3364706f2543Smrg	sweepno = 0;
3365706f2543Smrg	for (;;) {
3366706f2543Smrg		q0 = 90 * 64;
3367706f2543Smrg		mask = 0;
3368706f2543Smrg		/*
3369706f2543Smrg		 * find left-most point
3370706f2543Smrg		 */
3371706f2543Smrg		for (i = 0; i < bandno; i++)
3372706f2543Smrg			if (band[i].a0 <= q0) {
3373706f2543Smrg				q0 = band[i].a0;
3374706f2543Smrg				q1 = band[i].a1;
3375706f2543Smrg				mask = band[i].mask;
3376706f2543Smrg			}
3377706f2543Smrg		if (!mask)
3378706f2543Smrg			break;
3379706f2543Smrg		/*
3380706f2543Smrg		 * locate next point of change
3381706f2543Smrg		 */
3382706f2543Smrg		for (i = 0; i < bandno; i++)
3383706f2543Smrg			if (!(mask & band[i].mask)) {
3384706f2543Smrg				if (band[i].a0 == q0) {
3385706f2543Smrg					if (band[i].a1 < q1)
3386706f2543Smrg						q1 = band[i].a1;
3387706f2543Smrg					mask |= band[i].mask;
3388706f2543Smrg 				} else if (band[i].a0 < q1)
3389706f2543Smrg					q1 = band[i].a0;
3390706f2543Smrg			}
3391706f2543Smrg		/*
3392706f2543Smrg		 * create a new sweep
3393706f2543Smrg		 */
3394706f2543Smrg		sweep[sweepno].a0 = q0;
3395706f2543Smrg		sweep[sweepno].a1 = q1;
3396706f2543Smrg		sweep[sweepno].mask = mask;
3397706f2543Smrg		sweepno++;
3398706f2543Smrg		/*
3399706f2543Smrg		 * subtract the sweep from the affected bands
3400706f2543Smrg		 */
3401706f2543Smrg		for (i = 0; i < bandno; i++)
3402706f2543Smrg			if (band[i].a0 == q0) {
3403706f2543Smrg				band[i].a0 = q1;
3404706f2543Smrg				/*
3405706f2543Smrg				 * check if this band is empty
3406706f2543Smrg				 */
3407706f2543Smrg				if (band[i].a0 == band[i].a1)
3408706f2543Smrg					band[i].a1 = band[i].a0 = 90 * 64 + 1;
3409706f2543Smrg			}
3410706f2543Smrg	}
3411706f2543Smrg	computeAcc (tarc, l, &def, &acc);
3412706f2543Smrg	for (j = 0; j < sweepno; j++) {
3413706f2543Smrg		mask = sweep[j].mask;
3414706f2543Smrg		passRight = passLeft = 0;
3415706f2543Smrg 		if (mask & (1 << rightq)) {
3416706f2543Smrg			if (sweep[j].a0 == righta)
3417706f2543Smrg				passRight = right;
3418706f2543Smrg			else if (sweep[j].a1 == righta) {
3419706f2543Smrg				passLeft = right;
3420706f2543Smrg				flipRight = 1;
3421706f2543Smrg			}
3422706f2543Smrg		}
3423706f2543Smrg		if (mask & (1 << leftq)) {
3424706f2543Smrg			if (sweep[j].a1 == lefta)
3425706f2543Smrg			{
3426706f2543Smrg				if (passLeft)
3427706f2543Smrg					copyEnd = 1;
3428706f2543Smrg				passLeft = left;
3429706f2543Smrg			}
3430706f2543Smrg			else if (sweep[j].a0 == lefta) {
3431706f2543Smrg				if (passRight)
3432706f2543Smrg					copyEnd = 1;
3433706f2543Smrg				passRight = left;
3434706f2543Smrg				flipLeft = 1;
3435706f2543Smrg			}
3436706f2543Smrg		}
3437706f2543Smrg		drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask,
3438706f2543Smrg 			      passRight, passLeft, spdata);
3439706f2543Smrg	}
3440706f2543Smrg	/*
3441706f2543Smrg	 * when copyEnd is set, both ends of the arc were computed
3442706f2543Smrg	 * at the same time; drawQuadrant only takes one end though,
3443706f2543Smrg	 * so the left end will be the only one holding the data.  Copy
3444706f2543Smrg	 * it from there.
3445706f2543Smrg	 */
3446706f2543Smrg	if (copyEnd)
3447706f2543Smrg		*right = *left;
3448706f2543Smrg	/*
3449706f2543Smrg	 * mirror the coordinates generated for the
3450706f2543Smrg	 * faces of the arc
3451706f2543Smrg	 */
3452706f2543Smrg	if (right) {
3453706f2543Smrg		mirrorSppPoint (rightq, &right->clock);
3454706f2543Smrg		mirrorSppPoint (rightq, &right->center);
3455706f2543Smrg		mirrorSppPoint (rightq, &right->counterClock);
3456706f2543Smrg		if (flipRight) {
3457706f2543Smrg			SppPointRec	temp;
3458706f2543Smrg
3459706f2543Smrg			temp = right->clock;
3460706f2543Smrg			right->clock = right->counterClock;
3461706f2543Smrg			right->counterClock = temp;
3462706f2543Smrg		}
3463706f2543Smrg	}
3464706f2543Smrg	if (left) {
3465706f2543Smrg		mirrorSppPoint (leftq,  &left->counterClock);
3466706f2543Smrg		mirrorSppPoint (leftq,  &left->center);
3467706f2543Smrg		mirrorSppPoint (leftq,  &left->clock);
3468706f2543Smrg		if (flipLeft) {
3469706f2543Smrg			SppPointRec	temp;
3470706f2543Smrg
3471706f2543Smrg			temp = left->clock;
3472706f2543Smrg			left->clock = left->counterClock;
3473706f2543Smrg			left->counterClock = temp;
3474706f2543Smrg		}
3475706f2543Smrg	}
3476706f2543Smrg	free(spdata);
3477706f2543Smrg}
3478706f2543Smrg
3479706f2543Smrgstatic void
3480706f2543SmrgdrawQuadrant (
3481706f2543Smrg	struct arc_def		*def,
3482706f2543Smrg	struct accelerators	*acc,
3483706f2543Smrg	int			a0,
3484706f2543Smrg	int			a1,
3485706f2543Smrg	int			mask,
3486706f2543Smrg	miArcFacePtr		right,
3487706f2543Smrg	miArcFacePtr		left,
3488706f2543Smrg	miArcSpanData		*spdata)
3489706f2543Smrg{
3490706f2543Smrg	struct arc_bound	bound;
3491706f2543Smrg	double			yy, x, xalt;
3492706f2543Smrg	int			y, miny, maxy;
3493706f2543Smrg	int			n;
3494706f2543Smrg	miArcSpan		*span;
3495706f2543Smrg
3496706f2543Smrg	def->a0 = ((double) a0) / 64.0;
3497706f2543Smrg	def->a1 = ((double) a1) / 64.0;
3498706f2543Smrg	computeBound (def, &bound, acc, right, left);
3499706f2543Smrg	yy = bound.inner.min;
3500706f2543Smrg	if (bound.outer.min < yy)
3501706f2543Smrg	    yy = bound.outer.min;
3502706f2543Smrg	miny = ICEIL(yy - acc->fromIntY);
3503706f2543Smrg	yy = bound.inner.max;
3504706f2543Smrg	if (bound.outer.max > yy)
3505706f2543Smrg	    yy = bound.outer.max;
3506706f2543Smrg	maxy = floor(yy - acc->fromIntY);
3507706f2543Smrg	y = spdata->k;
3508706f2543Smrg	span = spdata->spans;
3509706f2543Smrg	if (spdata->top)
3510706f2543Smrg	{
3511706f2543Smrg	    if (a1 == 90 * 64 && (mask & 1))
3512706f2543Smrg		newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
3513706f2543Smrg	    span++;
3514706f2543Smrg	}
3515706f2543Smrg	for (n = spdata->count1; --n >= 0; )
3516706f2543Smrg	{
3517706f2543Smrg	    if (y < miny)
3518706f2543Smrg		return;
3519706f2543Smrg	    if (y <= maxy) {
3520706f2543Smrg		arcSpan (y,
3521706f2543Smrg			 span->lx, -span->lx, 0, span->lx + span->lw,
3522706f2543Smrg			 def, &bound, acc, mask);
3523706f2543Smrg		if (span->rw + span->rx)
3524706f2543Smrg		    tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
3525706f2543Smrg	    }
3526706f2543Smrg	    y--;
3527706f2543Smrg	    span++;
3528706f2543Smrg	}
3529706f2543Smrg	if (y < miny)
3530706f2543Smrg	    return;
3531706f2543Smrg	if (spdata->hole)
3532706f2543Smrg	{
3533706f2543Smrg	    if (y <= maxy)
3534706f2543Smrg		arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
3535706f2543Smrg	}
3536706f2543Smrg	for (n = spdata->count2; --n >= 0; )
3537706f2543Smrg	{
3538706f2543Smrg	    if (y < miny)
3539706f2543Smrg		return;
3540706f2543Smrg	    if (y <= maxy)
3541706f2543Smrg		arcSpan (y, span->lx, span->lw, span->rx, span->rw,
3542706f2543Smrg			 def, &bound, acc, mask);
3543706f2543Smrg	    y--;
3544706f2543Smrg	    span++;
3545706f2543Smrg	}
3546706f2543Smrg	if (spdata->bot && miny <= y && y <= maxy)
3547706f2543Smrg	{
3548706f2543Smrg	    n = mask;
3549706f2543Smrg	    if (y == miny)
3550706f2543Smrg		n &= 0xc;
3551706f2543Smrg	    if (span->rw <= 0) {
3552706f2543Smrg		arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
3553706f2543Smrg			  def, &bound, acc, n);
3554706f2543Smrg		if (span->rw + span->rx)
3555706f2543Smrg		    tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
3556706f2543Smrg	    }
3557706f2543Smrg	    else
3558706f2543Smrg		arcSpan0 (span->lx, span->lw, span->rx, span->rw,
3559706f2543Smrg			  def, &bound, acc, n);
3560706f2543Smrg	    y--;
3561706f2543Smrg	}
3562706f2543Smrg	while (y >= miny) {
3563706f2543Smrg	    yy = y + acc->fromIntY;
3564706f2543Smrg	    if (def->w == def->h) {
3565706f2543Smrg		xalt = def->w - def->l;
3566706f2543Smrg		x = -sqrt(xalt * xalt - yy * yy);
3567706f2543Smrg	    } else {
3568706f2543Smrg		x = tailX(yy, def, &bound, acc);
3569706f2543Smrg		if (acc->left.valid && boundedLe (yy, bound.left)) {
3570706f2543Smrg		    xalt = intersectLine (yy, acc->left);
3571706f2543Smrg		    if (xalt < x)
3572706f2543Smrg			x = xalt;
3573706f2543Smrg		}
3574706f2543Smrg		if (acc->right.valid && boundedLe (yy, bound.right)) {
3575706f2543Smrg		    xalt = intersectLine (yy, acc->right);
3576706f2543Smrg		    if (xalt < x)
3577706f2543Smrg			x = xalt;
3578706f2543Smrg		}
3579706f2543Smrg	    }
3580706f2543Smrg	    arcSpan (y,
3581706f2543Smrg		     ICEIL(acc->fromIntX - x), 0,
3582706f2543Smrg		     ICEIL(acc->fromIntX + x), 0,
3583706f2543Smrg		     def, &bound, acc, mask);
3584706f2543Smrg	    y--;
3585706f2543Smrg	}
3586706f2543Smrg}
3587