/**
 ** scanellp.c ---- draw the outline or scan fill an ellipse
 **
 ** Copyright (c) 1995 Csaba Biegl, 820 Stirrup Dr, Nashville, TN 37221
 ** [e-mail: csaba@vuse.vanderbilt.edu]
 **
 ** This file is part of the GRX graphics library.
 **
 ** The GRX graphics library is free software; you can redistribute it
 ** and/or modify it under some conditions; see the "copying.grx" file
 ** for details.
 **
 ** This library is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 **
 **/

#include "libgrx.h"
#include "arith.h"
#include "clipping.h"
#include "shapes.h"

#define MAXR 120  /* max radius for which Bresenham works */

void _GrScanEllipse(int xc,int yc,int xa,int ya,GrFiller *f,GrFillArg c,int filled)
{
    int x1,x2,y1,y2;

    if(xa < 0) xa = (-xa);
    if(ya < 0) ya = (-ya);
    x1 = xc - xa; y1 = yc - ya;
    x2 = xc + xa; y2 = yc + ya;
    clip_ordbox(CURC,x1,y1,x2,y2);
    mouse_block(CURC,x1,y1,x2,y2);
    if((xa == 0) || (ya == 0)) {
        (*f->line)((x1 + CURC->gc_xoffset),
                   (y1 + CURC->gc_yoffset),
                   (x2 - x1),(y2 - y1),c);
    }
    else if((xa > MAXR) || (ya > MAXR)) {   /* Bresenham would overflow !! */
        int points[GR_MAX_ELLIPSE_POINTS+1][2];
        int count = GrGenerateEllipse(xc,yc,xa,ya,points);
        if(filled) _GrScanConvexPoly(count,points,f,c);
        else       _GrDrawPolygon(count,points,f,c,TRUE);
    }
    else {
        int scans[ya+1];
        int  row   = ya;
        int  col   = 0;
        if(scans != NULL) {
            long yasq  = umul32(ya,ya);
            long xasq  = umul32(xa,xa);
            long xasq2 = xasq + xasq;
            long yasq2 = yasq + yasq;
            long xasq4 = xasq2 + xasq2;
            long yasq4 = yasq2 + yasq2;
            long error = (xasq2 * (row - 1) * row) + (yasq2 * (1 - xasq)) + xasq;
            while((xasq * row) > (yasq * col)) {
                if(error >= 0) {
                    scans[row] = col;
                    row--;
                    error -= xasq4 * row;
                }
                error += yasq2 * (3 + (col << 1));
                col++;
            }
            error = (yasq2 * (col + 1) * col) + 
                    (xasq2 * ((row * (row - 2)) + 1)) +
                    (yasq  * (1 - xasq2));
            while(row >= 0) {
                scans[row] = col;
                if(error <= 0) {
                    col++;
                    error += yasq4 * col;
                }
                row--;
                error += xasq2 * (2 - (row << 1));
            }
            for(row = y1; row <= y2; row++) {
                col = iabs(yc - row);
                if(!filled && (col < ya)) {
                    x1 = xc - scans[col];
                    x2 = xc - scans[col + 1];
                    if(x1 < x2) x2--;
                    do {
                        clip_ordxrange_(CURC,x1,x2,break,CLIP_EMPTY_MACRO_ARG);
                        (*f->scan)((x1  + CURC->gc_xoffset),
                                   (row + CURC->gc_yoffset),
                                   (x2  - x1 + 1),c);
                    } while (0);
                    x1 = xc + scans[col + 1];
                    x2 = xc + scans[col];
                    if(x1 < x2) x1++;
                }
                else {
                    x1 = xc - scans[col];
                    x2 = xc + scans[col];
                }
                clip_ordxrange_(CURC,x1,x2,continue,CLIP_EMPTY_MACRO_ARG);
                (*f->scan)((x1  + CURC->gc_xoffset),
                           (row + CURC->gc_yoffset),
                           (x2  - x1 + 1),c);
            }
        }
    }
    mouse_unblock();
}
