/*
 * @(#)PlotMarkAlgorithm.java    2.6.0   22 August 2007
 *
 * Copyright 2007
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;

import java.awt.*;
import java.awt.geom.*;

/**
 * <p>Class <code>PlotMarkAlgorithm</code> defines the requirements for an
 * algorithm that will produce a scalable shape defined in the neighborhood
 * of a given point.  This class is intended to be used in constructors for
 * the class <code>PlotMark</code>.</p>
 *
 * <p>This class provides several static instances of itself.</p>
 * 
 * <p>The following changes were made in 2.6.0:</p>
 * 
 * <ul>
 *   <li>The minimum <code>size</code> parameter was reduced from
 *       1 to 0 to permit the base case of each shape to be created.
 *   </li>
 *   <li>A new circle algorithm named <code>BetterCircle</code> was
 *       introduced that produces elegant, symmetrical circles for
 *       small values of the <code>size</code> parameter.
 *   </li>
 *   <li>The methods <code>QuarterCircleData</code> and
 *       <code>BetterCirclePath</code> on which the algorithm
 *       <code>BetterCircle</code> is based are made available as
 *       public static methods.
 *   </li>
 *   <li>The earlier circle algorithm was renamed as
 *       <code>JavaCircle</code> and is still available.
 *   </li>
 *   <li>Algorithms for a &ldquo;slider thumb&rdquo; shape have been
 *       added.
 *   </li>
 * </ul>
 *
 * @author  Richard Rasala
 * @version 2.6.0
 * @since   2.4.0
 */
public abstract class PlotMarkAlgorithm
{
    
    /**
     * <p>Returns a shape constructed in the neighborhood of the given point
     * and scaled with the given size.</p>
     *
     * <p>If the given point is <code>null</code>, returns an empty shape.</p>
     *
     * <p>Otherwise calls <code>makeShape(double, double, int)</code>.</p>
     *
     * @param p the point defining where to place the shape
     * @param size the scale size
     */
    public final Shape makeShape(Point2D p, int size) {
        if (p == null)
            return new GeneralPath();
        
        return makeShape(p.getX(), p.getY(), size);
    }
    
    
    /**
     * <p>Returns a shape constructed in the neighborhood of the given point
     * and scaled with the given size.</p>
     *
     * <p>When defined this method may return an empty shape but should not
     * return <code>null</code>.</p>
     *
     * <p>The scale size should be forced to at least 0 before building the
     * shape.</p>
     *
     * @param x the x-coordinate of the point defining where
     *          to place the shape
     * @param y the y-coordinate of the point defining where
     *          to place the shape
     * @param size the scale size
     */
    public abstract Shape makeShape(double x, double y, int size);
    
    
    /**
     * <p>The horizontal bar algorithm.</p>
     *
     * <p>Produces the line segment path
     * <code>(x-size,y)</code> to <code>(x+size,y)</code>.</p>
     *
     * <p>When used in a <code>PlotMark</code>, the fill setting should
     * be <code>false</code>.</p>
     */
    public static final PlotMarkAlgorithm HBar = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) (x - s), (float) y);
            path.lineTo((float) (x + s), (float) y);
            
            return path;
        }
    };
    
    
    /**
     * <p>The vertical bar algorithm.</p>
     *
     * <p>Produces the line segment path
     * <code>(x,y-size)</code> to <code>(x,y+size)</code>.</p>
     *
     * <p>When used in a <code>PlotMark</code>, the fill setting should
     * be <code>false</code>.</p>
     */
    public static final PlotMarkAlgorithm VBar = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) (y - s));
            path.lineTo((float) x, (float) (y + s));
            
            return path;
        }
    };
    
    
    /**
     * <p>The plus sign algorithm.</p>
     *
     * <p>Produces the combined path with 2 line segments:</p>
     *
     * <ul>
     *   <li><code>(x-size,y)</code> to <code>(x+size,y)</code></li>
     *   <li><code>(x,y-size)</code> to <code>(x,y+size)</code></li>
     * </ul>
     *
     * <p>When used in a <code>PlotMark</code>, the fill setting should
     * be <code>false</code>.</p>
     */
    public static final PlotMarkAlgorithm Plus = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) (x - s), (float) y);
            path.lineTo((float) (x + s), (float) y);
            
            path.moveTo((float) x, (float) (y - s));
            path.lineTo((float) x, (float) (y + s));
            
            return path;
        }
    };
    
    
    /**
     * <p>The cross sign algorithm.</p>
     *
     * <p>Produces the combined path with 2 line segments:</p>
     *
     * <ul>
     *   <li><code>(x-size,y-size)</code> to <code>(x+size,y+size)</code></li>
     *   <li><code>(x+size,y-size)</code> to <code>(x-size,y+size)</code></li>
     * </ul>
     *
     * <p>When used in a <code>PlotMark</code>, the fill setting should
     * be <code>false</code>.</p>
     */
    public static final PlotMarkAlgorithm Cross = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) (x - s), (float) (y - s));
            path.lineTo((float) (x + s), (float) (y + s));
            
            path.moveTo((float) (x + s), (float) (y - s));
            path.lineTo((float) (x - s), (float) (y + s));
            
            return path;
        }
    };
    
    
    /**
     * <p>The asterisk algorithm.</p>
     *
     * <p>Produces the combined path with 4 line segments:</p>
     *
     * <ul>
     *   <li><code>(x-size,y)</code> to <code>(x+size,y)</code></li>
     *   <li><code>(x,y-size)</code> to <code>(x,y+size)</code></li>
     *   <li><code>(x-size,y-size)</code> to <code>(x+size,y+size)</code></li>
     *   <li><code>(x+size,y-size)</code> to <code>(x-size,y+size)</code></li>
     * </ul>
     *
     * <p>When used in a <code>PlotMark</code>, the fill setting should
     * be <code>false</code>.</p>
     */
    public static final PlotMarkAlgorithm Asterisk = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) (x - s), (float) y);
            path.lineTo((float) (x + s), (float) y);
            
            path.moveTo((float) x, (float) (y - s));
            path.lineTo((float) x, (float) (y + s));
            
            path.moveTo((float) (x - s), (float) (y - s));
            path.lineTo((float) (x + s), (float) (y + s));
            
            path.moveTo((float) (x + s), (float) (y - s));
            path.lineTo((float) (x - s), (float) (y + s));
            
            return path;
        }
    };
    
    
    /**
     * <p>The square algorithm.</p>
     *
     * <p>Returns <code>new XSquare(x, y, size)</code>.</p>
     */
    public static final PlotMarkAlgorithm Square = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            return new XSquare(x, y, size);
        }
    };
    
    
    /**
     * <p>The diamond algorithm.</p>
     *
     * <p>Produces the closed path with 4 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y-size)</code></li>
     *   <li><code>(x+size,y)</code></li>
     *   <li><code>(x,y+size)</code></li>
     *   <li><code>(x-size,y)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm Diamond = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) (y - s));
            path.lineTo((float) (x + s), (float) y);
            path.lineTo((float) x, (float) (y + s));
            path.lineTo((float) (x - s), (float) y);
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The better circle algorithm for creation of plot marks.</p>
     *
     * <p>Uses the method <code>BetterCirclePath</code> which is
     * in turn based on the method <code>QuarterCircleData</code>.
     * The path produced has 8-fold symmetry about the horizontal,
     * vertical, and two diagonal axes.  Its appearance for small
     * values of <code>size</code> is much better than that given
     * by Java's algorithm.</p>
     * 
     * <p>On the other hand, for large values of <code>size</code>,
     * this algorithm is more computationally intensive than the
     * <code>JavaCircle</code> technique.</p>
     * 
     * <p>If <code>size &gt; 16384</code>, this algorithm will
     * revert to the <code>JavaCircle</code> algorithm.</p>
     */
    public static PlotMarkAlgorithm BetterCircle = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size > 16384)
                return JavaCircle.makeShape(x, y, size);
            
            int a = (int) Math.round(x);
            int b = (int) Math.round(y);
            
            return BetterCirclePath(a, b, size);
        }
    };
    
    
    /**
     * <p>The Java circle algorithm.</p>
     *
     * <p>Returns:</p>
     * 
     * <pre>    new XCircle(x, y, size)</pre>
     * 
     * <p>This is equivalent to the pure Java call:</p>
     * 
     * <pre>    new Ellipse2D.Double(x-size, y-size, 2*size, 2*size)</pre>
     * 
     * <p>Warning: For small values of <code>size</code>, the circle
     * constructed by Java does not have 8-fold symmetry about the
     * horizontal, vertical, and two diagonal axes.  The circle is
     * therefore not very pleasing aesthetically.</p>
     * 
     * <p>See <code>BetterCircle</code>.</p>
     */
    public static final PlotMarkAlgorithm JavaCircle = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            return new XCircle(x, y, size);
        }
    };
    
    
    /**
     * <p>The wedge-facing-north algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+size,y+(2*size))</code></li>
     *   <li><code>(x-size,y+(2*size))</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm WedgeN = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + s), (float) (y + d));
            path.lineTo((float) (x - s), (float) (y + d));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The wedge-facing-east algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-(2*size),y+size)</code></li>
     *   <li><code>(x-(2*size),y-size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm WedgeE = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - d), (float) (y + s));
            path.lineTo((float) (x - d), (float) (y - s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The wedge-facing-south algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-size,y-(2*size))</code></li>
     *   <li><code>(x+size,y-(2*size))</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm WedgeS = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - s), (float) (y - d));
            path.lineTo((float) (x + s), (float) (y - d));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The wedge-facing-west algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+(2*size),y-size)</code></li>
     *   <li><code>(x+(2*size),y+size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm WedgeW = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + d), (float) (y - s));
            path.lineTo((float) (x + d), (float) (y + s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The blunt-wedge-facing-north algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+size,y+size)</code></li>
     *   <li><code>(x-size,y+size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm BluntWedgeN = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + s), (float) (y + s));
            path.lineTo((float) (x - s), (float) (y + s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The blunt-wedge-facing-east algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-size,y+size)</code></li>
     *   <li><code>(x-size,y-size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm BluntWedgeE = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - s), (float) (y + s));
            path.lineTo((float) (x - s), (float) (y - s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The blunt-wedge-facing-south algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-size,y-size)</code></li>
     *   <li><code>(x+size,y-size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm BluntWedgeS = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - s), (float) (y - s));
            path.lineTo((float) (x + s), (float) (y - s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The blunt-wedge-facing-west algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+size,y-size)</code></li>
     *   <li><code>(x+size,y+size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm BluntWedgeW = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + s), (float) (y - s));
            path.lineTo((float) (x + s), (float) (y + s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The sharp-wedge-facing-north algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+size,y+(3*size))</code></li>
     *   <li><code>(x-size,y+(3*size))</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm SharpWedgeN = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 3 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + s), (float) (y + d));
            path.lineTo((float) (x - s), (float) (y + d));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The sharp-wedge-facing-east algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-(3*size),y+size)</code></li>
     *   <li><code>(x-(3*size),y-size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm SharpWedgeE = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 3 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - d), (float) (y + s));
            path.lineTo((float) (x - d), (float) (y - s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The sharp-wedge-facing-south algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-size,y-(3*size))</code></li>
     *   <li><code>(x+size,y-(3*size))</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm SharpWedgeS = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 3 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - s), (float) (y - d));
            path.lineTo((float) (x + s), (float) (y - d));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The sharp-wedge-facing-west algorithm.</p>
     *
     * <p>Produces the closed path with 3 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+(3*size),y-size)</code></li>
     *   <li><code>(x+(3*size),y+size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm SharpWedgeW = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 3 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + d), (float) (y - s));
            path.lineTo((float) (x + d), (float) (y + s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The thumb-facing-north algorithm.</p>
     *
     * <p>Produces the closed path with 5 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+size,y+size)</code></li>
     *   <li><code>(x+size,y+(2*size))</code></li>
     *   <li><code>(x-size,y+(2*size))</code></li>
     *   <li><code>(x-size,y+size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm ThumbN = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + s), (float) (y + s));
            path.lineTo((float) (x + s), (float) (y + d));
            path.lineTo((float) (x - s), (float) (y + d));
            path.lineTo((float) (x - s), (float) (y + s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The thumb-facing-east algorithm.</p>
     *
     * <p>Produces the closed path with 5 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-size,y+size)</code></li>
     *   <li><code>(x-(2*size),y+size)</code></li>
     *   <li><code>(x-(2*size),y-size)</code></li>
     *   <li><code>(x-size,y-size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm ThumbE = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - s), (float) (y + s));
            path.lineTo((float) (x - d), (float) (y + s));
            path.lineTo((float) (x - d), (float) (y - s));
            path.lineTo((float) (x - s), (float) (y - s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The thumb-facing-south algorithm.</p>
     *
     * <p>Produces the closed path with 5 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x-size,y-size)</code></li>
     *   <li><code>(x-size,y-(2*size))</code></li>
     *   <li><code>(x+size,y-(2*size))</code></li>
     *   <li><code>(x+size,y-size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm ThumbS = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x - s), (float) (y - s));
            path.lineTo((float) (x - s), (float) (y - d));
            path.lineTo((float) (x + s), (float) (y - d));
            path.lineTo((float) (x + s), (float) (y - s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>The thumb-facing-west algorithm.</p>
     *
     * <p>Produces the closed path with 5 vertices:</p>
     *
     * <ul>
     *   <li><code>(x,y)</code></li>
     *   <li><code>(x+size,y-size)</code></li>
     *   <li><code>(x+(2*size),y-size)</code></li>
     *   <li><code>(x+(2*size),y+size)</code></li>
     *   <li><code>(x+size,y+size)</code></li>
     * </ul>
     */
    public static final PlotMarkAlgorithm ThumbW = new PlotMarkAlgorithm() {
        public Shape makeShape(double x, double y, int size) {
            if (size < 0)
                size = 0;
            
            int s = size;
            int d = 2 * size;
            
            GeneralPath path = new GeneralPath();
            
            path.moveTo((float) x, (float) y);
            path.lineTo((float) (x + s), (float) (y - s));
            path.lineTo((float) (x + d), (float) (y - s));
            path.lineTo((float) (x + d), (float) (y + s));
            path.lineTo((float) (x + s), (float) (y + s));
            
            path.closePath();
            
            return path;
        }
    };
    
    
    /**
     * <p>Returns the data for construction of a quarter circle
     * path of the given radius.</p>
     * 
     * <p>Assume that the <code>int[][]</code> returned has the
     * name <code>data</code>.  Then <code>data</code> is
     * normally of size 2.  Let:</p>
     * 
     * <pre>    int[] xx = data[0];</pre>
     * <pre>    int[] yy = data[1];</pre>
     * 
     * <p>Then, the array <code>xx</code> contains the
     * x-coordinates of desired data points
     * and the array <code>yy</code> the corresponding
     * y-coordinates.  In particular, the arrays have
     * the same length, say, <code>k</code>.  The ends
     * of the arrays are as follows:</p>
     * 
     * <p><code>xx[0]</code> equals <code>radius</code>.</p>
     * 
     * <p><code>yy[0]</code> equals <code>0</code>.</p>
     * 
     * <p><code>xx[k-1]</code> equals <code>0</code>.</p>
     * 
     * <p><code>yy[k-1]</code> equals <code>radius</code>.</p>
     * 
     * <p>More generally, the data is constructed to include
     * the boundary points <code>(x,y)</code> whose distance
     * from the origin is less than or equal to
     * <code>radius+0.5</code>.  A horizontal or vertical run
     * of boundary points is represented by the two endpoints
     * of the run.</p>
     * 
     * <p>The path associated with the quarter circle data is
     * designed to be symmetrical relative to the main diagonal.</p>
     * 
     * <p>Note that:</p>
     * 
     * <ul>
     *   <li>If <code>radius &lt;= 0</code> returns
     *       the array <code>{ { 0 }, { 0 } }</code>
     *   </li>
     *   <li>If <code>radius &gt; 16384</code> returns
     *       <code>null</code>.
     *   </li>
     * </ul>
     * 
     * <p>The latter constraint is due to a desire to do all
     * internal computations in integer arithmetic.</p>
     * 
     * @param radius the radius of the desired quarter circle
     */
    public static int[][] QuarterCircleData(int radius) {
        if (radius <= 0) 
            return new int[][] { { 0 }, { 0 } };
        
        if (radius > 16384)
            return null;
        
        // largest integer < square of (radius + 0.5).
        int square = radius * radius + radius;
        
        // adequate length for internal arrays
        int length = 2 * (radius + 1);
        
        // create internal arrays
        
        int[] x = new int[length];
        int[] y = new int[length];
        
        // initialize first point
        
        int a = radius;
        int b = 0;
        
        x[0] = a;
        y[0] = b;
        
        int k = 1;
        
        // create first eighth of circle
        
        while (b < a) {
            int limit = square - a * a;
            
            int c = b;
            
            while (c * c <= limit)
                c++;
            
            c--;
            
            // vertical step
            
            if (a < c)
                c = a;
            
            if (b < c) {
                b = c;
                x[k] = a;
                y[k] = b;
                k++;
            }
            
            // diagonal step
            
            a--;
            b++;
            
            if (b <= a) {
                x[k] = a;
                y[k] = b;
                k++;
            }
        }
        
        // create second eighth of circle
        
        int s = k - 1;
        
        if (x[s] == y[s])
            s--;
        
        while (s >= 0) {
            x[k] = y[s];
            y[k] = x[s];
            k++;
            s--;
        }
        
        // create minimal arrays
        
        int[] xx = new int[k];
        int[] yy = new int[k];
        
        for (int i = 0; i < k; i++) {
            xx[i] = x[i];
            yy[i] = y[i];
        }
        
        return new int[][] { xx, yy };
    }
    
    
    /**
     * <p>Returns a full circle path with the given center
     * (x,y) and the given radius using the method
     * <code>QuarterCircleData</code> for the computation.
     * Since that method provides only a quarter of a
     * circle, the remaining path points are generated via
     * symmetry.  In particular, 8-fold symmetry about the
     * horizontal, vertical, and two diagonal axes is
     * therefore guaranteed.</p>
     * 
     * <p>If <code>radius &gt; 16384</code>, the method
     * <code>QuarterCircleData</code> cannot be used so
     * this method returns an <code>XCircle</code>-based
     * path instead.</p>
     * 
     * @param x the circle x-center
     * @param y the circle y-center
     * @param radius the circle radius
     */
    public static Shape BetterCirclePath(int x, int y, int radius) {
        GeneralPath path = new GeneralPath();
        
        if (radius <= 0) {
            path.moveTo(x, y);
            path.lineTo(x, y);
            path.closePath();
            return path;
        }
        
        int[][] data = QuarterCircleData(radius);
        
        if (data == null)
            return new XCircle(x, y, radius);
        
        int[] xx = data[0];
        int[] yy = data[1];
        
        int k = xx.length;
        
        path.moveTo(x + xx[0], y + yy[0]);
        
        for (int i = 1; i < k; i++)
            path.lineTo(x + xx[i], y + yy[i]);
        
        for (int i = (k - 2); i >= 0; i--)
            path.lineTo(x - xx[i], y + yy[i]);
        
        for (int i = 1; i < k; i++)
            path.lineTo(x - xx[i], y - yy[i]);
        
        for (int i = (k - 2); i >= 0; i--)
            path.lineTo(x + xx[i], y - yy[i]);
        
        path.closePath();
        
        return path;
    }
    
}
