/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

package com.kitfox.svg.batik;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;

/**
 * Provides the actual implementation for the RadialGradientPaint.
 * This is where the pixel processing is done.  A RadialGradienPaint
 * only supports circular gradients, but it should be possible to scale
 * the circle to look approximately elliptical, by means of a
 * gradient transform passed into the RadialGradientPaint constructor.
 *
 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
 * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
 * @version $Id: RadialGradientPaintContext.java,v 1.2 2005/10/12 20:36:55 kitfox Exp $
 *
 */
final class RadialGradientPaintContext extends MultipleGradientPaintContext {  
    
    /** True when (focus == center)  */
    private boolean isSimpleFocus = false;

    /** True when (cycleMethod == NO_CYCLE) */
    private boolean isNonCyclic = false;
       
    /** Radius of the outermost circle defining the 100% gradient stop. */
    private float radius;   
    
    /** Variables representing center and focus points. */
    private float centerX, centerY, focusX, focusY;     

    /** Radius of the gradient circle squared. */
    private float radiusSq; 
        
    /** Constant part of X, Y user space coordinates. */
    private float constA, constB;
       
    /** This value represents the solution when focusX == X.  It is called
     * trivial because it is easier to calculate than the general case.
     */
    private float trivial;       

    private static final int FIXED_POINT_IMPL = 1;
    private static final int DEFAULT_IMPL     = 2;
    private static final int ANTI_ALIAS_IMPL  = 3;

    private int fillMethod;
    
    /** Amount for offset when clamping focus. */
    private static final float SCALEBACK = .97f;
    
    /** 
     * Constructor for RadialGradientPaintContext.
     *
     *  @param cm {@link ColorModel} that receives
     *  the <code>Paint</code> data. This is used only as a hint.
     *
     *  @param deviceBounds the device space bounding box of the 
     *  graphics primitive being rendered
     *
     *  @param userBounds the user space bounding box of the 
     *  graphics primitive being rendered
     * 
     *  @param t the {@link AffineTransform} from user
     *  space into device space (gradientTransform should be 
     *  concatenated with this)    
     *
     *  @param hints the hints that the context object uses to choose
     *  between rendering alternatives
     *       
     *  @param cx the center point in user space of the circle defining 
     *  the gradient.  The last color of the gradient is mapped to the 
     *  perimeter of this circle X coordinate
     *
     *  @param cy the center point in user space of the circle defining 
     *  the gradient.  The last color of the gradient is mapped to the 
     *  perimeter of this circle Y coordinate
     *     
     *  @param r the radius of the circle defining the extents of the 
     *  color gradient
     *
     *  @param fx the point in user space to which the first color is mapped
     *  X coordinate
     *
     *  @param fy the point in user space to which the first color is mapped
     *  Y coordinate
     *
     *  @param fractions the fractions specifying the gradient distribution
     *
     *  @param colors the gradient colors
     *
     *  @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
     *
     *  @param colorSpace which colorspace to use for interpolation, 
     *  either SRGB or LINEAR_RGB
     *
     */
    public RadialGradientPaintContext(ColorModel cm,
                                      Rectangle deviceBounds,
                                      Rectangle2D userBounds,
                                      AffineTransform t,
                                      RenderingHints hints,
                                      float cx, float cy,
                                      float r,
                                      float fx, float fy,
                                      float[] fractions,
                                      Color[] colors,
                                      MultipleGradientPaint.CycleMethodEnum 
                                      cycleMethod,
                                      MultipleGradientPaint.ColorSpaceEnum 
                                      colorSpace)
        throws NoninvertibleTransformException
    {       	
        super(cm, deviceBounds, userBounds, t, hints, fractions, colors, 
              cycleMethod, colorSpace);

        //copy some parameters.
        centerX = cx;
        centerY = cy;	
        focusX = fx;
        focusY = fy;
        radius = r;

        this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
        this.isNonCyclic = (cycleMethod == RadialGradientPaint.NO_CYCLE);
	
        //for use in the quadractic equation
        radiusSq = radius * radius;

        float dX = focusX - centerX;
        float dY = focusY - centerY;

        double dist = Math.sqrt((dX * dX) + (dY * dY));

        //test if distance from focus to center is greater than the radius
        if (dist > radius* SCALEBACK) { //clamp focus to radius
          double angle = Math.atan2(dY, dX);
			
          //x = r cos theta, y = r sin theta
          focusX = (float)(SCALEBACK * radius * Math.cos(angle)) + centerX;
          
          focusY = (float)(SCALEBACK * radius * Math.sin(angle)) + centerY;
        }

        //calculate the solution to be used in the case where X == focusX
        //in cyclicCircularGradientFillRaster
        dX = focusX - centerX;
        trivial = (float)Math.sqrt(radiusSq - (dX * dX));

        // constant parts of X, Y user space coordinates 
        constA = a02 - centerX;
        constB = a12 - centerY;

        Object colorRend;
        Object rend;
        //hints can be null on Mac OSX
        if (hints == null)
        {
            colorRend = RenderingHints.VALUE_COLOR_RENDER_DEFAULT;
            rend = RenderingHints.VALUE_RENDER_DEFAULT;
        }
        else
        {
            colorRend = hints.get(RenderingHints.KEY_COLOR_RENDERING);
            rend      = hints.get(RenderingHints.KEY_RENDERING);
        }

        fillMethod = 0;

        if ((rend      == RenderingHints.VALUE_RENDER_QUALITY) ||
            (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)) {
            // System.out.println("AAHints set: " + rend + ", " + colorRend);
            fillMethod = ANTI_ALIAS_IMPL;
        }

        if ((rend      == RenderingHints.VALUE_RENDER_SPEED) ||
            (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)) {
            // System.out.println("SPHints set: " + rend + ", " + colorRend);
            fillMethod = DEFAULT_IMPL;
        }

        // We are in the 'default' case, no hint or hint set to
        // DEFAULT values...
        if (fillMethod == 0) {
            // For now we will always use the 'default' impl if 
            // one is not specified.
            fillMethod = DEFAULT_IMPL;

            if (false) {
                // This could be used for a 'smart' choice in
                // the default case, if the gradient has obvious
                // discontinuites use AA, otherwise default
                if (hasDiscontinuity) {
                    fillMethod = ANTI_ALIAS_IMPL;
                } else {
                    fillMethod = DEFAULT_IMPL;
                }
            }
        }

        if ((fillMethod == DEFAULT_IMPL) &&
            (isSimpleFocus && isNonCyclic && isSimpleLookup)) {
            this.calculateFixedPointSqrtLookupTable();
            fillMethod = FIXED_POINT_IMPL;
        }
    }
    
    /**
     * Return a Raster containing the colors generated for the graphics
     * operation.
     * @param x,y,w,h The area in device space for which colors are
     * generated.
     */
    protected void fillRaster(int pixels[], int off, int adjust,
                              int x, int y, int w, int h) {
        switch(fillMethod) {
        case FIXED_POINT_IMPL:
            // System.out.println("Calling FP");
            fixedPointSimplestCaseNonCyclicFillRaster(pixels, off, adjust, x, 
                                                      y, w, h);
            break;
        case ANTI_ALIAS_IMPL:
            // System.out.println("Calling AA");
            antiAliasFillRaster(pixels, off, adjust, x, y, w, h);
            break;
        case DEFAULT_IMPL:
        default:
            // System.out.println("Calling Default");
            cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
        }
    }    
    
    /**
     * This code works in the simplest of cases, where the focus == center 
     * point, the gradient is noncyclic, and the gradient lookup method is 
     * fast (single array index, no conversion necessary).
     *
     */         
    private void fixedPointSimplestCaseNonCyclicFillRaster(int pixels[], 
                                                           int off,
                                                           int adjust, 
                                                           int x, int y, 
                                                           int w, int h) {
        float iSq=0;  // Square distance index
        final float indexFactor = fastGradientArraySize / radius;      

        //constant part of X and Y coordinates for the entire raster
        final float constX = (a00*x) + (a01*y) + constA;
        final float constY = (a10*x) + (a11*y) + constB;
        final float deltaX = indexFactor * a00; //incremental change in dX
        final float deltaY = indexFactor * a10; //incremental change in dY
        float dX, dY; //the current distance from center
        final int fixedArraySizeSq=
            (fastGradientArraySize * fastGradientArraySize);
        float g, gDelta, gDeltaDelta, temp; //gradient square value
        int gIndex; // integer number used to index gradient array
        int iSqInt; // Square distance index       		   
	
        int end, j; //indexing variables
        int indexer = off;//used to index pixels array

        temp        = ((deltaX * deltaX) + (deltaY * deltaY));
        gDeltaDelta = ((temp * 2));

        if (temp > fixedArraySizeSq) {
            // This combination of scale and circle radius means
            // essentially no pixels will be anything but the end
            // stop color.  This also avoids math problems.
            final int val = gradientOverflow;
            for(j = 0; j < h; j++){ //for every row
                //for every column (inner loop begins here)
                for (end = indexer+w; indexer < end; indexer++) 
                    pixels[indexer] = val;
                indexer += adjust;
            }
            return;
        }

        // For every point in the raster, calculate the color at that point
        for(j = 0; j < h; j++){ //for every row
            //x and y (in user space) of the first pixel of this row
            dX = indexFactor * ((a01*j) + constX);
            dY = indexFactor * ((a11*j) + constY);	   	   

            // these values below here allow for an incremental calculation
            // of dX^2 + dY^2 

            //initialize to be equal to distance squared
            g = (((dY * dY) + (dX * dX)) );
            gDelta =  (((((deltaY * dY) + (deltaX * dX))* 2) + 
                        temp));	 
	    
            //for every column (inner loop begins here)
            for (end = indexer+w; indexer < end; indexer++) {	       
                //determine the distance to the center
		
                //since this is a non cyclic fill raster, crop at "1" and 0
                if (g >= fixedArraySizeSq) {
                    pixels[indexer] = gradientOverflow;
                }
		
                // This should not happen as gIndex is a square
                // quantity. Code commented out on purpose, can't underflow.
                // else if (g < 0) {
                //    gIndex = 0;		    
                // }
		
                else {
                    iSq = (g * invSqStepFloat);
                    
                    iSqInt = (int)iSq; //chop off fractional part
                    iSq -= iSqInt;		    
                    gIndex = sqrtLutFixed[iSqInt];
                    gIndex += (int)(iSq * (sqrtLutFixed[iSqInt + 1]-gIndex));
                    pixels[indexer] = gradient[gIndex]; 
                }
		
				
                //incremental calculation
                g += gDelta;
                gDelta += gDeltaDelta;		
            }	  
            indexer += adjust;
        }
    }

    /** Length of a square distance intervale in the lookup table */
    private float invSqStepFloat; 
    
    /** Used to limit the size of the square root lookup table */
    private int MAX_PRECISION = 256;
    
    /** Square root lookup table */
    private int sqrtLutFixed[] = new int[MAX_PRECISION];
    
    /**
     * Build square root lookup table
     */       
    private void calculateFixedPointSqrtLookupTable() {	      
        float sqStepFloat;
        sqStepFloat = ((fastGradientArraySize  * fastGradientArraySize) 
                       / (MAX_PRECISION - 2));
	
        // The last two values are the same so that linear square root 
        // interpolation can happen on the maximum reachable element in the 
        // lookup table (precision-2)
        int i;
        for (i = 0; i < MAX_PRECISION - 1; i++) {
            sqrtLutFixed[i] = (int)(Math.sqrt(i*sqStepFloat));
        }
        sqrtLutFixed[i] = sqrtLutFixed[i-1];	
        invSqStepFloat = 1/sqStepFloat;
    }
    
    /** Fill the raster, cycling the gradient colors when a point falls outside
     *  of the perimeter of the 100% stop circle.          
     * 
     *  This calculation first computes the intersection point of the line
     *  from the focus through the current point in the raster, and the
     *  perimeter of the gradient circle.
     * 
     *  Then it determines the percentage distance of the current point along
     *  that line (focus is 0%, perimeter is 100%). 
     *
     *  Equation of a circle centered at (a,b) with radius r:
     *  (x-a)^2 + (y-b)^2 = r^2
     *  Equation of a line with slope m and y-intercept b
     *  y = mx + b
     *  replacing y in the cirlce equation and solving using the quadratic
     *  formula produces the following set of equations.  Constant factors have
     *  been extracted out of the inner loop.
     *
     */   
    private void cyclicCircularGradientFillRaster(int pixels[], int off, 
                                                  int adjust, 
                                                  int x, int y, 
                                                  int w, int h) {
        // Constant part of the C factor of the quadratic equation
        final double constC = 
            -(radiusSq) + (centerX * centerX) + (centerY * centerY);
        double A; //coefficient of the quadratic equation (Ax^2 + Bx + C = 0)
        double B; //coefficient of the quadratic equation
        double C; //coefficient of the quadratic equation
        double slope; //slope of the focus-perimeter line
        double yintcpt; //y-intercept of the focus-perimeter line
        double solutionX;//intersection with circle X coordinate
        double solutionY;//intersection with circle Y coordinate       
       	final float constX = (a00*x) + (a01*y) + a02;//const part of X coord
        final float constY = (a10*x) + (a11*y) + a12; //const part of Y coord
       	final float precalc2 = 2 * centerY;//const in inner loop quad. formula
        final float precalc3 =-2 * centerX;//const in inner loop quad. formula
        float X; // User space point X coordinate 
        float Y; // User space point Y coordinate
        float g;//value between 0 and 1 specifying position in the gradient
        float det; //determinant of quadratic formula (should always be >0)
        float currentToFocusSq;//sq distance from the current pt. to focus
        float intersectToFocusSq;//sq distance from the intersect pt. to focus
        float deltaXSq; //temp variable for a change in X squared.
        float deltaYSq; //temp variable for a change in Y squared.
        int indexer = off; //index variable for pixels array
        int i, j; //indexing variables for FOR loops
        int pixInc = w+adjust;//incremental index change for pixels array

        for (j = 0; j < h; j++) { //for every row
	    
            X = (a01*j) + constX; //constants from column to column
            Y = (a11*j) + constY;
	    
            //for every column (inner loop begins here)
            for (i = 0; i < w; i++) {	       			
	
                // special case to avoid divide by zero or very near zero
                if (((X-focusX)>-0.000001) &&
                    ((X-focusX)< 0.000001)) {		   
                    solutionX = focusX;
		    
                    solutionY = centerY;
		    
                    solutionY += (Y > focusY)?trivial:-trivial;
                }
		
                else {    
		    
                    //slope of the focus-current line
                    slope =   (Y - focusY) / (X - focusX);
		    
                    yintcpt = Y - (slope * X); //y-intercept of that same line
		    
                    //use the quadratic formula to calculate the intersection
                    //point		  
                    A = (slope * slope) + 1; 
		    
                    B =  precalc3 + (-2 * slope * (centerY - yintcpt));
		    
                    C =  constC + (yintcpt* (yintcpt - precalc2));
		    
                    det = (float)Math.sqrt((B * B) - ( 4 * A * C));
		    
                    solutionX = -B;
		    
                    //choose the positive or negative root depending
                    //on where the X coord lies with respect to the focus.
                    solutionX += (X < focusX)?-det:det;
		    
                    solutionX = solutionX / (2 * A);//divisor
		    
                    solutionY = (slope * solutionX) + yintcpt;
                }	                    	

                //calculate the square of the distance from the current point 
                //to the focus and the square of the distance from the 
                //intersection point to the focus. Want the squares so we can
                //do 1 square root after division instead of 2 before.

                deltaXSq = (float)solutionX - focusX;
                deltaXSq = deltaXSq * deltaXSq;

                deltaYSq = (float)solutionY - focusY;
                deltaYSq = deltaYSq * deltaYSq;

                intersectToFocusSq = deltaXSq + deltaYSq;

                deltaXSq = X - focusX;
                deltaXSq = deltaXSq * deltaXSq;

                deltaYSq = Y - focusY;
                deltaYSq = deltaYSq * deltaYSq;

                currentToFocusSq = deltaXSq + deltaYSq;

                //want the percentage (0-1) of the current point along the 
                //focus-circumference line
                g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);

                //Get the color at this point
                pixels[indexer + i] = indexIntoGradientsArrays(g);
		
                X += a00; //incremental change in X, Y
                Y += a10;	
            } //end inner loop
            indexer += pixInc;
        } //end outer loop
    }


    /** Fill the raster, cycling the gradient colors when a point
     *  falls outside of the perimeter of the 100% stop circle. Use
     *  the anti-aliased gradient lookup.
     *
     *  This calculation first computes the intersection point of the line
     *  from the focus through the current point in the raster, and the
     *  perimeter of the gradient circle.
     * 
     *  Then it determines the percentage distance of the current point along
     *  that line (focus is 0%, perimeter is 100%). 
     *
     *  Equation of a circle centered at (a,b) with radius r:
     *  (x-a)^2 + (y-b)^2 = r^2
     *  Equation of a line with slope m and y-intercept b
     *  y = mx + b
     *  replacing y in the cirlce equation and solving using the quadratic
     *  formula produces the following set of equations.  Constant factors have
     *  been extracted out of the inner loop.
     * */
    private void antiAliasFillRaster(int pixels[], int off, 
                                     int adjust, 
                                     int x, int y, 
                                     int w, int h) {
        // Constant part of the C factor of the quadratic equation
        final double constC = 
            -(radiusSq) + (centerX * centerX) + (centerY * centerY);
        //coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
       	final float precalc2 = 2 * centerY;//const in inner loop quad. formula
        final float precalc3 =-2 * centerX;//const in inner loop quad. formula

        //const part of X,Y coord (shifted to bottom left corner of pixel.
       	final float constX = (a00*(x-.5f)) + (a01*(y+.5f)) + a02;
        final float constY = (a10*(x-.5f)) + (a11*(y+.5f)) + a12;
        float X; // User space point X coordinate 
        float Y; // User space point Y coordinate
        int i, j; //indexing variables for FOR loops
        int indexer = off-1; //index variable for pixels array

        // Size of a pixel in user space.
        double pixSzSq = (float)(a00*a00+a01*a01+a10*a10+a11*a11);
        double [] prevGs = new double[w+1];
        double deltaXSq, deltaYSq;
        double solutionX, solutionY;
        double slope, yintcpt, A, B, C, det;
        double intersectToFocusSq, currentToFocusSq;
        double g00, g01, g10, g11;

        // Set X,Y to top left corner of first pixel of first row.
        X = constX - a01;
        Y = constY - a11;

        // Calc top row of g's.
        for (i=0; i <= w; i++) {
            // special case to avoid divide by zero or very near zero
            if (((X-focusX)>-0.000001) &&
                ((X-focusX)< 0.000001)) {		   
                solutionX = focusX;
                solutionY = centerY;
                solutionY += (Y > focusY)?trivial:-trivial;
            }
            else { 
                // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
                // Formula line:        Y = Slope*x + Y0;
                // 
                // So you substitue line into Circle and apply
                // Quadradic formula.


                //slope of the focus-current line
                slope =   (Y - focusY) / (X - focusX);
		    
                yintcpt = Y - (slope * X); //y-intercept of that same line
		    
                //use the quadratic formula to calculate the intersection
                //point		  
                A = (slope * slope) + 1; 
		    
                B =  precalc3 + (-2 * slope * (centerY - yintcpt));
		    
                C =  constC + (yintcpt* (yintcpt - precalc2));
		    
                det = Math.sqrt((B * B) - ( 4 * A * C));
		    
                solutionX = -B;
		    
                //choose the positive or negative root depending
                //on where the X coord lies with respect to the focus.
                solutionX += (X < focusX)?-det:det;
		    
                solutionX = solutionX / (2 * A);//divisor
		    
                solutionY = (slope * solutionX) + yintcpt;
            }	                    	

            //calculate the square of the distance from the current point 
            //to the focus and the square of the distance from the 
            //intersection point to the focus. Want the squares so we can
            //do 1 square root after division instead of 2 before.
            deltaXSq = solutionX - focusX;
            deltaXSq = deltaXSq * deltaXSq;
        
            deltaYSq = solutionY - focusY;
            deltaYSq = deltaYSq * deltaYSq;
        
            intersectToFocusSq = deltaXSq + deltaYSq;
        
            deltaXSq = X - focusX;
            deltaXSq = deltaXSq * deltaXSq;
        
            deltaYSq = Y - focusY;
            deltaYSq = deltaYSq * deltaYSq;
        
            currentToFocusSq = deltaXSq + deltaYSq;
        
            //want the percentage (0-1) of the current point along the 
            //focus-circumference line
            prevGs[i] = Math.sqrt(currentToFocusSq / intersectToFocusSq);

            X += a00; //incremental change in X, Y
            Y += a10;	
        }

        for (j = 0; j < h; j++) { //for every row
	    
            // Set X,Y to bottom edge of pixel row.
            X = (a01*j) + constX; //constants from row to row
            Y = (a11*j) + constY;

            g10 = prevGs[0];
            // special case to avoid divide by zero or very near zero
            if (((X-focusX)>-0.000001) &&
                ((X-focusX)< 0.000001)) {		   
                solutionX = focusX;
                solutionY = centerY;
                solutionY += (Y > focusY)?trivial:-trivial;
            }
            else { 
                // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
                // Formula line:        Y = Slope*x + Y0;
                // 
                // So you substitue line into Circle and apply
                // Quadradic formula.


                //slope of the focus-current line
                slope =   (Y - focusY) / (X - focusX);
		    
                yintcpt = Y - (slope * X); //y-intercept of that same line
		    
                //use the quadratic formula to calculate the intersection
                //point		  
                A = (slope * slope) + 1; 
		    
                B =  precalc3 + (-2 * slope * (centerY - yintcpt));
		    
                C =  constC + (yintcpt* (yintcpt - precalc2));
		    
                det = Math.sqrt((B * B) - ( 4 * A * C));
		    
                solutionX = -B;
		    
                //choose the positive or negative root depending
                //on where the X coord lies with respect to the focus.
                solutionX += (X < focusX)?-det:det;
		    
                solutionX = solutionX / (2 * A);//divisor
		    
                solutionY = (slope * solutionX) + yintcpt;
            }	                    	

            //calculate the square of the distance from the current point 
            //to the focus and the square of the distance from the 
            //intersection point to the focus. Want the squares so we can
            //do 1 square root after division instead of 2 before.
            deltaXSq = solutionX - focusX;
            deltaXSq = deltaXSq * deltaXSq;
        
            deltaYSq = solutionY - focusY;
            deltaYSq = deltaYSq * deltaYSq;
        
            intersectToFocusSq = deltaXSq + deltaYSq;
        
            deltaXSq = X - focusX;
            deltaXSq = deltaXSq * deltaXSq;
        
            deltaYSq = Y - focusY;
            deltaYSq = deltaYSq * deltaYSq;
                
            currentToFocusSq = deltaXSq + deltaYSq;
            g11 = Math.sqrt(currentToFocusSq / intersectToFocusSq);
            prevGs[0] = g11;
            
            X += a00; //incremental change in X, Y
            Y += a10;	

            //for every column (inner loop begins here)
            for (i=1; i <= w; i++) {
                g00 = g10;
                g01 = g11;
                g10 = prevGs[i];

                // special case to avoid divide by zero or very near zero
                if (((X-focusX)>-0.000001) &&
                    ((X-focusX)< 0.000001)) {		   
                    solutionX = focusX;
                    solutionY = centerY;
                    solutionY += (Y > focusY)?trivial:-trivial;
                }
                else { 
                    // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
                    // Formula line:        Y = Slope*x + Y0;
                    // 
                    // So you substitue line into Circle and apply
                    // Quadradic formula.


                    //slope of the focus-current line
                    slope =   (Y - focusY) / (X - focusX);
		    
                    yintcpt = Y - (slope * X); //y-intercept of that same line
		    
                    //use the quadratic formula to calculate the intersection
                    //point		  
                    A = (slope * slope) + 1; 
		    
                    B =  precalc3 + (-2 * slope * (centerY - yintcpt));
		    
                    C =  constC + (yintcpt* (yintcpt - precalc2));
		    
                    det = Math.sqrt((B * B) - ( 4 * A * C));
		    
                    solutionX = -B;
		    
                    //choose the positive or negative root depending
                    //on where the X coord lies with respect to the focus.
                    solutionX += (X < focusX)?-det:det;
		    
                    solutionX = solutionX / (2 * A);//divisor
		    
                    solutionY = (slope * solutionX) + yintcpt;
                }	                    	

                //calculate the square of the distance from the current point 
                //to the focus and the square of the distance from the 
                //intersection point to the focus. Want the squares so we can
                //do 1 square root after division instead of 2 before.
                deltaXSq = solutionX - focusX;
                deltaXSq = deltaXSq * deltaXSq;
        
                deltaYSq = solutionY - focusY;
                deltaYSq = deltaYSq * deltaYSq;
        
                intersectToFocusSq = deltaXSq + deltaYSq;
        
                deltaXSq = X - focusX;
                deltaXSq = deltaXSq * deltaXSq;
        
                deltaYSq = Y - focusY;
                deltaYSq = deltaYSq * deltaYSq;
        
                currentToFocusSq = deltaXSq + deltaYSq;
                g11 = Math.sqrt(currentToFocusSq / intersectToFocusSq);
                prevGs[i] = g11;

                //Get the color at this point
                pixels[indexer+i] = indexGradientAntiAlias
                    ((float)((g00+g01+g10+g11)/4), 
                     (float)Math.max(Math.abs(g11-g00),
                                     Math.abs(g10-g01)));

                X += a00; //incremental change in X, Y
                Y += a10;	
            } //end inner loop
            indexer += (w+adjust);
        } //end outer loop
    }
}
